C# EFCore: Wie verwirkliche ich stark genestete Entities?

Kokujou

Lieutenant
Registriert
Dez. 2017
Beiträge
929
Hallo ihr Lieben... ich arbeite gerade an einem Projekt dass ich möglichst anonym behandeln muss, ich muss also konkrete Details verschweigen^^

Dort gibt es eine Datenbank, wo die Leute wohl dachten je härter wir die Tabellen zerteilen desto besser... Tja. Aber jetzt versuch das mal auf EFCore zu mappen. Und zwar habe ich nun folgenden Fall:

Es gibt einen Nutzer.
Der Nutzer hat eine GUID.
Dann gibt es Rollenzuweisungen mit User-GUID x Role-GUID
Dazu brauche ich dann in der Role-Tabelle die RoleId
Mit dieser Role-ID kann ich an die Role-Rights-Mapping Tabelle gehen und bekomme so die Right-Id
Mit dieser Right-ID gehe ich an die Right-Tabelle und bekomme ich den Right-Namen.

Und das ganze muss ich irgendwie in eine Liste von Strings mit Rechten konvertieren. Und wenn möglich gleich noch distincten.

Ich hab gerade stundenlang rumprobiert wie ich die ganzen Fremdschlüssel irgendwie auflösen kann aber ich kam immer nur bis Schritt 2, wo ich die Rollenzuweisungen habe. Daraus die Rollen zu kriegen ist offenbar schon zu viel.

kann mir jemand helfen?

Bonus-Frage: Die Datenbank ist alt, aber das Framework dass wir draufspielen können wir uns aussuchen. Ich hab schon gesucht, aber EFCore scheint so ziemlich das beste und einzige zu sein für ne SQL Server DB. Vielleicht bin ich aber auch nur mal wieder zu blöd zum suchen.
 
wenn EF richtig Implimentiert ist, also mit allen Navigation Propierties, solltest du wie folget vorgehen können
Rights.where(r=>r.RoleRights.Any(rr=>rr.Role.UserRights.Any(u=>u.USerID==UserID))).Selcet(r=>r.Name).Tolist()
 
und genau das ist der Punkt. Wie implementiere ich es denn richtig? denn das zeug aus ner SQL query zu kriegen wenn schon alles da ist - kein Problem.

aber wie implementiere ich diese übergang? BIs jetzt scheint es mir als könnte EFCore nicht mit Foreign Keys von Foreign Keys umgehen...

achja und ich will das auch möglichst nicht per-request machen müssen, es wäre also schön wenn man den modelBuilder entsprechend konfigurieren könnte. ansonsten könnte man natürlich auch für jede der erwähnten entities ein DBSet anlegen und es live querien.
 
Du kannst dir mittels Scaffold-DbContext aus einer bestehenden Datenbank deinen Databasecontext generieren lassen. Wie gut das funktioniert, musst du ausprobieren.
Alternativ kannst du natürlich deine Datenbankmodels manuell konfigurieren.
 
  • Gefällt mir
Reaktionen: Kokujou
die Kombination von HasOne/HasMand und WithOne/WithMany kenn ich natürlich schon.

Das Problem ist ist dass das ganze wie ihr ja seht so massiv genested ist, dass ich nicht weiß ob das überhaupt funktioniert.
Ich komm nicht über den 2. Schritt hinaus, weil er irgendwie die Foreign Keys verwechselt.

Das User-Mode hat eine Guid, damit gehst du zum User-Role-Mapping
Das user-Role-Mapping hat eine Role-Guid, damit müsste man zur Role-Tabelle gehen und sich die Rolle laden können.

Aber aus irgendwelchen unerfindlichen Gründen nimmt er nicht die RoleId sondern die UserId dafür... oder vielleicht die UserAssignment Id. das ist zumindest alles mit dem Typ Guid mit dem er es verwechseln kann, denn da Resultat ist - die Role ist null.
Ich hab natürlich in der DB schon geguckt und das kann natürlich nicht sein.

Ich glaube er kommt irgendwie mit den Foreign Keys durcheinander.
Ein solches statement besteht ja sowieso aus zwei schlüsseln. Und hier wirds komisch denn woher bekommt er überhaupt den zweiten?

Wir haben eine konfiguration a la:

UserEntity.HasOne(UserRoleMapping).WithOne(UserEntity).HasForeignKey(UserRoleMapping.MappingId)

(ich weiß jetzt aus dem Kopf gar nicht ob in den Foreign Key das zuerst oder das zu zweit geenannte modell gehört, aber egal)
Woher bekommt er nun also den Schlüssel des Zweiten Models? In meinem Fall sagt er er soll ein Join auf der MappingId machen. Nimmt er dann implizit den Primary Key?

Denn wenn das so ist würden alle Has/With Constraiints auf dem Mapping folglich auch statt dem Schlüssel den man eigentlich will, der RoleGuid, die MappingId zum Joinen nehmen.

Das verlinkte tool probiere ich gerne aus, danke dafür :)
 
