[英]EF Core domain-wide value conversion for (nested) owned types
我在 EF Core 中設置了幾個值對象(DDD 范式)作為擁有的類型。
EF Core 支持通過Owned()
方法以一種自動將給定類型的所有引用視為擁有類型的方式配置擁有類型。
但是,我似乎找不到一種方法來以類似的集中方式指定它們的配置,尤其是值轉換。
{ // Configure value objects as owned types.
builder.Owned(typeof(Money));
builder.Owned(typeof(Currency));
builder.Owned(typeof(Address));
builder.Owned(typeof(Mass));
builder.Owned(typeof(MassUnit));
// Store and restore mass unit as symbol.
builder.Entity<Product>()
.OwnsOne(p => p.Mass, c => c.Property(m => m.Unit)
.HasConversion(
u => u.Symbol,
s => MassUnit.FromSymbol(s))
.HasMaxLength(3)
);
}
如上所示,為MassUnit
配置了一個值轉換,即嵌套在Mass
中的 Value Object 。
但是對於所有使用值對象的地方,我必須手動執行此操作。 例如,我已經在 3 個不同的地方使用Money
類型,並且該類型包含Currency
,我希望為其配置類似的價值轉換。
是否有任何(好的)方法來為擁有的類型指定一般的、域范圍的配置?
我已經嘗試通過builder.Entity<Mass>().Property(m => m.Unit).HasConversion(..)
來配置它們,但是如果您嘗試通過Entity<>
配置擁有的類型,EF Core 似乎會拋出異常Entity<>
。
這可以使用共享的ValueConverter
:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
var converter = new ValueConverter<EquineBeast, string>(
v => v.ToString(),
v => (EquineBeast)Enum.Parse(typeof(EquineBeast), v));
modelBuilder
.Entity<Rider>()
.Property(e => e.Mount)
.HasConversion(converter);
}
更新:實際上我在 EF Core 6 中發現了一個可用於集中配置的新功能。 它被稱為約定前 model 配置。 在您的數據庫上下文 class 中,您可以定義一個接受ModelConfigurationBuilder
實例的新ConfigureConventions()
方法覆蓋。
有了這個,我在“過時的答案”部分提供的舊樣本可以變成:
protected override void ConfigureConventions(ModelConfigurationBuilder configBuilder)
{
configBuilder.Properties<Currency>()
.HaveConversion<CurrencyConverter>()
.HaveMaxLength(3);
}
// Create the converter:
public class CurrencyConverter : ValueConverter<Currency, string>
{
public CurrencyConverter()
: base(
currency => currency.Code,
currencyCode => Currency.FromCode(currencyCode))
{}
}
我唯一不喜歡的是我們被迫定義轉換器類而不是能夠使用 lambda。 對於像這樣的簡單轉換,這絕對是一種儀式感。
另外,不要忘記沒有顯式轉換的擁有類型仍必須配置為.Owned<>()
,因此在我的示例中,我應該在OnModelCreating()
中有builder.Owned<Money>()
) 。 我認為在 OnModelCreating 中保留.Owned<>()
配置有點令人困惑,因為這也是給定類型的集中/通用配置。
額外提示:您還可以集中配置字符串長度和小數精度:
// So, this awkward old solution:
protected override void OnModelCreating(ModelBuilder builder)
{
foreach (var entityType in builder.Model.GetEntityTypes())
{
foreach (var decimalProperty in entityType.GetProperties()
.Where(x => x.ClrType == typeof(decimal)))
{
decimalProperty.SetPrecision(18);
decimalProperty.SetScale(4);
}
}
}
// Can become:
protected override void ConfigureConventions(ModelConfigurationBuilder configBuilder)
{
configBuilder.Properties<decimal>()
.HavePrecision(precision: 18, scale: 4);
}
過時的答案:我仍然沒有找到實際的解決方案,即使 EF Core 6 似乎也沒有對此進行任何功能改進。 但是,為了在評論中回答 Bodgan 的問題,我采用的解決方法如下所示; 本質上類似於 Ionix 的建議,只是更進一步:
// Store and restore currency as currency code.
builder.Entity<Product>().OwnsOne(p => p.Price, StoreCurrencyAsCode);
builder.Entity<Transaction>().OwnsOne(p => p.Total, StoreCurrencyAsCode);
builder.Entity<TransactionLine>().OwnsOne(p => p.UnitPrice, StoreCurrencyAsCode);
static void StoreCurrencyAsCode<T>(OwnedNavigationBuilder<T, Money> onb) where T : class
=> onb.Property(m => m.Currency)
.HasConversion(
c => c.Code,
c => Currency.FromCode(c))
.HasMaxLength(3);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.