首页 > 解决方案 > 如何防止 EF Core 将 DDD ValueObjects 创建为表

问题描述

我正在使用 EF Core 5.0.1

我创建了以下 ValueObject

public class Address : BaseValueObject
    {

        public Address(string street, string zipCode, string city, string state, string country)
        {
            Street = street;
            ZipCode = zipCode;
            City = city;
            State = state;
            Country = country;
        }

        public string Street { get; set;  }
        public string ZipCode { get; set; }
        public string City { get; set; }
        public string State { get; set; }
        public string Country { get; set; }
        
        protected override IEnumerable<object> GetEqualityComponents()
        {
            yield return Street;
            yield return ZipCode;
            yield return City;
            yield return State;
            yield return Country;
        }

    }

以及拥有值对象的实体

public class Location : BaseEntity
    {
        [Required]        
        public string Name { get; set; }
        
        public Address Address { get; set; }
    }

Location对象配置如下

public class LocationConfiguration : BaseEntityConfiguration<Location>
    {
        public override void Configure(EntityTypeBuilder<Location> builder)
        {            
            builder.OwnsOne(o => o.Address, a =>
            {
                a.Property(p => p.Street).HasMaxLength(600)
                    .HasDefaultValue("");
                a.Property(p => p.City).HasMaxLength(150)
                    .HasDefaultValue("");
                a.Property(p => p.State).HasMaxLength(60)
                    .HasDefaultValue("");
                a.Property(p => p.ZipCode).HasMaxLength(12)
                    .HasDefaultValue("");
            });

            base.Configure(builder);
        }
    }

问题是 EF Core Migration 仍在将 ValueObjects 创建为数据库中的表。我究竟做错了什么?在生成的迁移代码中,我可以看到以下内容:

migrationBuilder.CreateTable(
                name: "Location",
                columns: table => new
                {
                    Id = table.Column<long>(type: "INTEGER", nullable: false)
                        .Annotation("Sqlite:Autoincrement", true),
                    Name = table.Column<string>(type: "TEXT", maxLength: 80, nullable: false),
                    Timestamp = table.Column<string>(type: "TEXT", rowVersion: true, nullable: false, defaultValueSql: "CURRENT_TIMESTAMP")
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Location", x => x.Id);
                });



migrationBuilder.CreateTable(
                name: "Address",
                columns: table => new
                {
                    LocationId = table.Column<long>(type: "INTEGER", nullable: false),
                    Street = table.Column<string>(type: "TEXT", maxLength: 600, nullable: true, defaultValue: ""),
                    ZipCode = table.Column<string>(type: "TEXT", maxLength: 12, nullable: true, defaultValue: ""),
                    City = table.Column<string>(type: "TEXT", maxLength: 150, nullable: true, defaultValue: ""),
                    State = table.Column<string>(type: "TEXT", maxLength: 60, nullable: true, defaultValue: ""),
                    Country = table.Column<string>(type: "TEXT", nullable: true)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Address", x => x.LocationId);
                    table.ForeignKey(
                        name: "FK_Address_Location_LocationId",
                        column: x => x.LocationId,
                        principalTable: "Location",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                });

最后是 DBContext 类

public class MyDbContext : DbContext
    {        
        public PlannerDbContext(DbContextOptions<PlannerDbContext> options)
            : base(options)
        {
        }                     
        
        public DbSet<Location> Locations { get; set; }        


        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {            
            // do not use plural form for table names
            modelBuilder.Model.GetEntityTypes()
                .Configure(e => e.SetTableName(e.DisplayName())); 
                            
            base.OnModelCreating(modelBuilder);                                              

            modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());            
        }
    }

标签: c#entity-framework-core.net-5

解决方案


EF 为值对象创建表的原因是更改了表命名约定的代码部分。我想,我已经从一些关于以单数形式设置表名的热问题中复制了它。

此方法modelBuilder.Model.GetEntityTypes()为模型中的所有对象(包括值对象)返回一个枚举器。针对值对象配置表名属性将导致 EF 将此类型视为实体。

这部分

            // do not use plural form for table names
            modelBuilder.Model.GetEntityTypes()
                .Configure(e => e.SetTableName(e.DisplayName())); 

应该改为

            // do not use plural form for table names
            modelBuilder.Model.GetEntityTypes()
                .Where(x => !x.ClrType.IsSubclassOf(typeof(BaseValueObject)))
                .Configure(e => e.SetTableName(e.DisplayName()));

推荐阅读