I’m trying to implement DDD approach in my project but realized that I have too big aggregates and trying to minimize the amount of data loaded from the database. I have an aggregate Order which during some operations depends on another aggregate- Company. To perform operation RefreshOrderNumber() I need three properties from the Company entity and totally not interested in other fields from the **Company. So, I got an idea to create a value type for Order – OrderCompanyInfo and incapsulate those fields on it
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
}
}
Now I’m trying to make this works with EF core. Basically OrderCompanyInfo should be configured using OwnOne() method and mapped to the same table as Company entity has so the data always will be actual during Order aggregate loading through repositories. So I have this configuration for the Order aggregate:
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);
});
}
But trying this way gives me an error:
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.
Is there any way to make EF core work in this way?
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
Ok, the answer was found on GitHub community.
https://github.com/dotnet/efcore/issues/13162#issuecomment-417411405
They provided an example with two Entities which mapped to the same 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));
});
}
}
I was wrong trying to build OrderCompanyInfo as Owned type, cause owned type implies having relation from owned type to the owner. In my scenario, this will lead to the situation when Company table will have OrderId field, which is at least incorrect. Having OrderCompanyInfo entity configured through HasOne() suites me well
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0