2010-04-06 17 views

Odpowiedz

9

Dla Entity Framework 1.0, stworzyłem kilka metod rozszerzenia tego.

public static class EntityFrameworkIncludeExtension 
{ 
    public static ObjectQuery<T> Include<T>(this ObjectQuery<T> src, Expression<Func<T, StructuralObject>> fetch) 
    { 
     return src.Include(CreateFetchingStrategyDescription(fetch)); 
    } 

    public static ObjectQuery<T> Include<T>(this ObjectQuery<T> src, Expression<Func<T, RelatedEnd>> fetch) 
    { 
     return src.Include(CreateFetchingStrategyDescription(fetch)); 
    } 

    public static ObjectQuery<T> Include<T, TFectchedCollection>(this ObjectQuery<T> src, Expression<Func<T, IEnumerable<TFectchedCollection>>> fetch) 
    { 
     return src.Include(CreateFetchingStrategyDescription(fetch)); 
    } 

    public static ObjectQuery<T> Include<T, FetchedChild>(this ObjectQuery<T> src, Expression<Func<T, RelatedEnd>> fetch, Expression<Func<FetchedChild, Object>> secondFetch) 
     where FetchedChild : StructuralObject 
    { 
     return src.Include(CombineFetchingStrategies(fetch, secondFetch)); 
    } 

    public static ObjectQuery<T> Include<T, FetchedChild>(this ObjectQuery<T> src, Expression<Func<T, RelatedEnd>> fetch, Expression<Func<FetchedChild, RelatedEnd>> secondFetch) 
     where FetchedChild : StructuralObject 
    { 
     return src.Include(CombineFetchingStrategies(fetch, secondFetch)); 
    } 

    public static ObjectQuery<T> Include<T, FetchedChild>(this ObjectQuery<T> src, Expression<Func<T, RelatedEnd>> fetch, Expression<Func<FetchedChild, StructuralObject>> secondFetch) 
     where FetchedChild : StructuralObject 
    { 
     return src.Include(CombineFetchingStrategies(fetch, secondFetch)); 
    } 

    public static ObjectQuery<T> Include<T, FetchedChild>(this ObjectQuery<T> src, Expression<Func<T, IEnumerable<FetchedChild>>> fetch, Expression<Func<FetchedChild, Object>> secondFetch) 
     where FetchedChild : StructuralObject 
    { 
     return src.Include(CombineFetchingStrategies(fetch, secondFetch)); 
    } 

    public static ObjectQuery<T> Include<T, FetchedChild>(this ObjectQuery<T> src, Expression<Func<T, IEnumerable<FetchedChild>>> fetch, Expression<Func<FetchedChild, RelatedEnd>> secondFetch) 
     where FetchedChild : StructuralObject 
    { 
     return src.Include(CombineFetchingStrategies(fetch, secondFetch)); 
    } 

    public static ObjectQuery<T> Include<T, FetchedChild>(this ObjectQuery<T> src, Expression<Func<T, IEnumerable<FetchedChild>>> fetch, Expression<Func<FetchedChild, StructuralObject>> secondFetch) 
     where FetchedChild : StructuralObject 
    { 
     return src.Include(CombineFetchingStrategies(fetch, secondFetch)); 
    } 

    private static String CreateFetchingStrategyDescription<TFetchEntity, TFetchResult>(
     Expression<Func<TFetchEntity, TFetchResult>> fetch) 
    { 
     fetch = (Expression<Func<TFetchEntity, TFetchResult>>)FixedWrappedMemberAcces.ForExpression(fetch); 
     if (fetch.Parameters.Count > 1) 
      throw new ArgumentException("CreateFetchingStrategyDescription support only " + 
       "one parameter in a dynamic expression!"); 

     int dot = fetch.Body.ToString().IndexOf(".") + 1; 
     return fetch.Body.ToString().Remove(0, dot); 
    } 

    private static String CreateFetchingStrategyDescription<T>(Expression<Func<T, Object>> fetch) 
    { 
     return CreateFetchingStrategyDescription<T, Object>(fetch); 
    } 

    private static String CombineFetchingStrategies<T, TFetchedEntity>(
       Expression<Func<T, Object>> fetch, Expression<Func<TFetchedEntity, Object>> secondFetch) 
    { 
     return CombineFetchingStrategies<T, Object, TFetchedEntity, Object>(fetch, secondFetch); 
    } 

