Laden...

[Gelöst] LINQ2SQL: Referenzierte Objekte werden bei SELECT-Abfrage nicht mit Daten befüllt

Erstellt von Cord Worthmann vor 11 Jahren Letzter Beitrag vor 11 Jahren 1.532 Views
C
Cord Worthmann Themenstarter:in
1.215 Beiträge seit 2004
vor 11 Jahren
[Gelöst] LINQ2SQL: Referenzierte Objekte werden bei SELECT-Abfrage nicht mit Daten befüllt

verwendetes Datenbanksystem: SQL Server 2008 Express

Hallo Forumsgemeinde,

ich verwende eine Reihe von Klassen, die als Entitäten für LINQ fungieren. Dabei gibt es eine Tabelle pro konkreter Klasse. Im Einzelnen geht's um eine Entität namens "Employee", die ich mit drei anderen Entitäten assoziiere ("Person", "Address", "BankAccount") - es besteht eine 1:1-Beziehung. Das habe ich in der Employee-Klasse so realisiert:


// Die Fremdschlüssel als private members.
[Column(Name="PersonID")]
private int PersonID { get; set; }
[Column(Name = "AddressID")]
private int AddressID { get; set; }
[Column(Name = "BankAccountID")]
private int BankAccountID { get; set; }

// Die Entitäten dazu
[Association(ThisKey = "PersonID", IsForeignKey = true, IsUnique=true)]
public Person Person { get; private set; }
[Association(ThisKey = "AddressID", IsForeignKey = true, IsUnique=true)]
public Address Address { get; private set; }
[Association(ThisKey = "BankAccountID", IsForeignKey = true, IsUnique=true)]
public BankAccount BankAccount { get; private set; }

// Instanzierung der Entitäten im Constructor
public Employee()
{
    this.Person = new Person();
    this.Address = new Address();
    this.BankAccount = new BankAccount();
}

Nun zu meinem Problem. Wenn ich ein neues Employee-Objekt erstelle und in die Datenbank schreibe, klappt alles prima, die Eigenschaftswerte werden alle übernommen, auch die aus den drei assoziierten Objekten. Auch werden die neu erzeugten IDs als Fremdschlüssel in die EMPLOYEE-Tabelle eingetragen. Wenn ich allerdings per LINQ-Syntax eine Abfrage auf ein oder mehrere Employee-Entitätsobjekte starte, dann funktioniert zwar die Abfrage selbst korrekt, aber LINQ füllt nur das Employee-Objekt mit Daten aus der DB, die assoziierten Objekte bleiben hingegen leer. Ich hoffe, dass ich mein Problem verständlich schildern konnte. Mit LINQ/SQL bin ich noch ziemlich neu, und ich habe jetzt einen kompletten Tag drauf verwendet, dieses Problem zu lösen - leider ohne Ergebnis. Kann mir jemand von euch hier weiterhelfen?

Gruß
Cord

16.807 Beiträge seit 2008
vor 11 Jahren

Ich interpretiere mal Deinen Beitrag so, dass Deine Entitäten zwar richtig geschrieben, aber nicht alles gelesen wird. Jedenfalls beschreibst Du es so.
Damit wäre auch Dein Titel falsch. Ich korrigiere ihn jetzt aber mal nicht, da ich Dir die Möglichkeit lassen will das a) selbst zu korrigieren oder b) Dich besser auszudrücken.

Du solltest Linq2SQL und Entity Framework nicht ganz verwechseln.
Sie sind sich zwar sehr ähnlich aber manche Dinge funktionieren anders.

Du willst eine komplette Materialisierung beim Lesen. Sprich alle Referenzen sofort zu laden.
Erst mal möcht ich sagen, dass dies auf die Masse gesehen sehr sehr langsam ist und unheimlich Performance beim Entity Framework kostet.
Das EF ist ohnehin nicht wirklich der schnellste Mapper (im Gegenteil sogar), da er teilweise ungünstig geschrieben ist. Vor allem bei vielen Daten macht sich das sehr sehr stark bemerkbar.
Daher solltest Du damit sehr vorsichtig sein.

Das ist auch mit unter ein Grund, wieso die sogenannte Lazy-Loading-Funktion des EF standardmäßig aktiviert ist.
Das heißt: es werden nur die Daten geladen, die aktuell gebraucht werden. Alle anderen werden - sofern die Verbindung und der Entity-Proxy noch existiert - nachgeladen; darunter auch Deine Relationen.

Vermutlich verwendest Du in Deinem Business-Code direkt den Zugriff auf die Datenbank.
Besser ist es Repositories zu verwenden, in dem ALLE Zugriffe - egal ob direkt oder über Linq - realisiert werden.
Der DbContext bietet Dir die Möglichkeit an, dass Relationen über Include() automatisch mitgeladen werden, sofern sie vorhanden sind.
Willst Du also an einer Stelle im Code wirklich ALLES laden, dann solltest Du Dir eine Methode im Repository erstellen, die das realisiert, anstatt dass ÜBERALL alle Daten geladen werden. Das kann sich sonst wirklich sehr in Sachen Performance negativ auswirken.

Tipp: lass die Primärschlüssel ruhig öffentlich.
Wird genug Situationen geben, bei denen die IDs sehr nützlich sind.
Ich hab damals, als ich noch mit dem EF gearbeitet hab, sogar die FK-Spalten selbst angelegt statt das dem EF zu überlassen. So kann bei Queries nochmal einiges an Performance rausgeholt werden, wenn man direkt auf die FKs geht.

C
Cord Worthmann Themenstarter:in
1.215 Beiträge seit 2004
vor 11 Jahren

@Abt:

Du hast Recht, ich habe hier ein paar Begrifflichkeiten durcheinandergebracht, ich arbeite ausschließlich mit LINQ2SQL. Das ist eine Vorgabe, so dass ich auf keine andere Technolgie ausweichen kann. Ich werde mir deinen Vorschlag zu den Repositories gerne einmal anschauen, wobei ich keine direkten Zugriffe auf die Datenbank vornehme, sondern ausschließlich mit LINQ arbeite. Bis jetzt hat sich zumindest noch nicht die Notwendigkeit für einen direkten Zugriff ergeben.

Mein Problem konnte ich inzwischen lösen, indem ich die referenzierten Objekte in EntityRef<>-Instanzen lege. Jetzt werden auch die assoziierten Daten einwandfrei in die entsprechenden Objekte übertragen. An einem exemplarischen Beispiel sieht das so aus:


private EntityRef<Person> _Person = new EntitiyRef<Person>();

[Column(Name="PersonID")]
private int PersonID { get; set; }

[Association(ThisKey = "PersonID", IsForeignKey = true, IsUnique = true, Storage = "_Person")]
public Person Person
{
    get { return this._person.Entity; }
}

public Employee()
{
    this._Person.Entity = new Person();
}

16.807 Beiträge seit 2008
vor 11 Jahren

sondern ausschließlich mit LINQ arbeite.

Naja... wenn Du den Query im laufenden Code, also nicht ausgelagert definierst, dann ist das eben ein direkter Zugriff - egalb ob LINQ oder nicht.
Simple ausgedrückt: über eine extra Klasse quasi.

C
Cord Worthmann Themenstarter:in
1.215 Beiträge seit 2004
vor 11 Jahren

Da hast du natürlich Recht.