【问题标题】:De-normalizing data into a code first Entity Framework entity, without a SQL View将数据反规范化为代码优先的实体框架实体,无需 SQL 视图
【发布时间】:2019-04-02 19:36:25
【问题描述】:

首先,这是一个简单的示例数据库模型,它已将Products 分配给Categories,其中Products 中的CategoryId 是与Categories 的FK 关系。

产品

  • ProductId (PK),INT
  • 产品名称 VARCHAR(255)
  • CategoryId (FK),INT

类别

  • CategoryId (PK),INT
  • 类别名称 VARCHAR(255)

对于 .NET 应用程序数据模型,只有 Product 的非规范化表示被定义为实体类:

public class Product
{
    public int ProductId { get; set; }
    public string ProductName { get; set; }
    public int CategoryId { get; set; }
    public string CategoryName { get; set; }
}

没有定义Category 类,对于这个例子,没有计划。

在代码优先的实体框架DbContext-派生类中,我设置了DbSet<Product> Products 实体集:

    public virtual DbSet<Product> Products { get; set; }

EntityTypeConfiguration 中,我正在尝试将其连接起来,但我无法使其正常工作:

public class ProductConfiguration : EntityTypeConfiguration<Product>
{
    public ProductConfiguration()
    {
        HasKey(t => t.ProductId);

        // How do I instruct EF to pull just the column 'CategoryName'
        // from the FK-related Categories table?
    }
}

我意识到可以创建一个 SQL 视图,然后我可以告诉 EF 使用 ToTable("App1ProductsView") 映射到该视图,但在本例中,我想避免这样做。

在 SQL ADO.NET ORM 解决方案中,这里没有问题。我可以简单地编写自己的 SQL 语句来执行 INNER JOIN Categories c ON c.CategoryId = p.CategoryId 连接。在填充实体时,如何使用 EF 代码优先 Fluent API 执行相同的内部联接?

在我的研究中,我看到了很多“实体拆分为多个表”的主题,但事实并非如此。类别和产品是两个不同的实体(从数据库的角度来看),但 .NET 代码意味着不知道这一点。

尝试 1 失败

这不起作用,并产生一个奇怪的查询(通过 SQL Server Profiler 看到)。

流畅的配置:

Map(m =>
{
    m.Property(t => t.CategoryName);
    m.ToTable("Categories");
});

生成的 SQL:

SELECT 
    [Extent1].[ProductId] AS [ProductId], 
    [Extent2].[ProductName] AS [ProductName], 
    [Extent2].[CategoryId] AS [CategoryId], 
    [Extent1].[CategoryName] AS [CategoryName], 
FROM  [dbo].[Categories] AS [Extent1]
INNER JOIN [dbo].[Product1] AS [Extent2] ON [Extent1].[ProductId] = [Extent2].[ProductId]

【问题讨论】:

  • 您为什么要这样做?这是一个 ORM,这意味着它需要一个对象来映射。听起来你真的想要更多地控制你的域级别对象,在这种情况下,只需为你的域使用一组单独的对象,为你的数据层使用另一个对象,并使用 automapper 之类的工具
  • 你是对的。在这个例子中,域对象被期望更紧密地绑定到 ORM 后端,消除了额外的 ORM-only 实体层和两层之间的后续映射。我很欣赏您对转向真正的 DDD 方法的洞察力。由于当前的设计决定是有争议的,我会离题。我只是希望 EF 专家可以与 EF Fluent API 交谈,以及这是否可能,而无需在 ORM 端创建相当于“特定于域实体的 SQL 视图”。从技术上讲,这似乎是可行的,只是我错过了一些东西......
  • 您将其描述为设计是一成不变的。为什么不能简单地映射 Product 和 Category 类并将它们投影到您显示的这个自定义 Product 类?

标签: c# .net sql-server entity-framework entity-framework-6


【解决方案1】:

简答:MS EF6 不是为此而设计的,你不能轻易做到。

请阅读keysrelationships 以及如何configure one-to-many relationship

EF 希望您的实体有一个键,该键是映射表的一部分。 您可以使用拆分,即将一些属性放在 Table1 中,而将一些属性放在 Table2 中,但如果两个表共享相同的主键。 这仅适用于 [1] -> [0..1] 关系。你所拥有的是一对多。

EF 映射您的数据库架构的方式创建两个实体并以Product.Category.Name 访问类别名称。

如果您完全不想暴露 Category 实体,您可以使用内部类和受保护的属性,将类别名称暴露为 sql-ignored 属性 public string CategoryName =&gt; this.Category?.Name

另一个选项是使用untracked SqlQuery。 然后您必须自己编写 SQL 查询,就像您为纯 ADO.NET 解决方案所做的那样。

如果您不想使用 EF 更改跟踪、关系等,请考虑使用更轻量级的 ORM,例如 Dapper、linq2db 或 BLToolkit。

【讨论】:

  • EF 中的直接 SQL 查询是一种选择,在我们评估该方法后,我们发现这意味着无论如何都要编写大量 SQL,而 EF 只是提供映射 API。 NHibernate 的行为方式相同(这是有道理的——NH 靠近 EF)。我们也将 Dapper 用于映射。最后,我们现在将坚持使用 ADO,并直接映射到我们存储库中的域模型,以提高性能和减少层数。如果我们看到这种情况越来越疯狂,我们总是可以选择将 ORM 推送到“真实”的东西(真正的 ORM——带有 1 个实体/表),然后映射到存储库中。
  • 你能用 EF Core 做到这一点
  • 更新!我们的模式已经完成了两年多,其中一半在生产中,而且对我们来说相当成功。我们已经做了一些很好的优化,以便获得超快速的查询,这在刚性 ORM 层中是不可能的。使用通用存储库设计,其实现直接位于 SQL 之上,我们成功地维护了我们的模式。我编写了一些相当复杂的架构组件,使其更易于使用,但并不是真正必要的。我希望有一天我能写下这个模式,也许是 github 吧。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多