Laden...

[Gelöst] - EF 6 - Relationen (Listen) zu ein Objekt nachladen

Erstellt von elTorito vor 9 Jahren Letzter Beitrag vor 8 Jahren 1.866 Views
elTorito Themenstarter:in
177 Beiträge seit 2009
vor 9 Jahren
[Gelöst] - EF 6 - Relationen (Listen) zu ein Objekt nachladen

verwendetes Datenbanksystem: SQL 2008, EF 6

Hi,

ich bin dabei meine Anwendung auf EF umzustellen. (Datenbank vorhanden)

Ich habe folgende Tabellen:
Users
Roles
Users_Roles

Tabelle Users und Roles besteht aus 2 Spalten : Guid und Name, wo Guid der PK ist
Tabelle Users_Roles besteht aus 3 Guid Spalten (ID, UserID, RoleID), wo ID = PK ist, und UserID und RoleID jeweils ein Fremdschlüssel auf die Tabelle Roles.RoleID und Users.UserID haben

Ein User kann mehrere Role haben.

Mit den EF PowerTools mache ich Reverse Engineer Code First.
Ich erhalte folgende Modelle:


public partial class User
    {
        public User()
        {
            this.Users_Roles = new List<Users_Roles>();
        }

        public System.Guid ID { get; set; }
        public string UserAccount { get; set; }
        public virtual ICollection<Users_Roles> Users_Roles { get; set; }
    }

public partial class Role
    {
        public Role()
        {
            this.Users_Roles = new List<Users_Roles>();
        }

        public System.Guid ID { get; set; }
        public string Name { get; set; }
        public virtual ICollection<Users_Roles> Users_Roles { get; set; }
    }

public partial class Users_Roles
    {
        public System.Guid ID { get; set; }
        public System.Guid UserID { get; set; }
        public System.Guid RoleID { get; set; }
        public virtual Role Role { get; set; }
        public virtual User User { get; set; }
    }

LazyLoadingEnabled und ProxyCreationEnabled sind false.

Mit folgender Funktion hole ich mir den User:


public User GetUserByUserAccountName(string username)
{
    var user = _unitOfWork.Context.Users.Include("Users_Roles").Where(s => s.UserAccount == username).FirstOrDefault<User>();
return user;
}

Als Ergebnis erhalte ich 2 User_Roles:

User.User_Roles[0]
User.User_Roles[1]

Aber verstehe nicht so ganz die Daten welche die User_Roles beinhalten:

User.User_Roles[0].ID = (PK in der Users_Roles Tabelle)
User.User_Roles[0].RoleID = (Guid der Role)
User.User_Roles[0].UserID = (Guid des User)
User.User_Roles[0].Role = Null
User.User_Roles[0].User = User.User_Roles[0]

Früher hatte mein User die Eigenschaft Role[], und ich hatte Manuell
Role[] userRoles = "Select alle Rollen die zur UserID passen"

Dann hatte ich eine Abfrage:


 foreach (Role role in userRoles){
                if (String.Compare(role.Name, EmployeerRoleName,
                    StringComparison.InvariantCultureIgnoreCase) == 0)
                {
                    // Current user is a member of employee role.
                    return;
                }
            }

Das funktioniert natürlich nicht mehr :


            foreach (Users_Roles r in userRoles)
            {
                if (String.Compare(r.Role.Name, EmployeerRoleName,
                    StringComparison.InvariantCultureIgnoreCase) == 0)
                {
                    // Current user is a member of employee role.
                    return;
                }
            }

weil User.User_Roles[0].Role = Null ist.

  1. sind meine Tabellenbeziehung korrekt?

  2. Wie bekomme ich eine Auflistung von Roles im User Objekt?

Statt


public virtual ICollection<[B]Users_Roles[/B]> Users_Roles { get; set; }

bräuchte ich doch eigentlich ein?:


public virtual ICollection<[B]Role[/B]> Users_Roles { get; set; }

Ist das jetzt falsch weil der Code Automatisiert generiert wurde? oder habe ich ein Denkfehler?

Danke

M
402 Beiträge seit 2005
vor 9 Jahren

Hi..

_unitOfWork.Context.Users.Include("Users_Roles").Where(s => s.UserAccount == username).FirstOrDefault<User>();

Du holst dir hier nur die User mit den User_Roles aus der Datenbank.
Es wurde aber nicht angegeben, dass zu den User_Roles auch noch die Role geladen werden soll.

Du musst hier also dein "include" erweitern.

_unitOfWork.Context.Users.Include("Users_Roles.Role").Where(s => s.UserAccount == username).FirstOrDefault<User>();

Dann ist auch User.User_Roles[0].Role nicht mehr null.

lg

elTorito Themenstarter:in
177 Beiträge seit 2009
vor 9 Jahren

Hi..
Du musst hier also dein "include" erweitern.

