我遇到这样的情况:一个实体有一个不活动的子代列表。此外,它们还有另一个相同类型的“当前/活动”子实体。
以下是理想的建模,但是我无法弄清楚如何使用Entity Framework Core:
public class Customer
{
public Application CurrentApplication { get; set; }
public List<Application> PastApplications { get; set; }
}
public class Application
{
public bool IsCurrent { get; set; }
public Customer Customer { get; set; }
}
过去,我通常将其建模为:
public class Customer
{
public Application CurrentApplication => AllApplications.Single(a => a.IsCurrent);
public List<Application> PastApplications => AllApplications.Where(a => !a.IsCurrent);
public List<Application> AllApplications { get; set; }
}
public class Application
{
public bool IsCurrent { get; set; }
public Customer Customer { get; set; }
}
但是,我认为这可能导致另一个Application
被错误地设置为IsCurrent
的可能性,从而破坏了.Single()
。
从DDD角度来看,完成此操作的建议方法是什么?如果这与EF Core的功能不符,那么什么是实用的好建议?
答案 0 :(得分:3)
我不认为这是DDD问题,而是关于如何设计关系数据库模型以及如何使用EF Core问题。
首先,您需要确定客户与应用程序之间的关系是什么
如果在给定时间(每个客户)只有一个活动的应用程序,则可以使用客户与应用程序之间的一对一(准确地说是零对一)关系来对活动应用程序进行建模。客户方的外键)。您也可以尝试在应用程序上使用标志字段对它进行建模,但是它不能像外键一样具有防错功能(但是可能具有更好的性能)。
您发布的代码非常类似于一对多的情况,因此我为这种情况提供了一个示例。了解以下内容后,您可以根据需要轻松地将其更改为“多对多”。
首先让我们定义实体:
public class Customer
{
public int Id { get; set; }
public int? CurrentApplicationId { get; set; }
public Application CurrentApplication { get; set; }
public ICollection<Application> Applications { get; set; }
}
public class Application
{
public int Id { get; set; }
public Customer Customer { get; set; }
}
唯一有趣的部分是int? CurrentApplicationId
。我们需要为零对多关系明确定义外键(稍后将对此进行详细介绍)。可为null的int(int?)告诉EF此字段可以为NULL。
EF通常能够找出关系映射,但是在这种情况下,我们需要对其进行明确解释:
class DataContext : DbContext
{
// ctor omitted for brevity
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Customer>(customer =>
{
customer.HasMany(entity => entity.Applications)
.WithOne(relatedEntity => relatedEntity.Customer)
.OnDelete(DeleteBehavior.Cascade);
customer.HasOne(entity => entity.CurrentApplication)
.WithOne()
.HasForeignKey<Customer>(entity => entity.CurrentApplicationId);
});
}
public DbSet<Application> Applications { get; set; }
public DbSet<Customer> Customers { get; set; }
}
OnModelCreating 方法中发生的事情称为fluent API configuration。该主题和conventions是理解和控制EF如何将实体映射到数据库表的必不可少的内容。
基于映射,EF能够生成(请参见code-first migrations)以下数据库模式:
CREATE TABLE [Customers] (
[Id] INTEGER NOT NULL
, [CurrentApplicationId] bigint NULL
, CONSTRAINT [sqlite_master_PK_Customers] PRIMARY KEY ([Id])
, FOREIGN KEY ([CurrentApplicationId]) REFERENCES [Applications] ([Id]) ON DELETE RESTRICT ON UPDATE NO ACTION
);
CREATE UNIQUE INDEX [IX_Customers_CurrentApplicationId] ON [Customers] ([CurrentApplicationId] ASC);
CREATE TABLE [Applications] (
[Id] INTEGER NOT NULL
, [CustomerId] bigint NULL
, CONSTRAINT [sqlite_master_PK_Applications] PRIMARY KEY ([Id])
, FOREIGN KEY ([CustomerId]) REFERENCES [Customers] ([Id]) ON DELETE CASCADE ON UPDATE NO ACTION
);
CREATE INDEX [IX_Applications_CustomerId] ON [Applications] ([CustomerId] ASC);
正是我们想要的。
现在,如何在此配置中查询活动和非活动应用程序?像这样:
var customerId = 1;
using (var ctx = new DataContext())
{
var currentApplication = (
from customer in ctx.Customers
where customer.Id == customerId
select customer.CurrentApplication
).FirstOrDefault();
var pastApplications =
(
from customer in ctx.Customers
from application in customer.Applications
where customer.Id == customerId && customer.CurrentApplication != application
select application
).ToArray();
}
我建议您通读here的方法以熟悉EF Core。
对于关系数据库建模,this site似乎是有用的资源。
答案 1 :(得分:0)
使用EFCore,可以进行以下操作:
public class Customer
{
[Key]
public int ID {get; set;}
//other properties
//Navigation Property
public virtual ICollection<Application> Applications{ get; set; }
}
public class Application
{
[Key]
public int ID {get; set;}
[ForeignKey("Customer")]
public int CustomerID{get; set;}
public DateTime ApplicationDate{get; set}
//other properties
public bool IsCurrent { get; set; }
}
我假设您使用的是Code First,因此这将是进行映射的正确方法。
迁移和更新上下文之后,您可以使用一些后端代码来始终确保最新的应用程序返回为IsCurrent。
然后,您可以按照以下步骤选择当前应用程序:
private yourContext _context;
var yourContext = _context.Customers
.Include(m=>m.Application)
.Where(m=> m.isCurrent==false)
.Single(a=>a.);
return yourContext;
您当然必须使用上下文等设置构造器