【问题标题】:In EFCore 3.0 - how to join related read only Keyless entities from views so that related entities are loaded在 EF Core 3.0 中 - 如何从视图中加入相关的只读 Keyless 实体,以便加载相关实体
【发布时间】:2019-11-06 14:16:13
【问题描述】:

使用 .NET EFCore 3.0 - 查询类型已贬值,现在我们在 Entity Framework Core 3.0 中移至 "Keyless entity type"

我的要求是使用新的HasNoKey() 语法将一些只读视图从 MS SQL 数据库映射到 DbContext。

  1. 返回的只读实体必须加载其相关的读取 仅限实体。
  2. 有没有办法让视图相互连接并自动加载相关的 实体?
  3. 也许还有另一种使用视图和只读实体的方法 除了 HasNoKey?

简单示例架构,Order 有许多 OrderItem。如果这两个 Entities 都来自一个视图,那么 Order 如何加载它的 OrderItems?

public class ReadonlyActionOnDb
{
    OrdersDbContext Db; //need to pass in via constructor etc, just for demo code.
    protected void PrintOrderItems()
    {
        var custItems = Db.vOrders.Where(i=> i.CustomerId == 10).SelectMany(i=> i.OrderItems);
        foreach (OrderItemDto i in custItems ) Console.WriteLine(i.ProductName); 
    }           
}

//part of the config shown...
public partial class OrdersDbContext: DbContext
{
    public DbSet<OrderDto> vOrders { get; set; }
    public DbSet<OrderDto> vOrderItems { get; set; }

    protected void OnModelCreating(ModelBuilder modelBuilder)
    {           
        modelBuilder.Entity<OrderItemDto>().HasNoKey().ToView("vOrderItems ","dbo");

        //how do we automatically load the OrderItems into this?
        modelBuilder.Entity<OrderDto>().HasNoKey().ToView("vOrders","dbo");
    }
}

public class OrderDto
{
    public int Id { get; set; }
    public int CustomerId { get; set; }
    public ICollection<OrderItemDto> OrderItems { get; set; }
}

public class OrderItemDto
{
    public int Id { get; set; }
    public string ProductName { get; set; }
}

我觉得这应该在 DbContext 实例上实现,我知道我可以稍后手动加载和加入。

对于这个问题,迁移并不重要,因为 DBA 会强制执行他们自己对数据库的更新。

以下是在MS Documentation 中找到的无密钥实体类型的限制。没有示例的实体导航有点混乱。

无密钥实体类型特征

无键实体类型支持许多与 常规实体类型,如继承映射和导航 特性。在关系存储上,他们可以配置目标 通过流畅的 API 方法或数据的数据库对象和列 注释。

但是,它们与常规实体类型的不同之处在于:

无法定义键。

永远不会跟踪 DbContext 中的更改,因此永远不会 在数据库中插入、更新或删除。

按照惯例从未被发现。

仅支持导航映射功能的子集, 具体来说:

他们可能永远不会成为一段关系的主要目的。

他们可能没有导航到拥有的实体

它们只能包含指向的参考导航属性 常规实体。

实体不能包含无键实体类型的导航属性。

需要配置.HasNoKey()方法调用。

可以映射到定义查询。定义查询是查询 在充当无密钥实体的数据源的模型中声明 输入。

【问题讨论】:

    标签: ef-core-3.0


    【解决方案1】:

    您不能查询订单然后包含订单项目。但是您可以查询所有订单商品,如果父订单符合您的条件,然后包含父记录。

    我最近发现了一种设计模式,您的“无键实体”只包含其他数据库实体的主键。每个都有定义的导航属性。

    public class OrderDetailKeys{
        public int OrderId { get; set; }
        public int OrderDetailId { get; set; }
        public virtual Order Order { get; set; }
        public virtual OrderDetail OrderDetail { get; set; }
    }
    
    modelBuilder.Entity<OrderDetailKeys>(entity => {
        entity.HasNoKey();
        entity.ToView(null); // or real view name
        entity.HasOne(e => e.Order)
            .WithOne()
            .HasForeignKey<OrderDetailKeys>(e => e.OrderId);
        entity.HasOne(e => e.OrderDetail)
            .WithOne()
            .HasForeignKey<OrderDetailKeys>(e => e.OrderDetailId);
    }
    

    通过这种方式,您可以使用数据库视图或FromRawSql 来定义可重用结果集中哪些记录可用。

        public static IQuerable<OrderDetailKeys> ComplexQuery(DbContext context) => 
            context.Set<OrderDetailKeys>()
                .FromRawSql("select OrderId, Id as OrderDetailId from OrderDetail where <complex sql condition here>");
    

    使用此结果集的每个地方都可以使用Where 条件进一步限制结果。然后IncludeSelect 只列出他们需要的列。

        var details = ComplexQuery(context)
            .Where(k => k.Order.CustomerId = ...)
            .Include(k => k.OrderDetail)
            .ToListAsync();
    

    EF 将使用您的视图或原始 sql 作为 from 子句中的子查询。然后围绕该连接构建到其他表。

    您需要的、无法说服 EF 生成的丑陋 SQL 被封装在一个位置。而且您最终不会为每个视图重复真实表的列架构。

    【讨论】:

    • 谢谢杰里米。我不清楚该模式,请您分享或提供一个示例吗?
    • 另外,为了回答您的实际问题,查询类型不能有子记录。但它可以加入具有子记录的父记录。例如查询 => 订单 => 订单项。
    【解决方案2】:

    根据以下情况,这是无法做到的,因为 Orders 将是您的委托人:

    • 仅支持导航映射功能的子集,具体而言:
      • 他们可能永远不会充当关系的主要目的。

    【讨论】:

      猜你喜欢
      • 2016-08-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-09-04
      相关资源
      最近更新 更多