I was refactoring a working implementation of a Complex Type class to apply the Value Object pattern which would make it an immutable type.
I got that working, I had thought, until I tried to set up the migrations for these changes (the main change that I think would matter to EF is that I changed some properties over to being get-only where they previously had setters). I am currently getting the following errors when I run 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.
This would appear to be defying what I have been reading about how EF handles Complex Types, in that it should map things automatically whenever it encounters entities which make use of a property that is a Complex Type. This was previously the case, however it is no longer working with the change to an immutable class. I am not sure why that is.
Here are the relevant classes pertaining to these error messages:
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; }
}
And, of course, the all-important Address class, which is now causing problems!
[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);
}
}
I have tried to do this mapping manually, in the same vein that I believe the EFCF Convention is - Address_PropertyName in the migrations file that EF does try to create when I run Add-Migration, but this did not work either.
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.
I had hoped that adding the [ColumnAttribute] with column names would solve the issue somehow, as that seemed to be indicated in this post, but that did not solve the issue either.