实体框架核心3-无法使查询正常工作

时间:2019-12-09 19:59:09

标签: sql-server entity-framework entity-framework-core

我正在尝试使用EF Core 3.1在多个表之间构建查询,但这根本无法正常工作。

我将尝试用一些虚拟的例子来解释。

假设我的SQL表在SQL Server数据库中都有以下定义:

  1. 主键(如果需要,可以组合)
  2. 外键

虚拟实体如下:

  • 小镇
  • 居民
  • 居民汽车
  • 汽车
  • 制造商
  • 机械师

外键关系:

  • 居民->城镇
  • InhabitantCar->居民和-> Car
  • 汽车->制造商和->机械师

所有实体都设置了其导航属性,并且我已经在数据库上下文中设置了主键和外键。

这些表中的绝大多数都有一个“已启用”位字段,因此可以禁用行而不删除它们

所以我要编写的查询类似于以下内容:

var data = await context.Town.AsNoTracking()
    .Where(t => t.TownName == request.TownName)
    .Include(t => t.Inhabitants.Where(i => i.Name == request.InhabitantName && i.Enabled)
    .ThenInclude(i => i.InhabitantCar.Where(ic => ic.Enabled))
    .ThenInclude(ic => ic.Cars.Where(c => c.Enabled))
    .ThenInclude(c => c.Manufacturer.Where(m => m.Enabled))

    .Include(t => t.Inhabitants.Where(i => i.Name == request.InhabitantName && i.Enabled)
    .ThenInclude(i => i.InhabitantCar.Where(ic => ic.Enabled))
    .ThenInclude(ic => ic.Cars.Where(c => c.Enabled))
    .ThenInclude(c => c.Mechanic.Where(m => m.Enabled && m.Name == request.AllowedMechanic))

    .ToListAsync().ConfigureAwait(false);

总而言之,我想知道居住在“伦敦”的“约翰·史密斯”所驾驶的是哪些汽车,这些汽车由“ MechanicsAreUs”提供服务。

对我来说这似乎很漫长,这也许就是我的问题所在。

无论如何,后面的ThenIncludes中的许多.WHERE子句将无法编译。一步一步地删除它们,直到编译完成为止。

var data = await context.Town.AsNoTracking()
    .Where(t => t.TownName == request.TownName)
    .Include(t => t.Inhabitants.Where(i => i.Name == request.InhabitantName && i.Enabled)
    .ThenInclude(i => i.InhabitantCar.Where(ic => ic.Enabled))
    .ThenInclude(ic => ic.Cars)
    .ThenInclude(c => c.Manufacturer)

    .Include(t => t.Inhabitants.Where(i => i.Name == request.InhabitantName && i.Enabled)
    .ThenInclude(i => i.InhabitantCar.Where(ic => ic.Enabled))
    .ThenInclude(ic => ic.Cars)
    .ThenInclude(c => c.Mechanic)

    .ToListAsync().ConfigureAwait(false);

因此,按照书面规定,它将带回禁用的条目,而我没有指定机制。但是,当我运行它时,出现异常:

  

System.InvalidOperationException:内部使用Lambda表达式   包含无效。

我花了很多时间浏览各种Microsoft示例,但是我没有找到任何看起来如此复杂的示例。这只是少数的内部联接。在几分钟内就可以在存储过程中完成某些事情。只是我想使用实体框架来做到这一点。

3 个答案:

答案 0 :(得分:2)

您无法过滤.Include(...)急切的负载-全部或全部都没有。正如大卫·布朗(David Browne)在对问题的评论中指出的那样,如果要基于记录的Enabled标志排除记录,则应使用query filters,例如:

modelBuilder.Entity<Car>()
    .HasQueryFilter(c => c.Enabled);

我似乎您对Car实体感兴趣,因此让我们重组查询以使焦点集中:

var query = context.Cars;

您想要与具有特定名称的Inhabitant关联的汽车,该名称与特定的Town关联,但也由特定的Mechanic提供服务,因此让我们根据该条件进行过滤:< / p>

query = query.Where( c => 
    c.InhabitantCar.Inhabitant.Name == request.InhabitantName
    && c.InhabitantCar.Inhabitant.Town.TownName == request.TownName
    && c.Mechanic == request.AllowedMechanic );

此查询现在将返回您想要的Car实体,所以现在让我们配置急切的负载:

query = query.Include( c => c.Manufacturer )
    .Include( c => c.Mechanic )
    .Include( c => c.InhabitantCar )
        .ThenInclude( ic => ic.Inhabitant )
            .ThenInclude( i => i.Town );

给个机会。

答案 1 :(得分:0)

实体反映数据状态。您无法过滤所需的相关数据,是全部还是全部。一个城镇不仅有“被启用”的居民,它还具有居民,其中一些人被启用,而其他人则没有。您不想查看禁用或不相关的视图,这是视图的关注点,而不是数据模型。

您可以使用Select来填充适合您的视图的模型结构。这可以使连接表变平,并仅加载要查看的已启用记录,还可以简化视图所需的字段,而不是公开有关域的所有内容。您可以利用AutoMapper来填充视图模型/ w ProjectTo

答案 2 :(得分:0)

一个建议是使用查询过滤器。

这背后的想法很棒-在我的数据库上下文文件中,我可以添加一组通用的过滤器,例如

builder.Entity<Town>()
    .HasQueryFilter(a => a.Enabled);
builder.Entity<Car>()
    .HasQueryFilter(a => a.Enabled);
builder.Entity<Manufacturer>()
    .HasQueryFilter(a => a.Enabled);

这将包含在我的Service文件生成的每个查询中-开发人员无需关心。

但是,当我分析生成的SQL时,我发现我的代码到处都是多个子查询,例如

Inner Join (Select...Where ...Enabled = 1)

在删除这些集中式查询过滤器并将其添加到LINQ语句的WHERE子句中后,查询效率将大大提高。