_unitOfWork.Context.Users.Include("Users_Roles.Role").Where(s => s.UserAccount == username).FirstOrDefault<User>();  
  

Dann ist auch User.User_Roles[0].Role nicht mehr null.

lg

Stimmt. Danke.

Aber so richtig "übersichtlich"/"Ordentlich" finde ich das nicht...

Dass kommt wahrscheinlich von der automatischen Code Generierung ? Die mich verwirrt?

Weil der User soll ja eine Auflistung von Role haben und nicht Users_Roles , oder?

M
402 Beiträge seit 2005
vor 9 Jahren

Hi...

das liegt mit Sicherheit am automatisch generierten Code oder an der bereits bestehenden Datenbank...

Was du gerne hättest sollte eigentlich so aussehen:

public partial class User
     {
         public User()
         {
             this.Roles = new List<Role>();
         }

         public System.Guid ID { get; set; }
         public string UserAccount { get; set; }
         public virtual ICollection<Role> Roles { get; set; }
     }

public partial class Role
     {
         public Role()
         {
             this.Users = new List<User>();
         }

         public System.Guid ID { get; set; }
         public string Name { get; set; }
         public virtual ICollection<User> Users { get; set; }
     }
 
16.833 Beiträge seit 2008
vor 9 Jahren

virtual weglassen und es wird direkt geladen (>kannst auf das include verzichten).
virtual = lazy loading.

M
402 Beiträge seit 2005
vor 9 Jahren

@Abt:

gibt aber auch Situationen wo das automatische/fixe laden von Navigation-Properties (eagerloading) kontraproduktiv ist.

Ich verwende daher virtual und hole mir die Daten über "include" dazu wen ich sie wirklich brauche.

lg

elTorito Themenstarter:in
177 Beiträge seit 2009
vor 9 Jahren

das liegt mit Sicherheit am automatisch generierten Code oder an der bereits bestehenden Datenbank...

👍

Hab die Schlüssel in der Users_Roles Table geändert, dort brauche ich ja keine eigene ID als Primary Key, jeder User kann mindestens eine Role haben. Also PK auf UserID und RoleID gesetzt.

Jetzt habe ich wieder **user.Roles **

virtual weglassen und es wird direkt geladen (>kannst auf das include verzichten).
virtual = lazy loading.

Wenn ich Virtual und Include() weg lasse, werden die Roles nicht geladen ...

werden nur geladen mit


var user = _unitOfWork.Context.Users.Include("Roles").Where(s => s.UserAccount == username).FirstOrDefault<User>();

oder


var user = _unitOfWork.Context.Users.Where(s => s.UserAccount == username).FirstOrDefault<User>();
            _unitOfWork.Context.Entry(user).Collection(s => s.Roles).Load();

Vielen Dank.
gruß
Peter

16.833 Beiträge seit 2008
vor 9 Jahren

gibt aber auch Situationen wo das automatische/fixe laden von Navigation-Properties (eagerloading) kontraproduktiv ist.

Das weiß ich auch; aber er erzwingt es im Select ja sowieso und UserRoles als Gesamtentität zu laden; da braucht man i.d.R. dann eh auch die Rollen. Ansonsten würde man es sowieso über einen Partial Select umsetzen.

Zudem wäre da noch der Faktor Change Tracking.

@elTorito: sicher?
Ohne virtual wird quasi eager-loading erzwungen, sowohl bei Navigation Properties wie auch bei Scalar Properties.
Würde mich ja doch sehr wundern, wenn das seit dem EF6 anders sein soll (auch wenn es einem POCO eher gerecht wird).

elTorito Themenstarter:in
177 Beiträge seit 2009
vor 9 Jahren

@elTorito: sicher?
Ohne virtual wird quasi eager-loading erzwungen, sowohl bei Navigation Properties wie auch bei Scalar Properties.
Würde mich ja doch sehr wundern, wenn das seit dem EF6 anders sein soll (auch wenn es einem POCO eher gerecht wird).

Ja.

Wobei nicht ausgeschlossen ist dass ich an einer anderen Stelle was krum habe 😃

Wenn ich aber hier und hier lese, scheint das so zu sein.

B
218 Beiträge seit 2012
vor 8 Jahren

Ohne virtual wird quasi eager-loading erzwungen, sowohl bei Navigation Properties wie auch bei Scalar Properties.
Würde mich ja doch sehr wundern, wenn das seit dem EF6 anders sein soll (auch wenn es einem POCO eher gerecht wird).

Das ist falsch. Das Weglassen von virtual sorgt dafür, dass Lazy Loading deaktivert wird, nicht aber, dass Eager Loading aktiviert wird.

https://msdn.microsoft.com/en-us/data/jj574232.aspx

16.833 Beiträge seit 2008
vor 8 Jahren

Dann hat sich das in einer der Versionen geändert, was ich nicht ausschließen kann.