将通用的紧急加载方法从EF6转换为EF Core [英] Translating generic eager load method from EF6 to EF Core

查看:193
本文介绍了将通用的紧急加载方法从EF6转换为EF Core的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于EF6,我在通用存储库中有一个方法可以暴露给所有服务层,以便根据需要从数据库中检索具有任何嵌套属性的实体:

For EF6, I had a method in my generic repository that I exposed to all service layers in order to retrieve entities from the database with any nested properties as needed:

public IQueryable<T> OldMethod(params Expression<Func<T, object>>[] includeProperties)
{
    var queryable = set.AsQueryable();

    return includeProperties.Aggregate(queryable, (current, includeProperty) => current.Include(includeProperty));
}

这样,我可以通过以下方式使用该方法:

This way, I could use the method in the following way:

var data = repo.OldMethod(x => x.Papers, => x.People.Select(y => y.Addresses)).ToList();

在EF6中,这将加载 Paper 导航属性,每个人的导航属性和地址导航属性。正如预期的那样,这会在EFCore中引发异常。由于切换到EFCore中的Include-> ThenInclude方法,我不太确定如何在我的服务层轻松地复制它,而我又不需要任何有关EntityFramework的信息。

In EF6, this would load the Papers navigation property, the People navigation property, and the Addresses navigation property on each person. This, as expected, throws an exception in EFCore. Because of the switch to Include-->ThenInclude method in EFCore, I'm not quite sure how to easily replicate this at my service layer which I'd like to not require any information about EntityFramework.

推荐答案

自EF Core最初发布以来,已被多次询问。早期版本的EF Core甚至都支持它,但是后来它从EF Core代码中删除了(我想为了推广新的 Include / ThenInclude 模式)。

This has been asked many times since the initial release of EF Core. Earlier prerelease versions of EF Core even were supporting it, but then it has been removed from EF Core code (I guess in order to promote the new Include / ThenInclude pattern).

Include / ThenInclude 模式看起来更加清晰(除了当前的Intellisense问题),它还有一个主要缺点-需要访问 EntityFrameworkQueryableExtensions ,因此引用了 Microsoft.EntityFrameworkCore 程序集。虽然 params Expression>`模式没有这样的要求。

While Include / ThenInclude pattern looks more clear (besides the current Intellisense issues), it has one major drawback - requires access to EntityFrameworkQueryableExtensions, thus reference to Microsoft.EntityFrameworkCore assembly. While paramsExpression>` pattern has no such requirement.

好处是,可以相对容易地添加一个功能。 EF6源代码可在GitHub上公开获得,从那里我们可以看到它使用了称为 TryParsePath 构建点分隔的字符串路径,然后将该路径传递到 string 重载> Include 方法。

The good thing is the one can relatively easily add that functionality. The EF6 source code is publicly available on GitHub, and from there we can see that it uses a method called TryParsePath to build dot separated string path which then is passed to the string overload of Include method.

同样可以在EF Core中应用。我们可能可以使用EF6代码,但我将提供自己的版本。可以很容易地看出,受支持的构造是成员访问器或对带有两个参数的名为 Select 的方法的调用,第二个是 LambdaExpression

The same can be applied in EF Core. We can probably use the EF6 code, but I'm going to provide my own version. It can be easily be seen that the supported constructs are member accessors or calls to method called Select with 2 arguments, the second being LambdaExpression.

以下是我对上述内容的解释,封装在两个自定义扩展方法中:

Following is my interpretation of the above, encapsulated in two custom extension methods:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace Microsoft.EntityFrameworkCore
{
    public static class IncludeExtensions
    {
        public static IQueryable<T> Include<T>(this IQueryable<T> source, IEnumerable<string> includePaths) where T : class
            => includePaths.Aggregate(source, (query, path) => query.Include(path));

        public static IQueryable<T> Include<T>(this IQueryable<T> source, IEnumerable<Expression<Func<T, object>>> includePaths) where T : class
            => source.Include(includePaths.Select(e => GetIncludePath(e?.Body)));

        static string GetIncludePath(Expression source, bool allowParameter = false)
        {
            if (allowParameter && source is ParameterExpression)
                return null; // ok
            if (source is MemberExpression member)
                return CombinePaths(GetIncludePath(member.Expression, true), member.Member.Name);
            if (source is MethodCallExpression call && call.Method.Name == "Select"
                && call.Arguments.Count == 2 && call.Arguments[1] is LambdaExpression selector)
                return CombinePaths(GetIncludePath(call.Arguments[0]), GetIncludePath(selector.Body));
            throw new Exception("Invalid Include path.");
        }

        static string CombinePaths(string path1, string path2)
            => path1 != null ? path1 + "." + path2 : path2;
    }
}

第一个只是调用多个<$ c的助手$ c> string 包括(摘自我对实体框架核心2.0.1急于在所有嵌套的相关实体上加载)。第二个是有问题的方法,该方法将表达式转换为字符串并调用第一个。主要工作由 GetIncludePath 私有方法完成,该私有方法基于上述规则以及一条附加规则递归处理表达式-自下而上导航时,应以lambda参数结尾。

The first is simply helper for calling multiple string includes (taken from my answer to Entity Framework Core 2.0.1 Eager Loading on all nested related entities). The second is the method in question, which converts the expressions to strings and call the first. The main work is done by GetIncludePath private method which recursively processes the expression based on the aforementioned rules, plus one additional rule - when navigating bottom up, it should end with lambda parameter.

现在该方法的实现很简单,因为问题很简单:

Now the implementation of the method is question is simple as that:

public IQueryable<T> OldMethod(params Expression<Func<T, object>>[] includeProperties)
    => set.Include(includeProperties);

这篇关于将通用的紧急加载方法从EF6转换为EF Core的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