重构为值对象模式后 EFCF 6 复杂类型映射失败

EFCF 6 Complex Type mapping failing after refactoring toward Value Object pattern

我正在重构复杂类型的工作实现 class 以应用值对象模式,这将使它成为不可变类型。

我曾想,我已经开始工作了,直到我尝试为这些更改设置迁移(我认为对 EF 重要的主要更改是我将一些属性更改为仅获取它们的位置以前有二传手)。当我 运行 Update-Database:

时,我目前遇到以下错误
PM> update-database
Specify the '-Verbose' flag to view the SQL statements being applied to the target database.
Applying explicit migrations: [201701010745393_initial].
Applying explicit migration: 201701010745393_initial.
Running Seed method.
System.Data.Entity.Core.EntityCommandCompilationException: An error occurred while preparing the command definition. See the inner exception for details. ---> System.Data.Entity.Core.MappingException: 
(29,10) : error 3004: Problem in mapping fragments starting at line 29:No mapping specified for properties Contact.Address in Set Contacts.

(46,10) : error 3004: Problem in mapping fragments starting at line 46:No mapping specified for properties Company.Address in Set Companies.

(56,10) : error 3004: Problem in mapping fragments starting at line 56:No mapping specified for properties CompanyLocation.Address in Set Locations.

这似乎违背了我一直在阅读的关于 EF 如何处理复杂类型的内容,因为它应该在遇到使用复杂类型 属性 的实体时自动映射事物。以前是这种情况,但是 不再使用不可变 class 的更改。 我不确定为什么会这样。

以下是与这些错误消息相关的 classes:

public class Company : PocoBase
{
    [Required]
    public Address Address { get; set; }

    public virtual ICollection<Client> Clients { get; set; }

    public virtual ICollection<CompanyLocation> Locations { get; set; }

    [Required]
    public string Name { get; set; }
}

public class CompanyLocation : PocoBase
{
    [Required]
    public Address Address { get; set; }

    public virtual Company Company { get; set; }

    [ForeignKey("Company")]
    public Guid CompanyId { get; set; }

    public string Description { get; set; }

    public string Label { get; set; }
}

public class Contact : PocoBase
{
    public Address Address { get; set; }

    [Required]
    public string CellNumber { get; set; }

    public virtual Client Client { get; set; }

    [ForeignKey("Client")]
    public Guid ClientId { get; set; }

    public virtual Company Company { get; set; }

    [ForeignKey("Company")]
    public Guid CompanyId { get; set; }

    [Required]
    public string Email { get; set; }

    [Required]
    public string Name { get; set; }

    [Required]
    public string OfficeNumber { get; set; }
}

当然还有最重要的地址 class,它现在引起了问题!

[ComplexType]
public class Address
{
    [Required]
    public string City { get; }

    [Required]
    public string Country { get; }

    [Required, StringLength(10, MinimumLength = 5)]
    public string PostalCode { get; }

    [Required, StringLength(2, MinimumLength = 2)]
    public string State { get; }

    [Required]
    public string StreetAddress { get; }

    public string UnitNumber { get; }

    public Address(string street, string city, string state, string zip, string unit = null) : this("United States", street, city, state, zip, unit) { }

    public Address(string country, string street, string city, string state, string zip, string unit = null)
    {
        VerifyZipCodeFormat(zip);

        Country = country;
        StreetAddress = street;
        City = city;
        State = state;
        PostalCode = zip;
        UnitNumber = unit;
    }

    private static void VerifyZipCodeFormat(string zip)
    {
        if (zip.Length > 5)
            if (zip[5] != '-')
                throw new ArgumentOutOfRangeException(nameof(zip), zip[5], "Postal Code must be in the format of \"XXXXX\" or \"XXXXX-XXXX\"");
            else if (zip.Length != 10)
                throw new ArgumentOutOfRangeException(nameof(zip), zip.Length, "Postal Code must be either 5 or 10 characters, in either the format of \"XXXXX\" or \"XXXXX-XXXX\"");
    }

    public Address WithCity(string city)
    {
        return new Address(Country, StreetAddress, city, State, PostalCode, UnitNumber);
    }
    public Address WithCountry(string country)
    {
        return new Address(country, StreetAddress, City, State, PostalCode, UnitNumber);
    }
    public Address WithStateOrProvince(string state)
    {
        return new Address(Country, StreetAddress, City, state, PostalCode, UnitNumber);
    }
    public Address WithStreetAddress(string street)
    {
        return new Address(Country, street, City, State, PostalCode, UnitNumber);
    }
    public Address WithUnitNumber(string unit)
    {
        return new Address(Country, StreetAddress, City, State, PostalCode, unit);
    }
    public Address WithZipOrPostalCode(string zip)
    {
        VerifyZipCodeFormat(zip);

        return new Address(Country, StreetAddress, City, State, zip, UnitNumber);
    }
}

我曾尝试手动执行此映射,与我相信 EFCF 约定相同的是 - Address_PropertyName 在我 运行 添加时 EF 尝试创建的迁移文件中 -迁移,但这也没有用。

无论是使用 [ComplexTypeAttribute] 还是使用 modelBuilder.ComplexType() 行都无法解决这些错误消息,所以我不得不假设它与现在不可变的 class 有关。

我曾希望添加带有列名的 [ColumnAttribute] 会以某种方式解决问题,正如 this post 中所指出的那样,但这也没有解决问题。

Neither having the [ComplexTypeAttribute] nor using the modelBuilder.ComplexType() lines have solved these error messages, so I have to assume it is related to this being an immutable class now.

没错。 EF6 不映射仅获取属性,并且无法通过数据注释或流畅配置更改该行为(请注意,EF Core 中的行为不同,但 still causes issues),因此提供私有设置器将解决此问题:

[ComplexType] // optional
public class Address
{
    [Required]
    public string City { get; private set; }

    [Required]
    public string Country { get; private set; }

    [Required, StringLength(10, MinimumLength = 5)]
    public string PostalCode { get; private set; }

    [Required, StringLength(2, MinimumLength = 2)]
    public string State { get; private set; }

    [Required]
    public string StreetAddress { get; private set; }

    public string UnitNumber { get; private set; }

    // ...
}

现在,我知道这不等同于确保仅在构造期间设置字段的原始实现。但总的来说,我建议您在对 EF 实体建模时忘记 OO 原则、模式和实践。实体 类 基本上是 DTO,表示没有关联业务逻辑的数据库表、记录和关系。另请注意,EF 使用引用身份跟踪实体,因此对实体类型应用不可变原则只会给您带来问题(幸运的是,这不适用于复杂类型)。