渴望加载.Net4 Link-to-EF;也许像DataLoadOptions

时间:2010-11-23 17:55:38

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

我正在使用VS2010 .Net4 Linq-to-EntityFramework,并希望明确地急切加载一些子数据。我想提供类似于 DataLoadOptions LoadWith 的功能,这些功能可用于Linq-to-SQL IIUC,但不适用于Linq-to-EF。< / p>

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

我正在努力提供MosesOfEgypt blog所述的类型安全的预先加载方案。我修改了T4的产生,并且我认为这是最后一个问题。在.Net4中,实体属性返回 ObjectSet 。但不幸的是,包含函数返回 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

以“ * ”开头的行是从 ObjectQuery 转换为 ObjectSet 的地方。这是一个无效的upcast,所以如果在设计时显式转换,将在运行时失败,除非我做错了。

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

另外想知道是否有办法将包含功能的结果从 ObjectQuery 转发到 ObjectSet 。再次,不确定这是否有效。

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

2 个答案:

答案 0 :(得分:1)

你在这里询问的并不是很清楚。

.Include会返回ObjectQuery<T>,其中会生成IQueryable<T>。为什么要将其强制转换为ObjectSet<T> ??

WRT“强类型”Include,简短的回答是你不能。但是你可以用扩展方法的语法含糖来使它更甜蜜。

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

答案 1 :(得分:0)

不确定这是否能解答您的问题...但是,这就是我如何做出“动态”的热切加载:

/// <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
    }

希望这有帮助。