【问题标题】:How to update EF Core 2.0 Owned Entities following a DDD aproach?如何按照 DDD 方法更新 EF Core 2.0 拥有的实体?
【发布时间】:2017-09-12 17:38:27
【问题描述】:

我有一个项目,我按照 DDD 方法使用 Asp.Net Core 2.0。因此,我拥有拥有实体和值对象的聚合根,这些实体和值对象被配置为拥有类型,这是 EF Core 2.0 中的一项新功能。我添加了此类配置的示例以更好地说明。

域:

public class Address : ValueObject 
{
    public Address(string street, int number){
        Street = street;
        Number = number;
    }

    public string Street {get; private set;}
    public int Number {get; private set;}
}  
public class Company : AggregateRoot
{
    public Address Address {get; private set;}
    // Other properties...

    // Value objects are immutables so I'm only able to replace it with a new object.
    public void UpdateAddress(string street, int number){
        Address = new Address(street, number);
    }
    // Other methods...
}

EF Core 实体配置:

internal class CompanyEntityTypeConfiguration : IEntityTypeConfiguration<Company>
{
    public void Configure(EntityTypeBuilder<Company> builder)
    {
        builder.HasKey(x => x.Id);
        builder.OwnsOne(x => x.Address);
    }
}

我正在从数据库中检索一个跟踪实体,当我尝试更新 AggregateRoot 并用一个新值对象替换一个值对象时,我收到一个异常消息,指出“已经在跟踪另一个相同类型的实体”。地址是被跟踪的实体。

因此,解决方案是检索未跟踪但我不想使用这种方法,因为它会在数据库中进行完整更新。 那么,如何从 Entity Framework 2.0 跟踪的实体中更新值对象(拥有的实体)?

【问题讨论】:

  • 不要替换旧的值对象,而是通过设置属性来修改它。
  • 据我了解,框架不允许您将 Address 设为 Value object (不可变),并且必须是 Entity (我的所有条款均来自 DDD 的观点) ,对吗?
  • CodingYoshi:我相信是这样,但这会很糟糕,因为我的域将与框架耦合。 Constantin Galbenu:是的,我必须将我所有的价值对象都变成实体。

标签: c# domain-driven-design entity-framework-core .net-2.0


【解决方案1】:

我在使用 ef core 1.0.0 时找到了一种解决方法。我无法再次找到将链接放在此处并将功劳归于作者的参考,但在您的情况下,解决方法是这样的:

public class Address : ValueObject
    {
        public Address(string street, int number)
        {
            Street = street;
            Number = number;
        }

        public string Street { get; private set; }
        public int Number { get; private set; }


        #region Value object workaround
        public Address WithStreet(string value) => new Address(value, Number);

        public Address WithNumber(int value) => new Address(Street, value);
        #endregion
    }
    public class Company : AggregateRoot
    {

        #region public public Address Address { get; set; }
        [NotMapped]
        public Address Address { get; set; }

        //In the DbContext this property needs to be mapped with a column in a database
        private string Address_Street
        {
            get { return Address.Street; }
            set { Address = Address.WithStreet(value); }
        }

        //In the DbContext this property needs to be mapped with a column in a database
        private int Address_Number
        {
            get { return Address.Number; }
            set { Address = Address.WithNumber(value); }
        }
        #endregion


        // Other properties...

        // Value objects are immutables so I'm only able to replace it with a new object.
        public void UpdateAddress(string street, int number)
        {
            Address = new Address(street, number);
        }
        // Other methods...
    }

EF Core 实体配置:

internal class CompanyEntityTypeConfiguration : IEntityTypeConfiguration<Company>
    {
        public void Configure(EntityTypeBuilder<Company> builder)
        {
            #region Address value object workaround
            builder.Property(typeof(string), "Address_Street").HasColumnName("Address_Street");
            builder.Property(typeof(int), "Address_Number").HasColumnName("Address_Number");
            #endregion
        }
    }

需要覆盖 DbContext 中的 SaveChanges()

DbContext:

public class YourDbContext : DbContext
    {
        //yours DbSet<>

        protected override void OnModelCreating(ModelBuilder builder)
        {
            //yours EntityTypeConfiguration
        }

        /// <summary>
        /// Overridden for value object workaround
        /// </summary>
        /// <returns></returns>
        public override int SaveChanges()
        {
            foreach (var entry in ChangeTracker.Entries())
            {
                foreach (var pi in entry.Entity.GetType().GetProperties(BindingFlags.Instance | BindingFlags.NonPublic))
                {
                    entry.Property(pi.Name).CurrentValue = pi.GetValue(entry.Entity);
                }
            }
            return base.SaveChanges();
        }
    }

如果 ef core 2.0 拥有的实体像 EF6.X 复杂类型一样工作以实现 DDD 值对象,那就太好了。此解决方法将需要一些猴子作业代码,但您将能够应用 DDD。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-10-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-14
    相关资源
    最近更新 更多