Entwickler-Ecke
Datenbanken (inkl. ADO.NET) - ADO.NET Entity Datamodel und MySQL
kingdave2nd - Fr 17.07.09 10:32
Titel: ADO.NET Entity Datamodel und MySQL
Hallo zusammen,
ich befürchte ich bewege mich mal wieder auf totalem Neuland, aber ich probiers trotzdem mal:
Ich habe EntityModel meiner MySQL Datenbank mithilfe des MySQL .NET Providers über den Visual Studio Wizard erstellt. Dann habe ich mir eine kleine Klasse geschrieben die mir die Entity Connection zusammenbaut. Da ich denke, das das für den einen oder anderen ganz hilfreich sien könnte, hier mal der Code:
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:
| public class MdbConnectionMySql : MdbConnection {
public MdbConnectionMySql(string ServerName, string DatabaseName, string UserName, string Password) : base(ServerName, DatabaseName, UserName, Password) { providerName = "MySql.Data.MySqlClient"; entityConn = new EntityConnection(this.GetEntityConnectionString()); }
public override string GetEntityConnectionString() { MySqlConnectionStringBuilder strBuilder = new MySqlConnectionStringBuilder(); strBuilder.Database = DatabaseName; strBuilder.Server = ServerName; strBuilder.UserID = UserName; strBuilder.Password = Password;
EntityConnectionStringBuilder entityBuilder = new EntityConnectionStringBuilder(); entityBuilder.Provider = providerName; entityBuilder.ProviderConnectionString = strBuilder.ToString(); entityBuilder.Metadata = @"res://*/MdbModelSystemApplication.csdl|res://*/MdbModelSystemApplication.ssdl|res://*/MdbModelSystemApplication.msl";
return entityBuilder.ToString(); } } |
Gut, das funktioniert aber alles einwandfrei, ich kann wie gewünscht Daten aus der DB lesen. Nun habe ich eine Eintität "MdbUser" die auf eine View in der Datenbank basiert. Das spannende dabei ist, das mir alle Attribute der Entität, die "NOT NULL" gekennzeichnet sind, als EntityKey im EntityModel angelegt werden (siehe untitled.GIF im Anhang).
Im gezeigten Screenshot ist dies das verschlüsselte Password des users das in der Datenbank gespeichert ist. Nun mal ganz abgesehen davon, dass das Attribut "userId" das einzige sein sollte, was meinem Verständnis nach als entityKey fungieren sollte, ergibt auch folgende Problematik:
Ich möchte in meiner DB Schicht direkt nach dem Laden des MDBUsers die Atribute "cryptPassword" und "passwordSalt" mit 'null' überschreiben, damit diese gar nicht erst an die Clientanwendung weitergegeben werden.
Ich erhalte dann als Fehlermeldung:
Quelltext
1:
| System.InvalidOperationException: The property 'cryptPassword' is part of the object's key information and cannot be modified. |
Setze ich den EntityKey für Crypt Password auf 'False' erhalte ich schon direkt im VS beim Build die folgende Meldung:
Quelltext
1: 2:
| Error 1 Error 3003: Problem in Mapping Fragment starting at line 252: All the key properties (MdbUserSet.authorizedUserId, MdbUserSet.created, MdbUserSet.createdBy, MdbUserSet.eMail, MdbUserSet.fullName, MdbUserSet.isDeleted, MdbUserSet.isSystem, MdbUserSet.loginName, MdbUserSet.modified, MdbUserSet.modifiedBy, MdbUserSet.passwordSalt, MdbUserSet.userId) of the EntitySet MdbUserSet must be mapped to all the key properties (v_system_user_select.authorizedUserId, v_system_user_select.created, v_system_user_select.createdBy, v_system_user_select.cryptPassword, v_system_user_select.eMail, v_system_user_select.fullName, v_system_user_select.isDeleted, v_system_user_select.isSystem, v_system_user_select.loginName, v_system_user_select.modified, v_system_user_select.modifiedBy, v_system_user_select.passwordSalt, v_system_user_select.userId) of table v_system_user_select. C:\usbstick\daveworldSVN\tooma\2009-07-15\Provider.Mdb\MdbModelSystemApplication.edmx 253 15 Provider.Mdb |
Hier auch der Code mit dem ich auf die DB zugreife:
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| var usrQuery = from u in entities.MdbUserSet where u.loginName.Contains(loginname) && u.isSystem == 0 && u.isDeleted == 0 && u.isApproved == 1 select new { u }; List<MdbUser> userList = new List<MdbUser>();
foreach (var q in usrQuery) { q.u.cryptPassword = null; q.u.passwordSalt = null; userList.Add(q.u); } return userList.ToArray(); |
So, jetzt hoffe ich habe alle wesentlichen Informationen gepostet. Hoffe es hat sich neben mir schon mal jemand mit dem Thema beschäfitgt und kann mir beantworten, warum der EntityDesigner überhaupt auf die Idee kommt, die ganzen NOT NULL Felder als EntityKey Felder zu markieren.
Viele Grüsse
Dave
kingdave2nd - Fr 17.07.09 14:50
Hi zusammen,
ein kleiner Nachtrag:
Ich habe noch mal weitergesucht und im Eintrag
http://www.devart.com/forums/viewtopic.php?p=46838
festgestellt, das es wohl mit dem Oracle .NET Provider ein ähnliches Thema gibt. Eignetlich beruhigend dass es noch andere Leidensgenossen gibt, andererseit hört sich das nach einem echten MS Bug an :(
gruss Dave
Kha - Fr 17.07.09 23:04
In dem Fall hatte die Tabelle ja keinen Primary Key, trifft das bei dir auch zu?
PS: Ein select new { u } ist relativ sinnlos, da genügt auch select u
kingdave2nd - Mo 20.07.09 08:56
Hi,
nein, bei mir trifft das nicht zu, es gibt einen Primary Key. Aber ganz egal, ob es einen gibt oder, es ist reproduzierbar, dass er alle "NOT NULL" Spalten der Tabelle auf IdentityKey = true setzt.
Ich habe am Wochenende noch ein bisschen weitergeforscht und folgendes dabei rausgefunden:
Ich habe als erstes meine MySQL DB komplett auf MSSQL (Version 2005) ugmeschrieben. Das erstaunliche Ergebniss: Auch in einem MS SQL Server Umfeld tritt das Phänomen auf. Nun habe ich erst dann daran gedacht, das ich alle Abfragen komplett auf der Basis von Views mache. Also habe ich im Designer dann doch mal endlich eine Tabelle anstelle einer View angegeben und siehe da, er setzt tatächlich nur noch auf dem richtigen Feld den IdentityKey auf true. Anbei auch ein Screenshot, links die Tabelle, rechts die View.
In meinem Fall eine richtig doofe Geschichte, da über die Views diverse Rollen und Berechtigungen für den Zugriff auf Objekte geprüft werden. Wenn es dafür jetzt keine Lösung gibt, heist das ein Redesign meiner kompletten Anwendung auf Basis eines ERM der keine Views unterstützt, oder Aufbau einer eigenen Datenbankobjektstruktur (STEINZEIT) oder Umstieg auf NHybernate. Alles keine schöne Sache, weil ewigst Aufwand. Somit verbleibe ich in der Hoffnung, das hier irgendwer eine Lösung für mein kleines Problem hat...
Viele Grüsse
Dave
Kha - Mo 20.07.09 10:54
kingdave2nd hat folgendes geschrieben : |
Nun habe ich erst dann daran gedacht, das ich alle Abfragen komplett auf der Basis von Views mache. |
Irgendwie aber logisch, dass die Entity dann read-only wird, oder? Schließlich kann das EF dann unmöglich die Update/Insert-Queries generieren - das wird auch bei NHibernate nicht anders sein.
Müssen denn Entities geupdated werden? Wenn ja, ist das einfach eine sehr OR/M-unfreundliche DB-Struktur, Updates wirst du dann wohl manuell über SPs erledigen müssen.
Jetzt aber zum Verstecken der zwei Properties:
Die Entity soll dann über WCF zum Client geschickt werden, nehme ich an? Dann wirst du von vielen hören, dass dort eine EF-Klasse sowieso nichts zu suchen habe. Stattdessen solltest du ein
DTO [
http://en.wikipedia.org/wiki/Data_Transfer_Object] benutzen, das aus nichts mehr als den benötigten Properties besteht, in dem du also insbesondere die zwei kritischen Werte weglassen kannst.
Wenn dir das zuviel Schreibaufwand ist ;) und du keine Probleme mit DAL-Klassen im Client hast, kannst du vielleicht auch einfach zwei verschiedene Entity-Klassen erstellen: Einmal mit, einmal ohne die zwei Werte. Möglicherweise lässt sich dann sogar die eine von der anderen ableiten, aber dafür kenne ich mich zu wenig mit dem EF aus.
PS: Sollte es statt einem cryptPassword nicht lieber ein hashedPassword sein ;) ?
kingdave2nd - Mo 20.07.09 14:27
Hi,
naja, eigentlich hatte ich vor, tatächlich die Updates/Deletes/Inserts über Stored Procedures zu erledigen. Und diese kann ich ja für die jeweilige Einfügeoperation im EntityModel hinterlegen.
Die Datenbank verfolgt tatächlich einen Objektrelationen Ansatz. Daten werden grundsätzlich in einer Objektentität abgelegt und über property Entitäten erweitert. Über Assoziationstabellen werden die Objekte miteinander assoziiert. Zugegebenermassen, vielleicht nicht der performanteste Ansatz, aber diese Datenbank soll zusammen mit einem Webservice bei verschiedenen Kundenprojekten zum Einsatz kommen, wo relativ wenig Daten verspeichert werden. Quasi eine Mini CMDB für arme ;-)))
Das mit den DTOs würde natürlich meine Verständnis-Lücke schliessen. Verstehe ich das richtig: Ein DTO ist eigentlich nichts weiter als ein Objekt ohne Methoden, (nur mit Eigenschaften) das in meinen DB Layer gefüllt und dann übertragen wird?
Und möchte ich ein Update des DAO durchführen, fülle ich auf Clientseite das DTO und im DB Layer muss ich irgendwie rausbekommen was hat sich im DTO geändert ?!?
Gruss Dave
PS: Ist auch ein Hash, der Name ist eher historisch gewachsen und jetzt mag ich Ihn nicht mehr ändern ;-)
kingdave2nd - Mo 20.07.09 15:48
Yes, das mit der OR/M unfreundlichkeit nehme ich aber mal bewusst in Kauf.
Soweit habe ich das dann aber jetzt kapiert und die Geschichte mit den DTOs sollte mir auch weiterhelfen.
Zudem merke ich mal wieder, es fehlt mir an ziemlich viel Design Erfahrung. Werde mich wohl noch ein bisschen mit Code Design Theorie beschäftigen müssen.
Vielen Dank für die Hilfe
Gruss Dave
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!