简体   繁体   English

按实体类型的 EF 条件包含

[英]EF Conditional Include by Entity Type

Please, suppose that architecture:请假设该架构:

public class Mammal
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class Dog : Mammal
{
    public int TailId { get; set; }
    public Tail Tail { get; set; }
}

public class Bat : Mammal
{
    public int WingId { get; set; }
    public Wing Wing { get; set; }
}

public class Buffalo : Mammal
{
    public virtual ICollection<Horn> Horns { get; set; }
}

public class Tail
{
    public int Id { get; set; }
    ...
}

public class Wing
{
    public int Id { get; set; }
    ...
}

public class Horn
{
    public int Id { get; set; }
    ...
}

Now, my context:现在,我的上下文:

public class MyContext : DbContext
{
    public DbSet<Mammal> Mammals { get; set; }
}

So, I want to make ONLY ONE sql query, and include (and load) all nested entities, something like:因此,我只想进行一个 sql 查询,并包含(并加载)所有嵌套实体,例如:

var query = myContext.Mammals
    .IncludeIfTypeIs<Dog>(d => d.Tail)
    .IncludeIfTypeIs<Bat>(b => b.Wing)
    .IncludeIfTypeIs<Buffalo>(b => b.Horns)
    ...
    ...
;

I know that I can do that separately, but I don't want because I have many entities, and I need to minimize database requests.我知道我可以单独做,但我不想要,因为我有很多实体,我需要尽量减少数据库请求。

I don't want use lazy loading because this will make many database requests also.我不想使用延迟加载,因为这也会产生许多数据库请求。

How to achieve that?如何做到这一点?

EF Core supports this in version 2.1 and higher. EF Core 在 2.1 及更高版本中支持此功能。 See the Github Issue here此处查看 Github 问题

var query = myContext.Mammals
    .Include(d => (d as Dog).Tail)
    .Include(b => (b as Bat).Wing)
    .Include(b => (b as Buffalo).Horns)

This will include all the properties in one query.这将包括一个查询中的所有属性。

Here is a link to the official documentation on this.这是官方文档的链接。

Can you try something like this:你可以尝试这样的事情:

public static class Extensions
{
    public static IQueryable<Mammal> IncludeExtraEntities<Mammal,T>(this IQueryable<Mammal> query, T derivedType) where T :Mammal
    {
        if (derivedType is Dog)
            return query.Include("Tail");
        if (derivedType is Bat)
            return query.Include("Wing");
        return query;
    }
}

Then in your db call:然后在你的数据库调用中:

var query = myContext.Mammals.IncludeExtraEntities(typeof(Dog));

Maybe this will work.也许这会奏效。

You could create a method to include a list of expressions.您可以创建一个方法来包含表达式列表。 (or perhaps an extension method). (或者可能是一种扩展方法)。

public static IQueryable<Mammal> GetMammals(params Expression<Func<T, Object>>[] includeExps)
{
     var query = context.Mammals.AsQueryable();
     if (includeExps != null)
        query = includeExps.Aggregate(query, (current, exp) => current.Include(exp));

     return query;

}

And then, in your code:然后,在您的代码中:

//Bat and Dog will be included here
var mammals = GetMammals(i => i.Bat, i => i.Dog);

Hope it helps!希望能帮助到你!

The question is, how would you load properties of derived types, using only a DbSet of the base class and without lazy loading?问题是,如何加载派生类型的属性,只使用基类的 DbSet 而没有延迟加载? I am afraid this is not possible.恐怕这是不可能的。

You can however do this:但是,您可以这样做:

public class MyContext : DbContext
{
    public DbSet<Mammal> Mammals { get; set; }
    public DbSet<Dog>    Dogs    { get; set; }
    public DbSet<Bat>    Bats    { get; set; }
}

You could also simply implement one method, type-specific for each of your types您也可以简单地实现一种方法,针对您的每种类型

public static class Extensions
{
    public static IQueryable<Dog> IncludeExtraEntities<Dog>(this IQueryable<Dog> query) where Dog : Mammal
    {
            return query.Include("Tail");
    }

    public static IQueryable<Bat> IncludeExtraEntities<Bat>(this IQueryable<Bat> query) where Bat: Mammal
    {
            return query.Include("Wing");
    }
}

Then, you can simply call:然后,您可以简单地调用:

myContext.Dogs.IncludeExtraEntities();

And the method depending on your type will be called.并且将调用取决于您的类型的方法。

You're expecting too much from Include .您对Include期望过高。 The lambda expression you enter in Include is merely a string provider that looks far too intelligent.您在Include输入的 lambda 表达式只是一个看起来过于智能的字符串提供程序。 Under the hood its member expression is dissected to get the name of the property, that's all.在幕后,它的成员表达式被剖析以获取属性的名称,仅此而已。 The name is entered into the Include method that accepts a string parameter.该名称被输入到接受字符串参数的Include方法中。 That method does the actual work.该方法执行实际工作。

The lambda expression has to point to a navigation property on the type in the IQueryable . lambda 表达式必须指向IQueryable类型的导航属性。 You can't do ...你不能做...

myContext.Mammals.Include(d => d.Tail)

... because Tail is not a property of the base type. ...因为Tail不是基本类型的属性。 You can only do ...你只能做...

myContext.Mammals.OfType<Dog>().Include(d => d.Tail)

The best you can get is something like你能得到的最好的东西是

from m in context.Mammals
let tail = (m as Dog).Tail
let wing = (m as Bat).Wing
let horns= (m as Buffalo).Horns
select new { Mammal = m, tail, wing, horns }

Since everything is translated into SQL you don't have to worry about null reference exceptions.由于所有内容都已转换为 SQL,因此您不必担心空引用异常。

i faced problem like that and here how i solved it:我遇到了这样的问题,在这里我是如何解决它的:

 public IEnumerable<OrderLine> GetAllOrderLinesData()
    {
        var _StockOrderLines = appContext.OrderLines.OfType<StockOrderLine>()
            .Include(p => p.Order).Include(p => p.Product);

        var _AnnualOrderLines = appContext.OrderLines.OfType<AnnualOrderLine>()
            .Include(p => p.Order).Include(p => p.Customer);

        return _StockOrderLines.ToList<OrderLine>().Union<OrderLine>(_AnnualOrderLines.ToList<OrderLine>());
    }

you can change classes as yours :)你可以改变你的课程:)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM