【问题标题】:Eager Loading in .Net4 Link-to-EF; maybe like DataLoadOptions.Net4 Link-to-EF 中的急切加载;可能像 DataLoadOptions
【发布时间】:2010-11-23 17:55:38
【问题描述】:

我正在使用 VS2010 .Net4 Linq-to-EntityFramework 并希望显式加载一些子数据。我想提供类似于 DataLoadOptionsLoadWith 的功能,这些功能可用于 Linq-to-SQL IIUC,但不适用于 Linq-to-EF。

(顺便说一句,这是为了让我可以记录数据以便稍后在测试期间播放。我们使用了延迟加载,我需要找到这些出现并替换为急切加载。DataLoadOptions 方法将允许一种干净的方式来实现这一点。)

我正在尝试提供一种类型安全的急切加载方案,如MosesOfEgypt blog 所述。我已经修改了 T4 的一代,并且遇到了我认为是最后一个问题。在 .Net4 中,实体属性返回 ObjectSet。但不幸的是,Include 函数返回 ObjectQuery,它是 ObjectSet 的基类。

这是从修改后的 T4 模板生成的 ObjectContext 类的子集:

    #region DataLoadOptions Functionality

            public DataLoadOptions LoadOptions { get; set; }

            private ObjectSet<TEntity> ApplyDataLoadOptions<TEntity>(string queryString) where TEntity : class
            {
                var query = CreateObjectSet<TEntity>(queryString);

                if (LoadOptions != null)
                {
                    var members = LoadOptions.GetPreloadedMembers<TEntity>();

                    foreach (var member in members)
                    {
 **********              query = query.Include(member.Name);
                    }
                }
                return query;
            }

    #endregion



    #region ObjectSet Properties

        /// <summary>
        /// No Metadata Documentation available.
        /// </summary>
        public ObjectSet<Address> Addresses
        {
            get
            {
                if ((_Addresses == null))
                {
                    _Addresses = ApplyDataLoadOptions<Address>("Addresses");
                }
                return _Addresses;
            }
        }

    #endregion

以“*”开头的行是从 ObjectQueryObjectSet 的转换发生的地方。而且这是一个无效的向上转换,因此如果在设计时显式转换,将在运行时失败,除非我做错了。

一种解决方案可能是为 ObjectSet.Include 编写扩展方法,使其返回 ObjectSet 而不是 ObjectQuery。如果可能的话,我想知道如何找到 ObjectQuery.Include 函数的源代码。而且我不确定这些解决方案是否可行。

还想知道是否有办法将 Include 函数的结果从 ObjectQuery 向上转换为 ObjectSet。同样,不确定这是否可行。

任何有关在 .Net4 中为 Linq-to-EF 实现 DataLoadOptions 功能的帮助将不胜感激。

【问题讨论】:

    标签: .net linq entity-framework-4 eager-loading


    【解决方案1】:

    目前还不清楚你在这里问什么。

    .Include 返回一个ObjectQuery&lt;T&gt;,它实现了IQueryable&lt;T&gt;。为什么要把它转换成ObjectSet&lt;T&gt;??

    WRT“强类型”Include's,简短的回答是你不能。但是你可以通过扩展方法的语法糖让它变得更甜。

    我们为每个实体创建枚举(可能有点矫枉过正,但我​​讨厌魔术字符串),其中包含每个关联。我们的存储库接受这些枚举的数组。然后我们在枚举上使用扩展方法来转换为Include

    示例(简化)存储库代码:

    public ICollection<Order> GetOrdersWithUser(Expression<Func<Order,bool>> predicate, OrderAssocations[] includes)
    {
       return _ctx.Orders.WithAssociations(includes).Where(predicate).ToList();
    }
    

    还有扩展名:

    public static ObjectQuery<Order> WithAssociations(this ObjectQuery<Order> source, OrderAssociations[] includes)
    {
       var query = source;
    
       foreach (var include in includes)
       {
          query = query.Include(include.ToNavigationalProperty()));
       }
    
       return query;
    }
    

    .ToNavigationalProperty() 是枚举的另一个扩展方法,它只是返回匹配的 Navigational 属性。

    【讨论】:

    • 我正在尝试提供一种类型安全的急切加载方案,如 MosesOfEgypt 博客中所述。该站点上的代码是为测试版编写的,似乎与已发布的 .net4 版本不太兼容。 (mosesofegypt.net/post/…) 看来我需要返回 ObjectSet 而不是 ObjectQuery 的函数---- WAS: private ObjectQuery ApplyDataLoadOptions(string queryString) NOW: private ObjectSet ApplyDataLoadOptions(string queryString )
    • 一个非常有趣的方法。但是,ObjectQuery&lt;T&gt; 实现了IQueryable&lt;T&gt;。当你做.Include,你返回一个ObjectQuery&lt;T&gt;时,没有办法把它带回ObjectSet&lt;T&gt;(也许如果你做了一个明确的演员,不确定)。他的方法行不通吗?
    【解决方案2】:

    不确定这是否能回答您的问题...但是,这是我完成“动态”急切加载的方式:

    /// <summary>
        /// Flags to indicate which child entities to eager load
        /// </summary>
        [Flags]
        public enum IncludeFlags
        {
            None = 1,
            All = 1 << 1,
            ChildEntity1 = 1 << 2,
            ChildEntity2 = 1 << 3,
            ChildEntity3 = 1 << 4
        }
    
        /// <summary>
        /// Method to create my object query
        /// </summary>
        /// <param name="context">Database context</param>
        /// <param name="includeFlags">Indicates which flags to Include</param>
        /// <returns></returns>
        private static ObjectQuery<MyEntity> GetContext(DataBaseContext context, IncludeFlags includeFlags)
        {
            ObjectQuery<MyEntity> query = new ObjectQuery<MyEntity>("MyEntity", context);
    
            if ((includeFlags & IncludeFlags.None) != IncludeFlags.None)
            {
                bool getAll = ((includeFlags & IncludeFlags.All) == IncludeFlags.All);
                if (getAll || ((includeFlags & IncludeFlags.ChildEntity1) == IncludeFlags.ChildEntity1))
                    query = query.Include("ChildEntity1");
    
                if (getAll || ((includeFlags & IncludeFlags.ChildEntity2) == IncludeFlags.ChildEntity2))
                    query = query.Include("ChildEntity2");
    
                if (getAll || ((includeFlags & IncludeFlags.ChildEntity3) == IncludeFlags.ChildEntity3))
                    query = query.Include("ChildEntity2.ChildEntity3");
            }
    
            return query;
        }
    
        public static MyEntity[] GetMyEntities(IncludeFlags flags = IncludeFlags.None)
        {
            DataBaseContext db = new DataBaseContext();
            from e in GetContext(db, flags)
            select e;
    
            return e.ToArray();
        }
    
        public void GetMyEntities()
        {
            MyEntity[] entities = Test.GetMyEntities(); // Does not load any child entities
            MyEntity[] entities2 = Test.GetMyEntities(IncludeFlags.All); // Loads them all
            MyEntity[] entities3 = Test.GetMyEntities(IncludeFlags.ChildEntity1 | IncludeFlags.ChildEntity2); // Only loads child 1 and 2
            MyEntity[] entities4 = Test.GetMyEntities(IncludeFlags.ChildEntity3); // Only loads ChildEntity2.ChildEntity3
        }
    

    希望这会有所帮助。

    【讨论】:

    • 我们的项目中有很多用户包含 linq-to-EF 查询,这些查询接受以前创建的 DatabaseContext。我希望找到一种机制,允许我通过添加 DataLoadOptions 扩展来继承我的 DatabaseContext;同时不影响其他人使用相同的代码。所以这是一个很好的解决方案,但不适合我的场景。我应该在前面更清楚地说明这一点。谢谢。 (顺便说一句,我会为你的帖子投票,但我是新手,还没有足够的积分)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多