Autor Beitrag
maxx
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 70



BeitragVerfasst: Di 20.04.10 10:04 
hallo,

ich habe einen OR-Mapper entworfen. Mich würde interessieren, ob der OK ist. Mir kommt der nämlich unglaublich kompliziert vor. Ich bin sicher, den kann man noch sehr vereinfachen. Ich weiß aber nicht, wie.?.?

Für jede Datenbank-Tabelle gibt es 2 Klassen. Gibt es in der Datenbank eine Tabelle A, gibt es im Programm die Klassen A und DbA. In A stehen die allgemeinen Eigenschaften, in DbA kommen noch Datenbank-spezifische Eigenschaften hinzu (z. B. eine Datenbank-ID).

Bei meinem Testbeispiel nehme ich an, dass ich in der Datenbank 2 Tabellen habe: A und B. A hat einerseits eine 1:n-Beziehung zu B (in C# als BindingList dargestellt), andererseits eine 1:1-Beziehung zu B. Ist zwar ziemlich unsinnig, aber das dient nur dazu, das Programm zu testen.

ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
using System;
using System.ComponentModel;
using System.Data;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
   public partial class Form1 : Form
   {
      public Form1()
      {
         InitializeComponent();

         // Hier im Hauptprogramm gibt es nur 3 Testfälle.

         A a = new A();
         a.s = "a1";

         // Test 1 (B-Collection in A):

         /*B b0 = new B();
         b0.s = "b0";
         B b1 = new B();
         b1.s = "b1";
         BindingList<B> bB = new BindingList<B>();
         bB.Add(b0);
         bB.Add(b1);
         a.cB = bB;

         B b = new B();
         b.s = "b";
         a.b = b;*/


         // Test 2 (DbB-Collection in A):

         /*DbB dbB0 = new DbB();
         dbB0.s = "b0";
         DbB dbB1 = new DbB();
         dbB1.s = "b1";
         BindingList<DbB> cDbB = new BindingList<DbB>();
         cDbB.Add(dbB0);
         cDbB.Add(dbB1);
         a.cB = cDbB;

         DbB dbB = new DbB();
         dbB.s = "b";
         a.b = dbB;*/


         // Anzeigen für Test 1 und 2:

         /*foreach (B _b in a.cB)
            MessageBox.Show(_b.s);
         MessageBox.Show(a.b.s);

         DbA _dbA = new DbA(a);
         foreach (DbB _dbB in _dbA.cB)
            MessageBox.Show(_dbB.s);
         MessageBox.Show(_dbA.b.s);

         A _a = (A)dbA;
         foreach (B _b in _a.cB)
            MessageBox.Show(_b.s);
         MessageBox.Show(_a.b.s);*/


         // Test 3 (B-Collection in DbA):

         DbA dbA = new DbA();
         dbA.s = "a1";

         B b0 = new B();
         b0.s = "b0";
         B b1 = new B();
         b1.s = "b1";
         BindingList<B> cB = new BindingList<B>();
         cB.Add(b0);
         cB.Add(b1);
         dbA.cB = cB;

         B b = new B();
         b.s = "b";
         dbA.b = b;

         // Anzeigen für Test 3:

         foreach (B _b in dbA.cB)
            MessageBox.Show(_b.s);
         MessageBox.Show(dbA.b.s);

         A _a = (A)dbA; // von DbA nach A casten
         foreach (B _b in _a.cB)
            MessageBox.Show(_b.s);
         MessageBox.Show(_a.b.s);

         DbA _dbA = new DbA(_a); // von A nach DbA casten
         foreach (DbB _dbB in _dbA.cB)
            MessageBox.Show(_dbB.s);
         MessageBox.Show(_dbA.b.s);
      }
   }
   // ==============================================================================
   // Für Datenbank-Tabelle A:
   public interface IA
   {
      string s { get; set; } // String
      IBindingList cB { get; set; } // Collection (in Datenbank 1:n-Beziehung)
      IB b { get; set; } // Objekt (in Datenbank 1:1-Beziehung)
   }
   public class A : IA
   {
      public string s { get; set; }
      public IBindingList cB { get; set; }
      public IB b { get; set; }
      public A()
      {
         this.cB = new BindingList<B>();
      }
   }
   public class DbA : A // die Datenbank-Klasse für A
   {
      public int i { get; set; } // entspricht einer Datenbank-ID
      public DbA()
      {
      }
      public DbA(A a)
         : this()
      {
         this.s = a.s;
         foreach (IB b in a.cB)
            this.cB.Add(new DbB(b));
         this.b = a.b;
      }
      // Hier noch Methoden, um Daten in Datenbank zu schreiben oder aus ihr zu lesen.
   }
   // ==============================================================================
   // Für Datenbank-Tabelle B:
   public interface IB
   {
      string s { get; set; }
   }
   public class B : IB
   {
      public string s { get; set; }
      public B()
      {
      }
   }
   public class DbB : B // die Datenbank-Klasse für B
   {
      public int i { get; set; } // entspricht einer Datenbank-ID
      public DbB()
      {
      }
      public DbB(IB b):this()
      {
         this.s=b.s;
      }
      // Hier noch Methoden, um Daten in Datenbank zu schreiben oder aus ihr zu lesen.
   }
}


