EF 核心 3.1 - 实体和拥有类型的通用 table
EF core 3.1 - common table for entity and owned type
我正在尝试在我的项目中实施 DDD 方法,但意识到我的聚合太大,并试图尽量减少从数据库加载的数据量。我有一个聚合 Order,它在某些操作中依赖于另一个聚合 - Company。要执行 RefreshOrderNumber() 操作,我需要 Company 实体的三个属性,并且对 **Company 的其他字段完全不感兴趣。因此,我想到了为订单创建一个值类型 - OrderCompanyInfo 并将这些字段封装在其上
public class Company
{
public Id {get;private set;}
public string Name {get; private set;}
public string Currency {get; private set;}
public string DocumentPrefix {get; private set;}
// other ~15 fields
}
public class OrderCompanyInfo : ValuableType
{
private OrderCompanyInfo ()
{
}
public long CompanyId {get; private set;}
public string Name {get; private set;}
public string Currency {get; private set;}
public string DocumentPrefix {get; private set;}
public static OrderCompanyInfo Create(Company company)
{
return OrderCompanyInfo {
CompanyId = company.Id,
Name = company.Name,
Currency = company.Currency ,
DocumentPrefix = company.DocumentPrefix
}
}
}
public class Order
{
private Order()
{
}
public Order(OrderCompanyInfo companyInfo)
{
CompanyInfo = companyInfo;
AddLog();
}
public OrderNumberInfo OrderNumber {get; private set;}
public OrderCompanyInfo CompanyInfo {get; private set;}
public void DoSomethingImportant()
{
RefreshOrderNumber();
AddLog();
}
private void AddLog()
{
//Add log entry using CompanyInfo.Name
}
private void RefreshOrderNumber()
{
//Create new OrderNumberInfo using OrderCompanyInfo.Currency and OrderCompanyInfo.DocumentPrefix
}
}
现在我正在努力使它与 EF 核心一起工作。基本上 OrderCompanyInfo 应该使用 OwnOne() 方法进行配置,并映射到与 Company[=29 相同的 table =] 实体有,因此数据在通过存储库加载订单聚合期间始终是实际的。所以我有这样的订单聚合配置:
public override void Configure(EntityTypeBuilder<Order> builder)
{
builder.OwnsOne(x => x.InventoryOwnsCompanyProfile, opt =>
{
opt.ToTable("Company");
opt.HasOne<Company>()
.WithOne()
.HasForeignKey<OrderCompanyInfo>(x => x.CompanyId);
});
}
但是尝试这种方式给我一个错误:
Cannot use table 'dbo.Company' for entity type 'OrderCompanyInfo ' since it is being used for entity type 'Company' and there is no relationship between their primary keys.
有没有办法让 EF 核心以这种方式工作?
好的,答案已在 GitHub 社区中找到。
https://github.com/dotnet/efcore/issues/13162#issuecomment-417411405
他们提供了一个示例,其中两个实体映射到同一个 table
namespace One
{
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
}
namespace Two
{
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime Birthday { get; set; }
}
}
public class BloggingContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Test;ConnectRetryCount=0");
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<One.Person>(b =>
{
b.HasOne<Two.Person>().WithOne().HasForeignKey<Two.Person>(e => e.Id);
b.ToTable("People");
b.Property(e => e.Name).HasColumnName(nameof(One.Person.Name));
});
builder.Entity<Two.Person>(b =>
{
b.ToTable("People");
b.Property(e => e.Name).HasColumnName(nameof(Two.Person.Name));
});
}
}
我试图将 OrderCompanyInfo 构建为 Owned 类型是错误的,因为拥有的类型意味着从拥有的类型到所有者的关系。在我的场景中,这将导致 Company table 将具有 OrderId 字段的情况,这至少是不正确的。通过 HasOne() 配置 OrderCompanyInfo 实体非常适合我
我正在尝试在我的项目中实施 DDD 方法,但意识到我的聚合太大,并试图尽量减少从数据库加载的数据量。我有一个聚合 Order,它在某些操作中依赖于另一个聚合 - Company。要执行 RefreshOrderNumber() 操作,我需要 Company 实体的三个属性,并且对 **Company 的其他字段完全不感兴趣。因此,我想到了为订单创建一个值类型 - OrderCompanyInfo 并将这些字段封装在其上
public class Company
{
public Id {get;private set;}
public string Name {get; private set;}
public string Currency {get; private set;}
public string DocumentPrefix {get; private set;}
// other ~15 fields
}
public class OrderCompanyInfo : ValuableType
{
private OrderCompanyInfo ()
{
}
public long CompanyId {get; private set;}
public string Name {get; private set;}
public string Currency {get; private set;}
public string DocumentPrefix {get; private set;}
public static OrderCompanyInfo Create(Company company)
{
return OrderCompanyInfo {
CompanyId = company.Id,
Name = company.Name,
Currency = company.Currency ,
DocumentPrefix = company.DocumentPrefix
}
}
}
public class Order
{
private Order()
{
}
public Order(OrderCompanyInfo companyInfo)
{
CompanyInfo = companyInfo;
AddLog();
}
public OrderNumberInfo OrderNumber {get; private set;}
public OrderCompanyInfo CompanyInfo {get; private set;}
public void DoSomethingImportant()
{
RefreshOrderNumber();
AddLog();
}
private void AddLog()
{
//Add log entry using CompanyInfo.Name
}
private void RefreshOrderNumber()
{
//Create new OrderNumberInfo using OrderCompanyInfo.Currency and OrderCompanyInfo.DocumentPrefix
}
}
现在我正在努力使它与 EF 核心一起工作。基本上 OrderCompanyInfo 应该使用 OwnOne() 方法进行配置,并映射到与 Company[=29 相同的 table =] 实体有,因此数据在通过存储库加载订单聚合期间始终是实际的。所以我有这样的订单聚合配置:
public override void Configure(EntityTypeBuilder<Order> builder)
{
builder.OwnsOne(x => x.InventoryOwnsCompanyProfile, opt =>
{
opt.ToTable("Company");
opt.HasOne<Company>()
.WithOne()
.HasForeignKey<OrderCompanyInfo>(x => x.CompanyId);
});
}
但是尝试这种方式给我一个错误:
Cannot use table 'dbo.Company' for entity type 'OrderCompanyInfo ' since it is being used for entity type 'Company' and there is no relationship between their primary keys.
有没有办法让 EF 核心以这种方式工作?
好的,答案已在 GitHub 社区中找到。 https://github.com/dotnet/efcore/issues/13162#issuecomment-417411405
他们提供了一个示例,其中两个实体映射到同一个 table
namespace One
{
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
}
namespace Two
{
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime Birthday { get; set; }
}
}
public class BloggingContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Test;ConnectRetryCount=0");
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<One.Person>(b =>
{
b.HasOne<Two.Person>().WithOne().HasForeignKey<Two.Person>(e => e.Id);
b.ToTable("People");
b.Property(e => e.Name).HasColumnName(nameof(One.Person.Name));
});
builder.Entity<Two.Person>(b =>
{
b.ToTable("People");
b.Property(e => e.Name).HasColumnName(nameof(Two.Person.Name));
});
}
}
我试图将 OrderCompanyInfo 构建为 Owned 类型是错误的,因为拥有的类型意味着从拥有的类型到所有者的关系。在我的场景中,这将导致 Company table 将具有 OrderId 字段的情况,这至少是不正确的。通过 HasOne() 配置 OrderCompanyInfo 实体非常适合我