Laden...

Selbstdefinierte Properties in typisierter DataRow können nicht gebunden werden

Erstellt von mec vor 9 Jahren Letzter Beitrag vor 9 Jahren 3.157 Views
M
mec Themenstarter:in
19 Beiträge seit 2014
vor 9 Jahren
Selbstdefinierte Properties in typisierter DataRow können nicht gebunden werden

verwendetes Datenbanksystem: SQL-Server, Access

Hallo,

ich habe ein typisiertes DataSet mit einer entsprechend typisierten DataTable (MYDataTable) und DataRow (MYRow) von Designer generieren lassen.

Die MYRow habe ich nun in einer eigenen Datei als partielle Klasse um public Properties erweitert. Wenn ich MYDataTable nun an die DataSource einer Forms.BindingSource übergebe, kann ich diese neuen Properties nicht binden. Weder im Designer noch zur Laufzeit.

Warum ist das so?

Kann es sein, dass nur die Columns-Eigenschaft des Table-Objekts gebunden werden kann?

Gibt es ein Attribut, welches mir diese neuen Properties zur Bindung bereit stellt ("[Bindable(true)]" tut es leider nicht)?

Vermutlich trifft meine erste Vermutung zu. Aber kann man das ändern?

Vielen Dank vorab.

4.938 Beiträge seit 2008
vor 9 Jahren

Ist denn die designer-generierte Klasse auch als 'partial' gekennzeichnet?
Wenn ja, dann mußt du schauen, ob auch der Namespace mit der von deiner eigenen 'partial'-Klasse übereinstimmt.
Am besten einfach den "Object Browser" öffnen und schauen, ob dort die Klasse mit allen Eigenschaften erscheint.

M
mec Themenstarter:in
19 Beiträge seit 2014
vor 9 Jahren

Ja, das ist alles richtig. In meiner partiellen Klasse habe ich auch Zugriff auf die vom Designer generierten Eigenschaften und Methoden.

Der Object-Browser zeigt mir allerdings weder meine noch die vom Designer generierten Eigenschaften an. Aber das ist eigentlich normal. Der zeigt nur Typdefinitionen an aber keine Properties.

Nee, so einfach isses leider nicht ...

4.938 Beiträge seit 2008
vor 9 Jahren

Wenn du auf einen Typ im linken Fenster des Object Browsers klickst, sollten im rechten Fenster alle Eigenschaften und Methoden angezeigt werden.
Aber da du Zugriff per Code hast, sollte es eigentlich keine Einschränkungen beim Binding geben.
Zeig doch mal den Ausschnitt von einer Eigenschaft aus der designer-generierten Klasse und deinem Code.

M
mec Themenstarter:in
19 Beiträge seit 2014
vor 9 Jahren

Hier ist meine partielle Klasse:


using System;

namespace Test 
{
    public interface IMy
    {
        Guid Id { get; }

        string Name { get; set; }

        byte[] Logo { get; set; }
    }

    
    public partial class MyDataSet 
    {
        partial class MYRow : IMy
        {
            public Guid Id
            {
                get { return this.CON_ID; }
            }
            

            public string Name
            {
                get
                {
                    try { return this.CON_NAME; }
                    catch { return null; }
                }

                set
                {
                    if (string.IsNullOrWhiteSpace(value))
                        this.SetCON_NAMENull();
                    else
                        this.CON_NAME = value.Trim();
                }
            }


            public byte[] Logo
            {
                get
                {
                    try { return this.CON_LOGO; }
                    catch { return null; }
                }

                set
                {
                    if (value == null || value.Length == 0)
                        this.SetCON_LOGONull();
                    else
                        this.CON_LOGO = value;
                }

            }
        }

    }
}