Was mich an dem Programm irgendwie ganz besonders stört ist, dass ich viel mit Interfaces arbeite. Dadurch geht mir die Typsicherheit verloren.


Zuletzt bearbeitet von maxx am Di 20.04.10 10:25, insgesamt 1-mal bearbeitet
Kha
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 3803
Erhaltene Danke: 176

Arch Linux
Python, C, C++ (vim)
BeitragVerfasst: Di 20.04.10 10:21 
user profile iconmaxx hat folgendes geschrieben Zum zitierten Posting springen:
ich habe einen OR-Mapper entworfen.
Wohl eher einen Data Access Layer für deine zwei Tabellen ;) . Für einen O/RM müsstest du das auf beliebige Entities und Tabellen verallgemeinern.

user profile iconmaxx hat folgendes geschrieben Zum zitierten Posting springen:
Was mich an dem Programm irgendwie ganz besonders stört ist, dass ich viel mit Interfaces arbeite. Dadurch geht mir die Typsicherheit verloren.
Warum trennst du dann überhaupt jede Entity in Interface-Klasse-Klasse :nixweiss: ? Hört sich nach Overengineering an, mach jeweils eine Klasse daraus.

_________________
>λ=
maxx Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 70



BeitragVerfasst: Di 20.04.10 10:36 
Zitat:
Warum trennst du dann überhaupt jede Entity in Interface-Klasse-Klasse. ? Hört sich nach Overengineering an, mach jeweils eine Klasse daraus.

Genau das will ich nicht. Es soll eine allgemeine Klasse geben, die universell einsetzbar ist. Diese Klasse soll man dann in unterschiedlichste Formen casten können. Z. B. in eine entsprechende Datenbank-Klasse.

Hier gehts mir auch um eine saubere Trennung. Zuerst überlegt man sich mal, welche Klassen es in einem Programm gibt (in meinem Beispiel die Klassen A und B). Danach kümmert man sich um die Datenbankanbindung und entwirft noch die zugehörigen Klassen DbA und DbB.

Man könnte die Klassen A und B dann auch in beliebige andere Formen casten, sofern man das dann irgendwann mal brauchen sollte. Vielleicht will ich irgendwann mal mit den Klassen A und B ein UFO steuern ;). Dann kann ich mir hierzu noch die Klassen UfoA und UfoB erzeugen und dann - wenn ich das will - z. B. von A nach UfoA casten.
Kha
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 3803
Erhaltene Danke: 176

Arch Linux
Python, C, C++ (vim)
BeitragVerfasst: Di 20.04.10 15:53 
Sag ich doch, eindeutig Overengineering :mrgreen: .
Im Ernst, mir ist so etwas noch nicht begegnet und ich sehe bisher auch noch keinen praktischen Nutzen darin.

_________________
>λ=
maxx Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 70



BeitragVerfasst: Di 20.04.10 20:35 
Das ist kein overengineering. Ich kenne einen OR-Mapper, der das auch so trennt. Nämlich "Propel".
Kha
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 3803
Erhaltene Danke: 176

Arch Linux
Python, C, C++ (vim)
BeitragVerfasst: Di 20.04.10 22:04 
Meinst du die om/FooTableMap-Klassen? Das ist doch nur die zwischengespeicherte XML-Datei und leitet vor allem nicht von FooBase ab.
Ich kenne eigentlich nur die folgenden zwei Ansätze:
  • Der User schreibt seine Entity-Klassen als POCOs selbst (alles schön virtual markieren), gefüllt wird dann mithilfe einer externen Config - (N)Hibernate
  • Die Entity-Klasse wird aus der Config generiert, der User kann durch Ableiten bzw. im .NET-Fall eher Partial Classes/Methods Logik hinzufügen (Linq2SQL, EF, Propel, ...)

_________________
>λ=