如何使用流利的 api 在 EF6 中映射值对象标识?

Posted

技术标签:

【中文标题】如何使用流利的 api 在 EF6 中映射值对象标识?【英文标题】:How to map a value object identity in EF6 using fluent api? 【发布时间】:2019-03-13 21:24:30 【问题描述】:

在 DDD 中,将实体的标识作为值对象是一种常见的设计。

例子:

public class FooId : ValueObject  
  
    public int Id  get; private set; 
  

public class Foo
  
    public FooId FooId  get; private set; 
  

在 EF6 中,我可以使用以下代码映射这些类型:

modelBuilder.ComplexType<SomeType>()
    .Property(x => x.SomeProperty)
    ...

(见3 Reasons to Model Identity as A Value Object,IDDD)

编辑:IDDD in .Net

但是当我尝试映射 Foo 和 FooId 时,在迁移过程中出现以下错误

属性表达式“x => x.FooId.Id”无效。该表达式应表示一个属性:C#: 't => t.MyProperty' VB.Net: 'Function(t) t.MyProperty'。指定多个属性时使用匿名类型:C#: 't => new t.MyProperty1, t.MyProperty2 ' VB.Net: 'Function(t) New With t.MyProperty1, t.MyProperty2 '。

我的配置:

public class FooConfiguration : EntityTypeConfiguration<Foo>
  
    public FooConfiguration()
    
        HasKey(x => x.FooId.Id);
    
  

public class FooContext : EntityTypeConfiguration<Foo>
  
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    
        modelBuilder.ComplexType<FooId>();
        modelBuilder.Configurations.Add(new FooConfiguration());
    
  

使用的包

EntityFramework 6.2.0 mysql.Data 6.10.7 MySql.Data.Entity 6.10.7

【问题讨论】:

【参考方案1】:

不幸的是,目前在EF 6.x 中是不可能的,您必须处理常规原语。虽然在EF core 2.1 中使用Value Conversions 是可能的。

作为经典.Net Framework 的替代方案,您可以尝试NHibernate,因为它允许将值对象作为身份。从Domain-Driven Design 的角度来看,NHibernate 看起来仍然比EF 更强大。

【讨论】:

【参考方案2】:

EF 自动识别复杂类型(值对象),无需添加任何流畅的 api 映射。

我给你一个来自this course, by Julie Lerman的例子

Address 是一个值对象:

public class Address 

    public string Street  get; private set; 
    public string City  get; private set; 
    public string StateProvince  get; private set; 
    public string PostalCode  get; private set; 

SalesOrder 是我们的实体,它使用Address 复杂类型。

public class SalesOrder

    public int SalesOrderId  get; private set; 
    public DateTime OrderDate  get; private set; 
    public DateTime? DueDate  get; private set; 
    public string PurchaseOrderNumber  get; private set; 
    public string Comment  get; private set; 
    public Address ShippingAddress  get; private set; 

现在,如果您首先使用 EF 代码来构建您的数据库表,您将得到以下结果(迁移代码):

CreateTable("SalesOrder", c => new

    SalesOrderId = c.Int(nullable: false),
    OrderDate = c.DateTime(nullable: false),
    DueDate = c.DateTime(),
    PurchaseOrderNumber = c.String(),
    Comment = c.String(),
    ShippingAddress_Street = c.String(),
    ShippingAddress_City = c.String(),
    ShippingAddress_StateProvince = c.String(),
    ShippingAddress_PostalCode = c.String(),
)
.PrimaryKey(t => t.SalesOrderId);

注意EF是直接把所有地址字段加到表中的。

您不需要任何额外的流利的api映射来让实体框架将Address字段添加到表中,以上是默认行为。

DbContext 是这样的:

public class OrderContext: DbContext

    public OrderContext() : base("connectionStringName")  
    DbSet<SalesOrder> Orders  get; set; 

【讨论】:

感谢您的评论。我的项目中有很多价值对象,是的,我可以用 EF 正确映射它们。我试图实现的唯一一件事是也将值对象用作身份,正如@Albert 所述,EF 目前不支持该身份。我想也许有其他人使用它来工作的解决方法,这就是我在 SO 上发布这个问题的原因。现在我仍在使用原语作为我的身份,因为在我当前的项目中迁移到另一个 ORM 对我来说有点昂贵,也许在我的下一个项目中,我会尝试 Albert 的建议

以上是关于如何使用流利的 api 在 EF6 中映射值对象标识?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 ASP.NET MVC 5、Entity Framework 6 中使用流利的 API 映射表?

您如何在流利的 nhibernate 中映射和使用连接表?

EF6:如何避免循环引用?

流利的nhibernate映射多对多配置错误

什么是实体框架流利的 api?

如何使用流利的断言报告对象的名称