2017-11-13 6 views
1

Ich habe eine Provider-Klasse und einen Artikel eins. Artikel hat eine int ProviderId{get;set} und eine public virtual Provider Provider {get;set;} Eigenschaften.Wie kann ich Child-Entitäten dynamisch in Entity Framework 6 laden

Ich weiß über Lazy Loading und warum ich nicht die Provider-Eigenschaft des Artikels außerhalb des Kontexts zugreifen kann, aber ich habe eine generische Methode, die die nächsten T wie folgt zurück:

public static T Next<T>(T currentElement) where T : class, IModel { 
    T data; 

    if (currentElement.Id >= GetLastId<T>()) 
     return currentElement; 

    using (DatabaseEntities context = new DatabaseEntities()) { 
     data = context.Set<T>().Single(el => el.Id == currentElement.Id + 1); 
    } 

    return data; 
} 

Aber ich kann nicht Rufen Sie untergeordnete Entitäten wie Provider in der Artikelklasse ab. Wie kann ich alle Entitäten einschließen? Sollte ich die Methode aktualisieren und eine pro Entität erstellen? Ich lese über Eager loading und Explicit loading aber ich weiß nicht, wie ich diese in meiner Methode implementieren kann.

Hinweis:

Nicht alle meine Entitäten Einheit Kinder und ich habe mehr Methoden wie Previous<T>(), First<T>() oder Last<T>(), die die Arbeit tun, die Sie erwarten.

+0

Nicht Ihre Frage zu beantworten, aber ich denke, mit 'Id + 1 'zu Ihrem nächsten Element zu erhalten kann gefährlich sein. Wenn eine Zeile in Ihrer Tabelle gelöscht wird, wird eine Ausnahme ausgelöst. – DavidG

+0

Wie wäre es mit einer Antwort, würden Sie bereit sein, eine Liste von Includes für diese Methode zu übergeben? – DavidG

+0

@DavidG Sie haben Recht mit dem ID-Problem, also sollte ich alle Datensätze in einer Liste speichern (vielleicht 3000)? Aber für diese Zeit, dass ich keine Zeile löschen kann, und ja, ich bin offen für die Verwendung enthält. – legomolina

Antwort

1

Sie können Ihre Methode erweitern, um eine Liste möglicher Includes zu erstellen. Zum Beispiel:

public static T Next<T>(T currentElement, params Expression<Func<T, object>>[] includes) 
    where T : class, IModel 
{ 
    if (currentElement.Id >= GetLastId<T>()) 
     return currentElement; 

    using (DatabaseEntities context = new DatabaseEntities()) 
    { 
     IQueryable<T> query = context.Set<T>(); 

     //Deferred execution allows us to build up the query 
     foreach (var include in includes) 
     { 
      query = query.Include(include); 
     } 

     return query.Single(el => el.Id == currentElement.Id + 1); 
    } 
} 

Und es wie folgt verwenden:

var someEntity = ...; 

var nextEntity = Next(someEntity, 
    e => e.Childcollection1, 
    e => e.Childcollection2, 
    // 
    e => e.ChildcollectionN) 

Als weiteren Punkt, auf Ihre nächste Einheit zu erhalten, sollten Sie nicht auf den ID-Werte sind sequenzielle verlassen, zum Beispiel, sie wird aus der Reihenfolge geraten, wenn Einträge gelöscht werden. Stattdessen, so etwas wie dies berücksichtigen:

return query.OrderBy(el => el.Id).First(el => el.Id > currentElement.Id); 
+0

Danke, ich werde es versuchen, wenn Sie wieder nach Hause kommen. Und danke für das Schnipsel, um das nächste Element zu nehmen;) – legomolina

+0

Funktioniert perfekt! Danke: D – legomolina

1

Sie könnten eine Überlastung Ihrer Next Methode erstellen, die akzeptiert ein Expression<Func<T, object>>:

public static T Next<T>(T currentElement, Expression<Func<T, object>> navigationProperty) where T : class, IModel 
{ 
    T data; 

    if (currentElement.Id >= GetLastId<T>()) 
     return currentElement; 

    using (DatabaseEntities context = new DatabaseEntities()) 
    { 
     IQueryable<T> dbQuery = context.Set<T>(); 
     if (navigationProperty != null) 
      dbQuery = dbQuery.Include<T, object>(navigationProperty); 

     data = dbQuery.AsNoTracking().Single(el => el.Id == currentElement.Id + 1); 
    } 
    return data; 
} 

Verbrauch:

var entity = Next(instance, x => x.Provider); 

