如何以比我现在更有效的方式将数据库列映射到 EF 模型 属性
How to map database column to EF model property in a more efficient way than I'm doing now
我在 SQL 服务器中有一个可为 null 的 varchar(max)
列,我正在映射到 EF 代码优先中的 Guid?
。然而,这个 属性 实际上是在许多其他实体派生自的基础 class 中。
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Model1>().Property(e => e.Property1).HasConversion(p => p.ToString(), p => (Guid?)Guid.Parse(p));
}
以上行对每个 table 重复多次。有没有办法告诉 EF 这是一个基数 class 属性 因此映射只能声明一次?
当然可以。由于缺少自定义约定,它是通过 "typical" modelBuilder.Model.GetEntityTypes()
循环实现的。像这样(只需更改基础 class 和 属性 名称):
var entityTypes = modelBuilder.Model.GetEntityTypes()
.Where(t => t.ClrType.IsSubclassOf(typeof(BaseClass)));
var valueConverter = new ValueConverter<Guid, string>(
v => v.ToString(), v => (Guid?)Guid.Parse(v));
foreach (var entityType in entityTypes)
entityType.FindProperty(nameof(BaseClass.Property1)).SetValueConverter(valueConverter);
您也可以考虑使用开箱即用的 EF Core Guid
到 String
转换器:
var valueConverter = new GuidToStringConverter();
最好进行下一次计算属性:
[Column("Property1")]
public string Property1Raw { get; set; }
[IgnoreDataMember]
public Guid? Property1
{
get => Guid.TryParse(Property1AsString, out Guid result) ? result : (Guid?)null;
set => Property1Raw = value?.ToString();
}
另一种方法是有一个匹配的基础 class IEntityTypeConfiguration
:
internal class EntityConfiguration<T> : IEntityTypeConfiguration<T> where T : Entity
{
public virtual void Configure(EntityTypeBuilder<T> builder)
{
builder.Property(e => e.Property1).HasConversion(p => p.ToString(), p => (Guid?)Guid.Parse(p));
// ... Other base-specific config here
}
}
(假设这里你的基地 class 被称为 Entity
- 根据需要改变)。
当您使用分解实体配置的模式时,这种方法效果更好,因此您的配置可能是这样的:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new Model1EntityConfiguration());
modelBuilder.ApplyConfiguration(new Model2EntityConfiguration());
// ...
}
...
internal sealed class Model1EntityConfiguration : EntityConfiguration<Model1>
{
public override void Configure(EntityTypeBuilder<Model1> builder)
{
base.Configure(builder); // <-- here's the key bit
// ...; e.g.
builder.Property(c => c.Name).HasMaxLength(80).IsRequired();
}
}
我在 SQL 服务器中有一个可为 null 的 varchar(max)
列,我正在映射到 EF 代码优先中的 Guid?
。然而,这个 属性 实际上是在许多其他实体派生自的基础 class 中。
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Model1>().Property(e => e.Property1).HasConversion(p => p.ToString(), p => (Guid?)Guid.Parse(p));
}
以上行对每个 table 重复多次。有没有办法告诉 EF 这是一个基数 class 属性 因此映射只能声明一次?
当然可以。由于缺少自定义约定,它是通过 "typical" modelBuilder.Model.GetEntityTypes()
循环实现的。像这样(只需更改基础 class 和 属性 名称):
var entityTypes = modelBuilder.Model.GetEntityTypes()
.Where(t => t.ClrType.IsSubclassOf(typeof(BaseClass)));
var valueConverter = new ValueConverter<Guid, string>(
v => v.ToString(), v => (Guid?)Guid.Parse(v));
foreach (var entityType in entityTypes)
entityType.FindProperty(nameof(BaseClass.Property1)).SetValueConverter(valueConverter);
您也可以考虑使用开箱即用的 EF Core Guid
到 String
转换器:
var valueConverter = new GuidToStringConverter();
最好进行下一次计算属性:
[Column("Property1")]
public string Property1Raw { get; set; }
[IgnoreDataMember]
public Guid? Property1
{
get => Guid.TryParse(Property1AsString, out Guid result) ? result : (Guid?)null;
set => Property1Raw = value?.ToString();
}
另一种方法是有一个匹配的基础 class IEntityTypeConfiguration
:
internal class EntityConfiguration<T> : IEntityTypeConfiguration<T> where T : Entity
{
public virtual void Configure(EntityTypeBuilder<T> builder)
{
builder.Property(e => e.Property1).HasConversion(p => p.ToString(), p => (Guid?)Guid.Parse(p));
// ... Other base-specific config here
}
}
(假设这里你的基地 class 被称为 Entity
- 根据需要改变)。
当您使用分解实体配置的模式时,这种方法效果更好,因此您的配置可能是这样的:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new Model1EntityConfiguration());
modelBuilder.ApplyConfiguration(new Model2EntityConfiguration());
// ...
}
...
internal sealed class Model1EntityConfiguration : EntityConfiguration<Model1>
{
public override void Configure(EntityTypeBuilder<Model1> builder)
{
base.Configure(builder); // <-- here's the key bit
// ...; e.g.
builder.Property(c => c.Name).HasMaxLength(80).IsRequired();
}
}