.Net4 中的预加载链接至 EF;也许像 DataLoadOptions

发布于 2024-10-03 20:29:40 字数 2414 浏览 0 评论 0 原文

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

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

我试图提供一个类型安全的急切加载方案,如 MosesOfEgypt 博客。我修改了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 功能的帮助将不胜感激。

I am using VS2010 .Net4 Linq-to-EntityFramework and would like to explicitly eager load some child data. I would like to provide functionality similar to that of DataLoadOptions or LoadWith which are available for Linq-to-SQL IIUC, but are not available to Linq-to-EF.

(As an aside, this is so that I can record data to be played back later during testing. We have used lazy loading and I need to find these occurrences and replace with eager loading. The DataLoadOptions method would allow a clean way to implement this.)

I am trying to provide a typesafe eager loading scheme as described at MosesOfEgypt blog. I have modified the generation of T4 and have what I think is the last problem. In .Net4, the entity properties return ObjectSet. But unfortunately, the Include function returns ObjectQuery which is the base class for ObjectSet.

Here is a subset of the ObjectContext class as generated from modified T4 template:

    #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

The line starting with "*" is where the cast from ObjectQuery to ObjectSet occurs. ANd this is an invalid upcast so if explicitly cast at designtime, will fail at runtime, unless I'm doing it wrong.

One solution might be to write an extension method for ObjectSet.Include so that it returns ObjectSet instead of ObjectQuery. I would like to know how to find the source code of the ObjectQuery.Include function, if that is possible. And I'm not sure if these solution would work.

Also wonder if there was a way to upcast the results of the Include function from ObjectQuery to ObjectSet. Again, not sure if this would work.

Any help on implementing a DataLoadOptions capability for Linq-to-EF in .Net4 would be appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

请帮我爱他 2024-10-10 20:29:40

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

.Include 返回一个 ObjectQuery,它实现 IQueryable。为什么要将其转换为 ObjectSet

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

我们为每个实体创建枚举(可能有点过头了,但我讨厌魔术字符串),其中包含每个关联。我们的存储库接受这些枚举的数组。然后,我们在枚举上使用扩展方法来转换为 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() 是枚举上的另一个扩展方法,它仅返回匹配的导航属性。

It's not really clear what your asking here.

.Include returns an ObjectQuery<T>, which implements IQueryable<T>. Why do you want to cast it as ObjectSet<T>??

WRT "strongly-typed" Include's, the short answer is you can't. But you can make it sweeter with the syntactic sugary of extension methods.

We create enumerations for each entity (may be overkill, but i hate magic strings), which contain each association. Our Repository accepts an array of these enumerations. We then use an extension method on the enumeration to convert to an Include.

Example (simplified) Repository code:

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

And the extension:

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() is another extension method on the enumeration, which simply returns the matching Navigational property.

椒妓 2024-10-10 20:29:40

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

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

希望这会有所帮助。

Not sure if this answers your question... however, here is how I have done "dynamic" eager loading:

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

Hope this helps.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文