【问题标题】:EF4 LINQ Include(string) alternative to hard-coded string?EF4 LINQ Include(string) 替代硬编码字符串?
【发布时间】:2012-04-14 15:36:20
【问题描述】:

有没有其他选择:

Organizations.Include("Assets").Where(o => o.Id == id).Single()

我想看到类似的东西:

Organizations.Include(o => o.Assets).Where(o => o.Id == id).Single()

避免硬编码字符串“资产”。

【问题讨论】:

    标签: linq string entity-framework include entity-framework-4


    【解决方案1】:

    在 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)。

    【讨论】:

    • 刚刚测试过,它可以工作!我有自我跟踪实体(因为我不能在我的 WCF 场景中使用 DbContext POCO 实体)。它们是基于 ObjectContext 的,我只需要添加 EntityFramework 引用(通过 NuGet),按照您在此处的说明放置 using 行,我就可以使用扩展 Include 方法!
    • 问题是,除了这个编译时间检查我的急切加载之外,我绝对没有理由添加 EF 4.1 引用......我想添加它是可以接受的,因为它只在服务器上使用边?也许我会找到其他我会使用的扩展方法。
    • EF4.1 扩展方法只是解析您提供的表达式,然后将其转换为基于字符串的包含调用。因此它有效地将上面的示例转换为.Include("Orders.LineItems")。如果您真的不想安装 EF4.1,您可能会找到其他编写了执行相同操作的扩展方法的人。在 4.1 之前,我写了自己的(借鉴其他示例),我可以告诉你这不太有趣。 (表达式 API 很……奇怪。)我很高兴能够访问此内置方法。
    【解决方案2】:

    对于 Entity Framework 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 上对此进行测试,但我希望它能够工作。

    【讨论】:

    • 它也应该适用于 EF 4.0,但前提是您使用设计器生成的类。它不适用于 POCO 实体,因为它们不继承自 StructuralObject
    • 这很遗憾无法编译(.Net 4),因为FixedWrappedMemberAcces 是未知的。
    • 啊,它是从我们的来源复制和粘贴的受害者。事实是,你不需要那行。它只是为了解决包装字段的问题(参见@987654321 @ 了解有关此的更多信息)但您不需要它来使包含工作。
    • 在 EF4 中,您可以包含 EntityCollection,然后深入挖掘集合的类型。你的解决方案支持吗?
    【解决方案3】:

    这很容易做到,使用表达式:

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

    您可以完全按照问题中的示例使用它


    更新

    改进版,支持多个链式属性:

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

    【讨论】:

    • 是的,我也是这样开始的,但你的缺点是编译时会产生运行时异常。因为您不限制 TProperty 可以是什么。 EF 只喜欢它自己在 Include 中的属性,因为它不能将自我声明的属性映射到它的数据模型(至少在 EF1.0 中)。这就是我包含所有重载的原因,这些重载限制表达式为包含提供编译时安全性。尽管 LINQ 中的所有其他表达式仍然会产生运行时错误。
    • 同意...很遗憾,您无法在编译时检查所有内容。确保表达式真正返回映射属性仍然是开发人员的责任
    • 我用 EF4 试过你的代码,但它不起作用。方法 Include 是在 ObjectQuery 而不是 ObjectSet 上定义的。所以它是 Query 对象的扩展方法。
    【解决方案4】:
    【解决方案5】:

    另一种解决方案是使用 EntitySet.Name 检索实体名称。
    您的代码将是:

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

    【讨论】:

      【解决方案6】:

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

      【讨论】:

        【解决方案7】:

        另一种选择是使用 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
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2020-08-16
          • 1970-01-01
          • 2018-05-05
          • 1970-01-01
          • 2012-02-03
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多