Bitte beachten Sie die Folgender Blogeintrag für weitere Informationen und ein vollständiges Beispiel.

eine generische Datenzugriffsschicht Implementierung unter Verwendung von Entity Framework:https://blog.magnusmontin.net/2013/05/30/generic-dal-using-entity-framework/

0

So haben Sie eine Eins-zu-Viele-Beziehung zwischen Provider und Article: Jeder Provider hat null oder mehr Articles, und jedes Article gehört genau zu ein Provider.

Wenn Sie diese richtig in Entity Framework eine Eins-zu-viele-Beziehung konfiguriert haben, sollte ein Provider einen Verweis auf seine vielen Articles haben:

public virtual ICollection<Article> Articles{get; set;} 

Des Weiteren haben Sie eine Funktion Next, die als Eingabe ein dauert Objekt der Klasse T, die eine Klasse, die IModel implementiert. Next gibt das nächste Element aus dem DbSet von Ts zurück, vorausgesetzt, Sie haben eine korrekte Definition für das nächste Element.

Anscheinend möchten Sie die Funktion Next so anpassen, dass Sie diese Funktion verwenden können, um die nächste Provider mit all ihren Articles zu erhalten.

Allgemeiner: Wenn T ein Typ ist, der in ihm Eins-zu-Viele-Beziehungen richtig entworfen hat, und Sie ein Objekt vom Typ T haben, möchten Sie das T mit der ersten ID höher als die ID des aktuellen T , inklusive all seiner ICollections.

Ich habe diese kleinere Funktionen in Sever unterteilt:

  • Eine Funktion, die, da ein Typ T alle Eigenschaften, die ICollection implementieren zurück
  • Eine Funktion, die, da ein DbSet alle Elemente der DbSet zurück inklusive all seinen ICollections

diese beide gegeben, und Funktionen wie OrderBy und FirstOrDefault werden Sie den ersten Anbieter mit Id größer als die aktuellen Anbieter mit allen Artikeln bekommen können.

static class QueryableExtensions 
{ 
    // returns true if PropertyInfo implements ICollection<...> 
    public static bool ImplementsICollection(this PropertyInfo propertyInfo) 
    { 
     return propertyInfo.IsGenericType 
      && propertyInfo.GetGenericTypeDefinition() == typeof(ICollection<>); 
    } 

    // returns all readable & writable PropertyInfos of type T that implement ICollection<...> 
    public static IEnumerable<PropertyInfo> CollectionProperties<T>() 
    { 
     return typeof(T).GetProperties() 
      .Where(prop => prop.CanRead 
       && prop.CanWrite 
       && prop.PropertyType.ImplementICollection(); 
    } 

    // given an IQueryable<T>, adds the Include of all ICollection<...> T has 
    public static IQueryable<T> IncludeICollection<T>(IQueryable<T> query) 
    { 
     var iCollectionProperties = CollectionProperties<T>(); 
     foreach (var collectionProperty in collectionProperties) 
     { 
       query = query.Include(prop.Name); 
     } 
     return query; 
    } 

    // given a IQueryable<T> and a T return the first T in the query with Id higher than T 
    // for this we need to be sure every T has an IComparable property Id 
    // so T should implement interface IId (see below) 
    public T Next<T>(this IQueryable<T> query, T currentItem) 
     where T : IId // makes sure every T has aproperty Id 
    { 
     T nextElement = query 
      // keep only those DbSet items that have larger Ids: 
      .Where(item => currentItem.Id < item.Id) 
      // sort in ascending Id: 
      .OrderBy(item => item.Id 
      // keep only the first element of this sorted collection: 
      .FirstOrDefault(); 
     return T 
    } 
} 

Ich muss sicher sein, dass jedes T eine ID hat, sonst können Sie nicht die nächste ID bekommen. Wahrscheinlich haben Sie diese in Ihrer IModel Schnittstelle:

public Interface IId 
{ 
    public int Id {get;} 
} 

Nach diesen Funktionen werden Ihre Anfrage wie:

public static T Next<T>(T currentElement) where T : class, IModel, IId 
{ 
    T data; 
    if (currentElement.Id >= GetLastId<T>()) 
     return currentElement; 

    using (DatabaseEntities context = new DatabaseEntities()) 
    { 
     return context.Set<T>().Next(currentElement); 
    } 
} 
+0

Das ist genau das Gegenteil, das ich will. Mein Ziel ist es, den Provider zu bekommen, wenn ich den Artikel bekomme, aber ich werde einen Blick darauf werfen, wenn ich das Gegenteil tun muss. Vielen Dank – legomolina