    private static String CombineFetchingStrategies<TFetchEntity, TFetchResult, TFetchedEntity, TSecondFetchResult>(
     Expression<Func<TFetchEntity, TFetchResult>> fetch, Expression<Func<TFetchedEntity, TSecondFetchResult>> secondFetch) 
    { 
     return CreateFetchingStrategyDescription<TFetchEntity, TFetchResult>(fetch) + "." + 
      CreateFetchingStrategyDescription<TFetchedEntity, TSecondFetchResult>(secondFetch); 
    } 
} 

Zastosowanie:

Orders.Include(o => o.Product); // generates .Include("Product") 
Orders.Include(o => o.Product.Category); // generates .Include("Product.Category") 
Orders.Include(o => o.History); // a 1-* reference => .Include("History") 
// fetch all the orders, and in the orders collection. 
// also include the user reference so: .Include("History.User") 
// but because history is an collection you cant write o => o.History.User, 
// there is an overload which accepts a second parameter to describe the fetching 
// inside the collection. 
Orders.Include(o => o.History, h => h.User); 

nie testowałem tego na EF4.0, ale można oczekiwać, że do pracy.

+1

Należy również pracować dla EF 4.0, ale tylko jeśli używasz klasy markowe generowane. Nie będzie działać z jednostkami POCO, ponieważ nie dziedziczą one po 'StructuralObject' –

+0

To niestety nie kompiluje się (.Net 4), ponieważ' FixedWrappedMemberAcces' jest nieznany. –

+0

