2010-04-06 143 views

回答

9

對於實體框架1.0,我爲此創建了一些擴展方法。

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); 
    } 
} 

用法:

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); 

我還沒有EF4.0測試這一點,但我期望它的工作。

+1

它也應該適用於EF 4.0,但只有當你使用設計器生成的類。它不適用於POCO實體,因爲它們不從'StructuralObject'繼承。 – 2010-04-06 16:58:55

+0

由於'FixedWrappedMemberAcces'是未知的,所以很遺憾不能編譯(.Net 4)。 – 2010-09-16 07:56:22

+0

啊,它是從我們的源複製和粘貼的受害者..事實是,你不需要這條線......它只是解決包裹字段的問題(請參閱http://landman-code.blogspot .com/2010/08/protection-your-entitycollections-from.html瞭解更多信息),但是你不需要這些工具。 – 2010-09-16 09:48:18

8

這很容易做到,使用表達式:

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"); 
    } 
} 

你可以在你的問題


UPDATE

改進版本,它支持多個正是用它作爲例子鏈式屬性:

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"); 
    } 
} 

實施例:

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

是的,我也是這樣開始的,但是它的缺點是編譯時會產生運行時異常。因爲你不會限制TProperty的功能。 EF只在Include中喜歡它自己的屬性,因爲它不能將自己聲明的屬性映射到它的數據模型(至少在EF1.0中)。這就是爲什麼我包含所有重載,這些重載限制了表達式爲包含提供編譯時安全性。儘管LINQ中的所有其他表達式仍然可以生成運行時錯誤。 – 2010-04-06 16:54:59

+0

同意......不幸的是,您不能在編譯時檢查*所有內容*。開發人員有責任確保表達式真正返回映射屬性 – 2010-04-06 17:01:36

+0

同意,這是一種折衷。 – 2010-04-06 17:08:25

1

好消息,EF4 CTP4目前支持此功能。

1

另一種選擇是使用TT模板在類中包含內部部分類。

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(); 
#> 

將產生類似:

#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

在EF 4.1,對於這個built-in extension method

您必須在項目中引用「EntityFramework」程序集(其中EF 4.1存在)並使用System.Data.Entity。

using System.Data.Entity; 

如果要包含嵌套的實體,你做這樣的:如果

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

不知道這個工程的EF4.0實體(ObjectContext的基礎)。

+0

剛剛測試過它,它的工作原理!我有自我跟蹤實體(因爲我不能在我的WCF場景中使用DbContext POCO實體)。他們是基於ObjectContext的,我只需要添加EntityFramework Reference(通過NuGet),把你使用的行放在這裏,我就可以使用擴展包含方法! – 2011-09-14 11:00:48

+0

問題是,我絕對沒有理由添加除此之外的EF 4.1引用編譯時間檢查我的加載...我猜是可以接受添加它,因爲它只用於服務器端?也許我會找到其他我將使用的擴展方法。 – 2011-09-14 11:06:18

+0

EF4.1擴展方法只是解析您提供的表達式,然後將其轉換爲基於字符串的Include調用。所以它有效地將上面的例子轉換成'.Include(「Orders.LineItems」)'。如果你真的不想安裝EF4.1,你或許可以找到其他人寫了擴展方法來做同樣的事情。在4.1之前,我寫了自己的(借用其他例子),我可以告訴你它不是太好玩。 (表達式API是...奇怪。)我很高興能夠訪問這個內置的方法。 – 2011-09-16 20:43:52

2

另一個解決方案是使用EntitySet.Name檢索實體名稱。
您的代碼將是:

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