Und das ist der Designer-Code von der Row:


       
        /// <summary>
        ///Represents strongly named DataRow class.
        ///</summary>
        public partial class MYRow : global::System.Data.DataRow {
            
            private MYDataTable tableMY;
            
            [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
            [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Data.Design.TypedDataSetGenerator", "4.0.0.0")]
            internal MYRow(global::System.Data.DataRowBuilder rb) : 
                    base(rb) {
                this.tableMY = ((MYDataTable)(this.Table));
            }
            
            [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
            [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Data.Design.TypedDataSetGenerator", "4.0.0.0")]
            public System.Guid CON_ID {
                get {
                    return ((global::System.Guid)(this[this.tableMY.CON_IDColumn]));
                }
                set {
                    this[this.tableMY.CON_IDColumn] = value;
                }
            }
            
            [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
            [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Data.Design.TypedDataSetGenerator", "4.0.0.0")]
            public string CON_NAME {
                get {
                    try {
                        return ((string)(this[this.tableMY.CON_NAMEColumn]));
                    }
                    catch (global::System.InvalidCastException e) {
                        throw new global::System.Data.StrongTypingException("Der Wert für Spalte CON_NAME in Tabelle MY ist DBNull.", e);
                    }
                }
                set {
                    this[this.tableMY.CON_NAMEColumn] = value;
                }
            }
            
            [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
            [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Data.Design.TypedDataSetGenerator", "4.0.0.0")]
            public byte[] CON_LOGO {
                get {
                    try {
                        return ((byte[])(this[this.tableMY.CON_LOGOColumn]));
                    }
                    catch (global::System.InvalidCastException e) {
                        throw new global::System.Data.StrongTypingException("Der Wert für Spalte CON_LOGO in Tabelle MY ist DBNull.", e);
                    }
                }
                set {
                    this[this.tableMY.CON_LOGOColumn] = value;
                }
            }
            
            [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
            [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Data.Design.TypedDataSetGenerator", "4.0.0.0")]
            public bool IsCON_NAMENull() {
                return this.IsNull(this.tableMY.CON_NAMEColumn);
            }
            
            [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
            [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Data.Design.TypedDataSetGenerator", "4.0.0.0")]
            public void SetCON_NAMENull() {
                this[this.tableMY.CON_NAMEColumn] = global::System.Convert.DBNull;
            }
            
            [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
            [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Data.Design.TypedDataSetGenerator", "4.0.0.0")]
            public bool IsCON_LOGONull() {
                return this.IsNull(this.tableMY.CON_LOGOColumn);
            }
            
            [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
            [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Data.Design.TypedDataSetGenerator", "4.0.0.0")]
            public void SetCON_LOGONull() {
                this[this.tableMY.CON_LOGOColumn] = global::System.Convert.DBNull;
            }
        }


Da ist eigentlich alles Standard. Und das Problem liegt sicher auch nicht an dieser Stelle. Vielmehr kann die BindingSource nicht auf die neuen Properties zugreifen. Vermutlich greift diese einfach nur auf Table.Columns zu ..

4.938 Beiträge seit 2008
vor 9 Jahren

Ja, das kann gut sein.
Irgendwo muß ja auch in der designer-generierten Datei die Zuweisung der Table.Columns stehen.
Kannst du nicht einfach per Code deine eigenen Eigenschaften (zur Laufzeit) hinzufügen?

M
mec Themenstarter:in
19 Beiträge seit 2014
vor 9 Jahren

Leider habe ich nichts gefunden, was die Eigenschaft Columns zur präferierten Binding-Eigenschaft macht - oder eben nicht, sondern eine andere nämlich die Struktur der DataRow.

Ich vermute eher, das geschieht schon vorher irgendwo in der Vererbungshierarchie zwischen DataTable und der TypedTableBase<T>-Klasse.

Und genau da beim Enumerator liegt vermutlich des Rätsels Lösung:


namespace System.Data
{
    // Zusammenfassung:
    //     Dieser Typ wird als Basisklasse für die typisierte System.Data.DataTable-Objektgenerierung
    //     durch Visual Studio und das .NET Framework-Tool XSD.exe verwendet, und er
    //     ist nicht für die direkte Verwendung im Code vorgesehen.
    //
    // Typparameter:
    //   T:
    //     Der Typ der Objekte in der von der Tabelle dargestellten Quellsequenz, meist
    //     System.Data.DataRow.
    [Serializable]
    public abstract class TypedTableBase<T> : DataTable, IEnumerable<T>, IEnumerable where T : System.Data.DataRow
    {
        // Zusammenfassung:
        //     Initialisiert eine neue System.Data.TypedTableBase'1.Diese Methode unterstützt
        //     die typisierte System.Data.DataTable-Objektgenerierung durch Visual Studio
        //     und das .NET Framework-Tool XSD.exe.Dieser Typ ist nicht für die direkte
        //     Verwendung im Code vorgesehen.
        [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
        protected TypedTableBase();
        //
        // Zusammenfassung:
        //     Initialisiert eine neue System.Data.TypedTableBase'1.Diese Methode unterstützt
        //     die typisierte System.Data.DataTable-Objektgenerierung durch Visual Studio
        //     und das .NET Framework-Tool XSD.exe.Diese Methode ist nicht für die direkte
        //     Verwendung im Code vorgesehen.
        //
        // Parameter:
        //   info:
        //     Eine System.Runtime.Serialization.SerializationInfo, die Daten zum Erstellen
        //     des Objekts enthält.
        //
        //   context:
        //     Der Streamingkontext für das deserialisierte Objekt.
        [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
        protected TypedTableBase(SerializationInfo info, StreamingContext context);

        // Zusammenfassung:
        //     Konvertiert die Elemente einer System.Data.TypedTableBase'1 in den angegebenen
        //     Typ.Diese Methode unterstützt die typisierte System.Data.DataTable-Objektgenerierung
        //     durch Visual Studio und das .NET Framework-Tool XSD.exe.Diese Methode ist
        //     nicht für die direkte Verwendung im Code vorgesehen.
        //
        // Typparameter:
        //   TResult:
        //
        // Rückgabewerte:
        //     Eine System.Data.EnumerableRowCollection, die jedes Element der Quellsequenz
        //     enthält, das in den angegebenen Typ konvertiert wird.
        public EnumerableRowCollection<TResult> Cast<TResult>();
        //
        // Zusammenfassung:
        //     Gibt einen Enumerator für die typisierte System.Data.DataRow zurück.Diese
        //     Methode unterstützt die typisierte System.Data.DataTable-Objektgenerierung
        //     durch Visual Studio und das .NET Framework-Tool XSD.exe.Diese Methode ist
        //     nicht für die direkte Verwendung im Code vorgesehen.
        //
        // Rückgabewerte:
        //     Ein Objekt, das die System.Collections.Generic.IEnumerator<T>-Schnittstelle
        //     implementiert.
        public IEnumerator<T> GetEnumerator();
    }
}

Ich hatte gehofft, dass jemand weiß, wie und wo man da ansetzen kann, aber das scheint nicht gerade trivial zu sein.

Mein Projekt hat sehr komplexe in sich verschachtelte Objekt-Strukturen, die sich gegenseitig referenzieren. Und damit eine Änderung in einem der Objekte sich auch tatsächlich überall durchzieht, möchte ich nicht mit Kopien dieser Objekt arbeiten. Daher war meine Idee, die Rows der einzelnen Tabellen, die in Dritter Normalform vorliegen, direkt über Interfaces verbinden. Das DataSet soll eigentlich nur einen Daten-Container darstellen und eine bequeme Verbindung zu austauschbaren Datenbankplattformen herstellen (Multi Layer Modell).

Das wäre halt eine elegante Lösung gewesen, wenn die BindingSource da mitspielen würde.

Dann muss ich halt doch die Daten aus der Datenbank lesen, in die Objektstruktur kopieren und anschließend wieder wegschreiben.
Dann kann ich leider nicht die DataTable als zu bindendes List-Objekt für die BindingSource verwenden, sondern muss die Rows in einer eigenen Liste verwalten, um den erforderlichen und auch so praktischen RowState (inserted, deleted, modified etc) für das DB-Update verwenden zu können.

Aber es wäre halt so viel einfacher, wenn ich die an die BindingSource zu bindende Eigenschaft der zugrundeliegenden DataTable einfach redefinieren oder überschreiben könnte ... da war wohl der Wunsch Vater des Gedanken ...