@Nafi Scaffold-DbContext hat bis jetzt bei mir allen Projekten gut Funktioniert, bis auf Trigger.
@Kokujou ich habe bis jetzt immer das EF die Queries generieren und auch ausführen lassen. Und auch wenn du nicht alles Zeigen kannst, zumindest die Tabellen und die Configuration die hier wichtig sind könntest du bereit stellen
 
Kokujou schrieb:
Bonus-Frage: Die Datenbank ist alt, aber das Framework dass wir draufspielen können wir uns aussuchen. Ich hab schon gesucht, aber EFCore scheint so ziemlich das beste und einzige zu sein für ne SQL Server DB. Vielleicht bin ich aber auch nur mal wieder zu blöd zum suchen.
Ich verwende für solche Dinge auch gerne SQLKata. Je nach Situation kann EFCore auch Overkill sein. ORM ist nicht immer gut.

Was deinen Fall betrifft, ist auch die Frage, wie lange das noch wartbar bleibt, wenn du trotz so vieler investierter Stunden nicht weiter kommst. Klingt als wäre das später "don't touch it or it'll break" Code... Lade doch einfach die extrem verschachtelten Entitäten, die nicht so große Datenmengen sind, extra und mappe die dann manuell (z.B. die Right-Names).

Ich bin aber bei solchen Mappings auch schon auf einen Bug im EFCore gestoßen (https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/922). Der ist zwar inzwischen geschlossen, aber auch EFCore ist vor Bugs nicht gefeit :-)
 
  • Gefällt mir
Reaktionen: pizza4ever
naja... ich fürchte der "dont touch it or it'll break" Code ist bei uns allein schon die Datenbank...
Das ganze Team flucht sich da dumm und dämlich aber wenn man da jemals wieder irgendetwas anfasst, dann brennt das ganze Haus ab >.<

Dieses SQLKata klingt aber ehrlich gesagt interessant. Und ist glaube ich auch mehr oder weniger genau das was mein Kollege gemeint hat. Er war wegen dem ganzen Overhead in EFCore nämlich so minder begeistert, dass er sowas quasi von 0 auf 100 selbst bauen wollte. ne ganze SQL-Linq-Library selbst schreiben damit wir nicht an dieses 1:1 Object-Bindung gebunden sind.

wo mir eine weitere Frage einfällt:
Ist es möglich sich quasi die Basis-Entität, in meinem Fall also das Vollständige User-Model mit einer Raw-SQL Query zusammenzuschustern und dann, sobald man das DBSet hat weiter mit LinQ zu arbeiten? quasi sowas wie:

DBContext.Users.FromRawSql("i will never touch this logic again, so shut up")
.Where(...).OrderBy(...).ToList();
 
Kokujou schrieb:
Ist es möglich sich quasi die Basis-Entität, in meinem Fall also das Vollständige User-Model mit einer Raw-SQL Query zusammenzuschustern und dann, sobald man das DBSet hat weiter mit LinQ zu arbeiten? quasi sowas wie:

DBContext.Users.FromRawSql("i will never touch this logic again, so shut up")
.Where(...).OrderBy(...).ToList();

Also ich kenne ja deinen Use-Case nicht, aber ein bisschen Beispielcode von SQLKata sieht so aus, wie du es gern hättest... schau doch mal in die Doku: https://sqlkata.com/docs
C#:
IEnumerable<Post> posts = await db.Query("Posts")
    .Where("Likes", ">", 10)
    .WhereIn("Lang", new [] {"en", "fr"})
    .WhereNotNull("AuthorId")
    .OrderByDesc("Date")
    .Select("Id", "Title")
    .GetAsync<Post>();
 
  • Gefällt mir
Reaktionen: Kokujou
hab ich schon gesehen :) ich nehms zwar an, aber joins unterstützt SQLKata auch oder?

und noch eine Frage: wie muss das Objekt bei SQLKata aussehen? Wir haben sehr üble Spalten-Namen, kann man es auch so drehen dass man die Columns umbenennt? vielleicht mit Data Annotations?
 
C#:
var users = db.Query("Table").Select("user_name as UserName").Get<User>();

var topComments = new Query("Comments").OrderByDesc("Likes").Limit(10);

var posts = new Query("Posts").LeftJoin(
    topComments.As("TopComments"), // Don't forget to alias the sub query
    j => j.On("TopComments.PostId", "Posts.Id")
);

Ich glaub jetzt wäre es an der Zeit mal die Doku zu lesen... ich kann zwar gut vorlesen, aber das Lesen traue ich dir auch zu ;)
 
okay dann funktioniert das also. danke für die Info :)

eine letzte frage würde ich aber gerne noch stellen.
kann man denn mit SQLKata auch nicht-string-basierte queries schreiben?

was ich möchte ist ja den ganzen overhead den EFCore erzeugen würde quasi in die Tonne schmeißen, mir mein Objekt zusammenklicken, also das UserObjekt, mit dieesen SQL Statements, immernoch halbwegs lesbar, aber dann ganz auf Type-Safety vertrauen wollen.

