EF Core中的多个Includes() [英] Multiple Includes() in EF Core

查看:135
本文介绍了EF Core中的多个Includes()的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个扩展方法,可让您在EF中普遍包含数据:

I have an extension method that lets you generically include data in EF:

public static IQueryable<T> IncludeMultiple<T>(this IQueryable<T> query, params Expression<Func<T, object>>[] includes)
    where T : class
{
    if (includes != null)
    {
        query = includes.Aggregate(query, (current, include) => current.Include(include));
    }
    return query;
}

这使我可以在存储库中使用如下方法:

This allows me to have methods in my repository like this:

public Patient GetById(int id, params Expression<Func<Patient, object>>[] includes)
{
    return context.Patients
        .IncludeMultiple(includes)
        .FirstOrDefault(x => x.PatientId == id);
}

我相信扩展方法在EF Core之前有效,但现在包括儿童这样做是这样的:

I believe the extension method worked before EF Core, but now including "children" is done like this:

var blogs = context.Blogs
    .Include(blog => blog.Posts)
        .ThenInclude(post => post.Author);

是否可以更改我的通用扩展方法以支持EF Core的新 ThenInclude()练习?

Is there a way to alter my generic extension method to support EF Core's new ThenInclude() practice?

推荐答案

评论,您可以进行 EF6代码可以解析您的表达式并应用相关的 Include / ThenInclude 调用。毕竟看起来并不难,但是由于这不是我的主意,所以我不想在代码中给出答案。

As said in comments by other, you can take EF6 code to parse your expressions and apply the relevant Include/ThenInclude calls. It does not look that hard after all, but as this was not my idea, I would rather not put an answer with the code for it.

您可以改为更改您的代码公开某些接口的模式,允许您从调用方指定您的包含,而不允许其访问基础的可查询对象。

You may instead change your pattern for exposing some interface allowing you to specify your includes from the caller without letting it accessing the underlying queryable.

这将导致以下情况:

using YourProject.ExtensionNamespace;

// ...

patientRepository.GetById(0, ip => ip
    .Include(p => p.Addresses)
    .ThenInclude(a=> a.Country));

名称空间中的 using 必须与名称空间匹配包含最后一个代码块中定义的扩展方法的名称。

The using on namespace must match the namespace name containing the extension methods defined in the last code block.

GetById 现在将是:

public static Patient GetById(int id,
    Func<IIncludable<Patient>, IIncludable> includes)
{
    return context.Patients
        .IncludeMultiple(includes)
        .FirstOrDefault(x => x.EndDayID == id);
}

扩展方法 IncludeMultiple

public static IQueryable<T> IncludeMultiple<T>(this IQueryable<T> query,
    Func<IIncludable<T>, IIncludable> includes)
    where T : class
{
    if (includes == null)
        return query;

    var includable = (Includable<T>)includes(new Includable<T>(query));
    return includable.Input;
}

包容性的类和放大器;接口,它们是简单的占位符,其上的附加扩展方法将模仿EF Include ThenInclude 方法:

Includable classes & interfaces, which are simple "placeholders" on which additional extensions methods will do the work of mimicking EF Include and ThenInclude methods:

public interface IIncludable { }

public interface IIncludable<out TEntity> : IIncludable { }

public interface IIncludable<out TEntity, out TProperty> : IIncludable<TEntity> { }

internal class Includable<TEntity> : IIncludable<TEntity> where TEntity : class
{
    internal IQueryable<TEntity> Input { get; }

    internal Includable(IQueryable<TEntity> queryable)
    {
        // C# 7 syntax, just rewrite it "old style" if you do not have Visual Studio 2017
        Input = queryable ?? throw new ArgumentNullException(nameof(queryable));
    }
}

internal class Includable<TEntity, TProperty> :
    Includable<TEntity>, IIncludable<TEntity, TProperty>
    where TEntity : class
{
    internal IIncludableQueryable<TEntity, TProperty> IncludableInput { get; }

    internal Includable(IIncludableQueryable<TEntity, TProperty> queryable) :
        base(queryable)
    {
        IncludableInput = queryable;
    }
}

不可容忍扩展方法:

using Microsoft.EntityFrameworkCore;

// others using ommitted

namespace YourProject.ExtensionNamespace
{
    public static class IncludableExtensions
    {
        public static IIncludable<TEntity, TProperty> Include<TEntity, TProperty>(
            this IIncludable<TEntity> includes,
            Expression<Func<TEntity, TProperty>> propertySelector)
            where TEntity : class
        {
            var result = ((Includable<TEntity>)includes).Input
                .Include(propertySelector);
            return new Includable<TEntity, TProperty>(result);
        }

        public static IIncludable<TEntity, TOtherProperty>
            ThenInclude<TEntity, TOtherProperty, TProperty>(
                this IIncludable<TEntity, TProperty> includes,
                Expression<Func<TProperty, TOtherProperty>> propertySelector)
            where TEntity : class
        {
            var result = ((Includable<TEntity, TProperty>)includes)
                .IncludableInput.ThenInclude(propertySelector);
            return new Includable<TEntity, TOtherProperty>(result);
        }

        public static IIncludable<TEntity, TOtherProperty>
            ThenInclude<TEntity, TOtherProperty, TProperty>(
                this IIncludable<TEntity, IEnumerable<TProperty>> includes,
                Expression<Func<TProperty, TOtherProperty>> propertySelector)
            where TEntity : class
        {
            var result = ((Includable<TEntity, IEnumerable<TProperty>>)includes)
                .IncludableInput.ThenInclude(propertySelector);
            return new Includable<TEntity, TOtherProperty>(result);
        }
    }
}

IIncludable< TEntity,TProperty> 几乎与EF中的 IIncludableQueryable< TEntity,TProperty> 类似,但它不会扩展 IQueryable ,并且不允许重塑查询。

IIncludable<TEntity, TProperty> is almost like IIncludableQueryable<TEntity, TProperty> from EF, but it does not extend IQueryable and does not allow reshaping the query.

当然,如果调用者在同一程序集中,它仍然可以强制转换 IIncludable Includable 并开始摆弄可查询对象。但是好吧,如果有人想弄错了,我们没有办法阻止他这样做(反射允许任何事情)。重要的是裸露的合约。

Of course if the caller is in the same assembly, it can still cast the IIncludable to Includable and start fiddling with the queryable. But well, if someone wants to get it wrong, there is no way we would prevent him doing so (reflection allows anything). What does matter is the exposed contract.

现在,如果您不关心将 IQueryable 暴露给调用者(我怀疑),显然只需将您的 params 参数更改为 Func< Queryable< T>,Queryable< T>> addIncludes 参数,并避免对上面的所有代码进行编码。

Now if you do not care about exposing IQueryable to the caller (which I doubt), obviously just change your params argument for a Func<Queryable<T>, Queryable<T>> addIncludes argument, and avoid coding all those things above.

最后的最佳选择:我没有测试过,我不使用当前是实体框架!

And the best for the end: I have not tested this, I do not use Entity Framework currently!

这篇关于EF Core中的多个Includes()的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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