ah, jest ofiarą kopii i wklejenia z naszego źródła. Prawda jest taka, że ​​nie potrzebujesz tej linii ... to tylko rozwiązanie problemu z owiniętymi polami (zobacz http: //landman-code.blogspot .com/2010/08/protection-your-entitycollections-from.html więcej informacji na ten temat), ale nie potrzebujesz tego, aby załączniki działały. –

8

To dość łatwe do zrobienia, używając wyrażeń:

public static class ObjectQueryExtensions 
{ 
    public static ObjectQuery<T> Include<T, TProperty>(this ObjectQuery<T> objectQuery, Expression<Func<T, TProperty>> selector) 
    { 
     MemberExpression memberExpr = selector.Body as MemberExpression; 
     if (memberExpr != null) 
     { 
      return objectQuery.Include(memberExpr.Member.Name); 
     } 
     throw new ArgumentException("The expression must be a MemberExpression", "selector"); 
    } 
} 

Można go używać dokładnie tak, jak na przykład w swoim pytaniu


UPDATE

wersja ulepszona, która obsługuje wiele właściwości powiązane:

public static class ObjectQueryExtensions 
{ 
    public static ObjectQuery<T> Include<T, TProperty>(this ObjectQuery<T> objectQuery, Expression<Func<T, TProperty>> selector) 
    { 
     string propertyPath = GetPropertyPath(selector); 
     return objectQuery.Include(propertyPath); 
    } 

    public static string GetPropertyPath<T, TProperty>(Expression<Func<T, TProperty>> selector) 
    { 
     StringBuilder sb = new StringBuilder(); 
     MemberExpression memberExpr = selector.Body as MemberExpression; 
     while (memberExpr != null) 
     { 
      string name = memberExpr.Member.Name; 
      if (sb.Length > 0) 
       name = name + "."; 
      sb.Insert(0, name); 
      if (memberExpr.Expression is ParameterExpression) 
       return sb.ToString(); 
      memberExpr = memberExpr.Expression as MemberExpression; 
     } 
     throw new ArgumentException("The expression must be a MemberExpression", "selector"); 
    } 
} 

przykład:

var query = X.Include(x => x.Foo.Bar.Baz) // equivalent to X.Include("Foo.Bar.Baz") 
+0

tak, tak też zacząłem, ale twoja ma tę wadę, że podczas kompilacji może generować wyjątki w czasie wykonywania. ponieważ nie ograniczasz tego, czym może być TProperty. EF lubi tylko swoje własne właściwości w Uwzględnianiu, ponieważ nie może odwzorować zadeklarowanych właściwości na swój model danych (przynajmniej w EF1.0). To dlatego uwzględniłem wszystkie przeciążenia, które ograniczają wyrażenia, aby zapewnić kompilację czasu bezpieczeństwa dla załączników. Chociaż wszystkie inne wyrażenia w LINQ nadal mogą generować błędy środowiska wykonawczego. –

+0

Zgoda ... niestety nie można sprawdzić * wszystkiego * podczas kompilacji. W dalszym ciągu deweloper jest odpowiedzialny za to, aby wyrażenie rzeczywiście zwracało odwzorowaną właściwość. –

+0

Uzgodnione, to kompromis. –

1

Dobra wiadomość, że CTP4 EF4 obsługuje obecnie tę funkcję.

1

Inną opcją jest włączenie wewnętrznej klasy cząstkowej do klasy za pomocą szablonu TT.

kodu T4:

<# 
    region.Begin("Member Constants"); 
#> 
    public partial class <#=code.Escape(entity)#>Members 
    { 
<# 
    foreach (EdmProperty edmProperty in entity.Properties.Where(p => p.TypeUsage.EdmType is PrimitiveType && p.DeclaringType == entity)) 
    { 
     bool isForeignKey = entity.NavigationProperties.Any(np=>np.GetDependentProperties().Contains(edmProperty)); 
     bool isDefaultValueDefinedInModel = (edmProperty.DefaultValue != null); 
     bool generateAutomaticProperty = false; 
     #> 
     public const string <#=code.Escape(edmProperty)#> = "<#=code.Escape(edmProperty)#>"; 
     <# 
    } 
    #> 
    } 
    <# 
    region.End(); 
#> 

która będzie produkować coś takiego:

#region Member Constants 
    public partial class ContactMembers 
    { 
     public const string ID = "ID"; 
       public const string OriginalSourceID = "OriginalSourceID"; 
       public const string EnabledInd = "EnabledInd"; 
       public const string EffectiveDTM = "EffectiveDTM"; 
       public const string EndDTM = "EndDTM"; 
       public const string EnterDTM = "EnterDTM"; 
       public const string EnterUserID = "EnterUserID"; 
       public const string LastChgDTM = "LastChgDTM"; 
       public const string LastChgUserID = "LastChgUserID"; 
      } 

    #endregion 
11

W EF 4.1, istnieje built-in extension method do tego.

Musisz mieć odniesienie do zestawu "EntityFramework" (gdzie EF 4.1 żyje) w twoim projekcie i użyć System.Data.Entity.

using System.Data.Entity; 

Jeśli chcesz zawierać zagnieżdżone podmioty, robisz to tak:

 db.Customers.Include(c => c.Orders.Select(o => o.LineItems)) 

Nie wiem, czy to działa na EF4.0 podmiotów (ObjectContext based).

+0

Właśnie przetestowałem to i to działa! Mam elementy Self Tracking (ponieważ nie mogę używać obiektów PbO DbContext z moim scenariuszem WCF). Są one oparte na ObjectContext i musiałem tylko dodać EntityFramework Reference (przez NuGet), wstawić linię używaną, jak tu podajesz i mogłem użyć rozszerzenia Include methods! –

+0

Pytanie brzmi, nie mam absolutnie żadnego powodu, aby dodać referencje EF 4.1 inne niż to sprawdzanie czasu kompilacji mojego gorliwego ładowania ... Zgaduję, że jest dopuszczalne, aby go dodać, ponieważ jest on używany tylko po stronie serwera? Być może znajdę inne metody, których użyję. –

+0

Metoda rozszerzenia EF4.1 analizuje podane wyrażenie, a następnie tłumaczy je na wywołanie oparte na łańcuchach. Więc skutecznie zamienia powyższy przykład na '.Include (" Orders.LineItems ")'. Prawdopodobnie możesz znaleźć innych, którzy pisali metody rozszerzeń, które robią to samo, jeśli naprawdę nie chcesz instalować EF4.1. Przed 4.1 pisałem własne (zapożyczając z innych przykładów) i mogę powiedzieć, że to nie jest zbyt zabawne. (API wyrażeń jest ... dziwne.) Cieszyłem się, że uzyskałem dostęp do wbudowanej metody. –

2

Innym rozwiązaniem jest pobranie nazwy obiektu za pomocą EntitySet.Name.
kod będzie:

var context = new DBContext(); 
context.Organizations.Include(context.Assets.EntitySet.Name).Where(o => o.Id == id).Single() 
Powiązane problemy