Kann man also z.B. die queries die bei SQL-Kata rauskommen mit EFCore queries verbinden? (falls das überhaupt unterschiedliche sind)

denn sobald mein Objekt steht und ich die ganze crappige Datenbank-Architektur überbrückt habe würde ich gerne auf type-safety bauen. So sind renamings, Wartbarkeit und Lesbarkeit wieder völlig gewährleistet
 
Kokujou schrieb:
kann man denn mit SQLKata auch nicht-string-basierte queries schreiben?
Ich weiß nicht genau was du meinst. Der Vorteil an SQLKata ist die Flexibilität. Es ist eben ein Query-Builder, statt eines ORMs. Es nimmt dir folgende Arbeiten ab:
  • DBAL - mehrere DBMS werden unterstützt
  • Prepared Parameterized Statement - du musst nicht selbst Queries, Platzhalter und Bindings definieren
  • Type mapping - du musst nicht selbst die Ergebnisse in Model Types konvertieren
  • ...

Das bedeutet es ist typesafe, leichtgewichtiger, hat aber nicht die Möglichkeit, Relations zu definieren und zu automatisieren. Es gibt zwas sowas wie Include und IncludeMany, das ist aber eingeschränkt. Sprich: Wenn du einen "User" lädst, hat der nicht automatisch "Roles" - die muss man eben manuell definieren und dazu laden. Typesafety hast du so lange, wie du z.B. bei GetAsync<MyType> ein Objekt als Ziel angibst. Wenns ein komplexer Query ist, kannst du aber auch mit object oder dynamic arbeiten.
Kokujou schrieb:
Kann man also z.B. die queries die bei SQL-Kata rauskommen mit EFCore queries verbinden? (falls das überhaupt unterschiedliche sind)
Nein... das sind zwei völlig verschiedene Ansätze. EFCore als ORM baut die Queries anhand der API Aufrufe (wenig beeinflusbar), SQLKata als QueryBuilder und DBAL baut die Queries nach deinen Vorgaben (flexibel). Ich kenne in EFCore keine (empfohlene) Möglichkeit, Queries manuell auszuführen.


Ich bin auch nicht der Werbefritze der Lib. Die hat bestimmt viele Schwächen - aber ich habe Sie schon erfolgreich benutzt... Versuchs doch einfach mal und schau wie weit du kommst. Ohne Test wirst du es nicht wissen...
 
ausprobieren werde ich es natürlich gleich am Montag.
Aber Type-Safety? viellicht steh ich gerade auf dem Schlauch, kann sein und vielleicht wird alles auch offensichtlich sobald ich wieder an die Arbeit gehe aber bis jetzt stellt sich das für mich so dar:

Du baust dir Queries zusammen. Du weißt nicht ob die Tabelle existiert, du weißt nicht ob die Spalte existiert, du weißt nicht welchen Typ eine Property hat und der Compiler hat keine Ahnung ob das was du da rein schreibst korrekt ist, das ist die Kehrseite, sow ie ich das verstehe. Was ja für den ersten Teil meines Vorhabens in Ordnung ist.

Aber irgendwann, und zwar ich rate mal an der Stelle .GetAsync(...) hättest du dein Objekt. Und ab da könntest du sämtliche weiteren queries auf dem Objekt laufen lassen. klar soweit? Das ist vielleicht zu spezifisch und ich muss mich wahrscheinlich mit entweder oder behelfen... oder wenigstens mit nameof type-safety improviseren. aber vielleichtg eht es ja doch irgendwie :)
 
Also ich habe ein Database First Projekt mit dem .NET Entity Framework am Laufen, das hat deutlich tiefere Abhängigkeiten und läuft völlig problemlos.
In der Datenbank muss halt alles schön Primärschlüssel und korrekte Fremdschlüssel haben, dann geht alles automatisch. Im EF Core scheint man nicht diesen netten Visual Studio "Designer" zu haben (zumindest laut einer 2 min Googlesuche), aber das "scaffold" sollte dasselbe machen.
Wenn bei dir Verknüpfungen nicht passen, schau erstmal, ob in der DB was fehlt.

Um irgendwelche Schlüssel musst du dich anschließend beim "Benutzen"/Abrufen gar nicht kümmern, dafür gibt es ja navigation properties.
 
Enurian schrieb:
Wenn bei dir Verknüpfungen nicht passen, schau erstmal, ob in der DB was fehlt.
die DB kann ich leider nicht anfassen, ich muss ja EFCore auf eine existierende DB aufsetzen und sollte ich da was anfassen werde ich von meinen Teammitgliedern erdolcht XD

ich werd mir aber auch mal dieses scaffolding ansehen, vielleicht überkompliziere ich ja auch alles.

ich glaube ich habe jetzt auf jeden fall genug stichpunkte mit denen ich arbeiten kann :) danke dass ihr alle so viel geduld mit mir hattet^^ ich weiß vermutlich denken viele "warum googlet der sack nicht einfach", aber ich verstehe dinge einfach besser wenn ich mit echten Menschen rede, die mir was erklären :)
 
Zurück
Oben