I've been searching already for hours and I can't seem to find the issue.
I am building an Entity Framework Fluent Api Code First TPH app. When I Add-Migration EF add's my "Type" column just fine, but it also adds a redundant Discriminator column (it should be overwritten by "Type"). I Use Map to specify the Type column name and possible values, this approach seems to work just fine for most of the domain models but this one gets a redundant second discriminator column and I can't seem to find the reason. Bond inherits from Asset in the domain model.
Heres my code:
public class BondConfiguration : EntityTypeConfiguration<Bond>
{
public BondConfiguration()
{
Property(b => b.IssueDate)
.HasColumnName("BondIssueDate")
.HasColumnType(DatabaseVendorTypes.TimestampField)
.IsRequired();
Property(b => b.MaturityDate)
.HasColumnName("BondMaturityDate")
.HasColumnType(DatabaseVendorTypes.TimestampField)
.IsRequired();
HasRequired(b => b.Currency).WithRequiredDependent();
Property(b => b.Coupon.Rate);
Property(b => b.Coupon.Amount);
Property(b => b.FaceValue)
.HasColumnName("BondFaceValue")
.IsRequired();
}
}
public class AssetConfiguration : EntityTypeConfiguration<Asset>
{
public AssetConfiguration()
{
Property(a => a.IsDeleted).HasColumnName("IsDeleted");
HasKey(a => a.Id);
ToTable("tbl_Asset");
Property(a => a.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)
.HasColumnName("AssetId");
Property(a => a.Name)
.HasColumnName("AssetName")
.IsRequired();
Property(a => a.Isin)
.HasColumnName("AssetISIN");
Map<Bond>(p => p.Requires("AssetClass").HasValue((int)AssetClass.Bond));
}
}
Domain Model:
public class Bond : Asset
{
public DateTime IssueDate { get; set; }
public DateTime MaturityDate { get; set; }
public BondCoupon Coupon { get; set; }
public Currency Currency { get; set; }
public decimal FaceValue { get; set; }
public IEnumerable<ValidationRule> SetCoupon(decimal amount, decimal rate)
{
var newCoupon = new BondCoupon
{
Rate = rate,
Amount = amount
};
if (Validate(new SetBondCouponValidator(newCoupon),out IEnumerable<ValidationRule> brokenRules))
{
Coupon = new BondCoupon
{
Rate = rate,
Amount = amount
};
}
return brokenRules;
}
}
public abstract class BaseAsset<T> : BaseEntity<T> where T : BaseEntity<T>, new()
{
public string Name { get; set; }
public string Isin { get; set; }
}
public class Asset : BaseAsset<Asset>, IEntityRoot
{
}
public class BaseEntity<T> where T : BaseEntity<T>, new()
{
public int Id { get; set; }
public bool IsDeleted { get; set; }
public bool Validate(IValidator validator, out IEnumerable<ValidationRule> brokenRules)
{
brokenRules = validator.GetBrokenRules();
return validator.IsValid();
}
}
You must be very careful when using any of EF6 inheritance. EF uses reflection to discover all classes in the same assembly which directly or indirectly inherit some of the entities which are part of EF inheritance and considers them being a part of the entity hierarchy, even if they are not used/referenced/configured anywhere from the EF model.
So just adding another class (in your real case it's called
Equity
)is enough to introduce the standard
Discriminator
column because it's not configured to use the discriminator column setup for theBond
class.This behavior is source of unexpected errors like yours and has been changed in EF Core where only the explicitly configured derived classes are considered.
In EF6, either mark such classes with
NotMapped
attribute, useIgnore
fluent API or properly map them as entity.