我有一个实体,它具有ExternalSystemName值对象和作为另一个实体的Deployment父类型。模型的重要部分如下:
public sealed class ExternalSystem : Entity
{
public ExternalSystemName Name { get; private set; }
public Deployment Deployment { get; private set; }
}
此实体的唯一性由部署ID(存储在部署实体类中(和名称(ExternalSystemName值对象的值(的组合确定。换句话说,一个部署不能有两个具有相同名称的外部系统。
当我试图用IEntityTypeConfiguration实现设置这个组合的唯一索引时,我遇到了一个问题:
internal sealed class ExternalSystemsConfiguration :
IEntityTypeConfiguration<ExternalSystem>
{
public void Configure(EntityTypeBuilder<ExternalSystem> builder)
{
builder.ToTable("TblExternalSystems");
builder.OwnsOne(e => e.Name, navigationBuilder =>
{
navigationBuilder.Property(e => e.Value)
.HasColumnName("Name");
});
builder.HasIndex(e => new { e.Name, e.Deployment }).IsUnique();
}
}
我在运行API时遇到这个异常:
System.InvalidOperationException: ''Name' cannot be used as a property on entity type 'ExternalSystem' because it is configured as a navigation.'
我试着将索引指向e.Name.Value,结果出现了以下错误:
System.ArgumentException: 'The expression 'e => new <>f__AnonymousType0`2(Value = e.Name.Value, Deployment = e.Deployment)' is not a valid member access expression. The expression should represent a simple property or field access: 't => t.MyProperty'. When specifying multiple properties or fields, use an anonymous type: 't => new { t.MyProperty, t.MyField }'. (Parameter 'memberAccessExpression')'
我还在其中一个属性上尝试了一个唯一的索引,但无论如何都会出现导航错误。我担心我已经知道答案了,但这是否意味着EF Core只支持非实体、非valueObject类型的列上的索引?这是否意味着我的模型需要一个表示部署ID的Guid属性,而不是部署本身?
更新/strong>
我了解到EF Core可以很好地处理引用/基元对。考虑到这一点,我的ExternalSystem实体现在可以同时具有以下两个属性:
public Deployment Deployment { get; private set; }
public Guid DeploymentId { get; private set; }
Guid属性不是构造函数的一部分,因为它们最终获得相同的列名,所以一切都很好。我现在可以把它添加到这个实体的配置中,并且索引已经正确创建:
builder.HasIndex(e => new { e.DeploymentId}).IsUnique();
我现在的问题是value对象。用同样的方法,我想我可以做这样的事情吗?
public ExternalSystemName NameV { get; private set; }
public string Name { get; private set; }
我必须重命名value对象属性,因为它们显然不能共享相同的名称。这与实体类型无关,因为EF Core知道添加";Id";到列名称的第一位。通过这种设置,EF Core正在复制列。其中一个具有名称";名称";而另一个具有";ExternalSystem_Name";。显然,由于该列不接受null值,因此其他任何操作都会失败。为什么会发生这种情况?
我通过为ExternalSystemName属性定义一个转换器来解决这个问题:
builder.Property(e => e.Name)
.HasConversion(
e => e.Value,
v => ExternalSystemName.Create(v).Value);
我现在可以这样声明我的唯一索引:
builder.HasIndex(e => new { e.DeploymentId, e.Name}).IsUnique();
我还升级到了.NET 7和EF Core 7,因为我问了这个问题,所以这可能也是一个因素。
您需要使用Fluent api手动将密钥定义为外键。
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<ExternalSystem>()
.HasOne(g => g.NameV)
.WithOne()
.HasForeignKey<ExternalSystem>(j => j.Name);
}
将此方法放入数据库上下文类中