Entity Framework 将冗余的重复鉴别器列添加到 table
Entity Framework adds redundant duplicate discriminator column to table
我已经搜索了几个小时,但似乎找不到问题所在。
我正在构建一个 Entity Framework Fluent Api Code First TPH 应用程序。当我添加迁移 EF 添加我的 "Type" 列就好了,但它也添加了一个冗余的 Discriminator 列(它应该被 "Type" 覆盖)。我使用 Map 来指定 Type 列名称和可能的值,这种方法似乎适用于大多数领域模型,但是这个方法有一个多余的第二个鉴别器列,我似乎找不到原因。 Bond继承自领域模型中的Asset。
这是我的代码:
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));
}
}
领域模型:
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();
}
}
这是一个完整的非复制品。确保您的 EntityTypeConfigurations 在 OnModelCreating 中连接,并且在模型初始化时实际上是 运行。也不要用 "tbl_" 前缀命名表。
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration;
using System.Data.SqlClient;
using System.Linq;
namespace ConsoleApp8
{
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 abstract class BaseAsset<T> : BaseEntity<T> where T : new()
{
public string Name { get; set; }
public string Isin { get; set; }
}
public class Asset : BaseAsset<Asset>
{
}
public class BaseEntity<T> where T : new()
{
public int Id { get; set; }
public bool IsDeleted { get; set; }
}
public class BondConfiguration : EntityTypeConfiguration<Bond>
{
public BondConfiguration()
{
Property(b => b.FaceValue)
.HasColumnName("BondFaceValue")
.IsRequired();
}
}
public enum AssetClass
{
Bond = 1
}
public class AssetConfiguration : EntityTypeConfiguration<Asset>
{
public AssetConfiguration()
{
Property(a => a.IsDeleted).HasColumnName("IsDeleted");
HasKey(a => a.Id);
ToTable("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));
}
}
class Db : DbContext
{
public DbSet<Bond> Bonds { get; set; }
public DbSet<Asset> Assets { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new AssetConfiguration());
modelBuilder.Configurations.Add(new BondConfiguration());
}
}
class Program
{
static void Main(string[] args)
{
Database.SetInitializer(new DropCreateDatabaseAlways<Db>());
using (var db = new Db())
{
db.Database.Log = m => Console.WriteLine(m);
db.Database.Initialize(true);
}
Console.WriteLine("Hit any key to exit");
Console.ReadKey();
}
}
}
产出(部分):
CREATE TABLE [dbo].[Asset] (
[AssetId] [int] NOT NULL IDENTITY,
[AssetName] [nvarchar](max) NOT NULL,
[AssetISIN] [nvarchar](max),
[IsDeleted] [bit] NOT NULL,
[IssueDate] [datetime],
[MaturityDate] [datetime],
[BondFaceValue] [decimal](18, 2),
[AssetClass] [int],
CONSTRAINT [PK_dbo.Asset] PRIMARY KEY ([AssetId])
)
使用任何 EF6 继承时都必须非常小心。 EF 使用反射来发现同一程序集中的所有 classes,它们直接或间接继承了一些作为 EF 继承一部分的实体,并认为它们是实体层次结构的一部分,即使它们不是 used/referenced/configured EF 模型中的任何位置。
所以只需添加另一个 class(在您的真实情况下它称为 Equity
)
public Asset2 : Asset { }
足以引入标准 Discriminator
列,因为它未配置为使用 Bond
class.
的鉴别器列设置
这种行为是像您这样的意外错误的来源,并且已在 EF Core 中更改,其中仅考虑显式配置的派生 classes。
在 EF6 中,使用 NotMapped
属性标记此类 classes,使用 Ignore
fluent API 或将它们正确映射为实体。
我已经搜索了几个小时,但似乎找不到问题所在。
我正在构建一个 Entity Framework Fluent Api Code First TPH 应用程序。当我添加迁移 EF 添加我的 "Type" 列就好了,但它也添加了一个冗余的 Discriminator 列(它应该被 "Type" 覆盖)。我使用 Map 来指定 Type 列名称和可能的值,这种方法似乎适用于大多数领域模型,但是这个方法有一个多余的第二个鉴别器列,我似乎找不到原因。 Bond继承自领域模型中的Asset。
这是我的代码:
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));
}
}
领域模型:
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();
}
}
这是一个完整的非复制品。确保您的 EntityTypeConfigurations 在 OnModelCreating 中连接,并且在模型初始化时实际上是 运行。也不要用 "tbl_" 前缀命名表。
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration;
using System.Data.SqlClient;
using System.Linq;
namespace ConsoleApp8
{
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 abstract class BaseAsset<T> : BaseEntity<T> where T : new()
{
public string Name { get; set; }
public string Isin { get; set; }
}
public class Asset : BaseAsset<Asset>
{
}
public class BaseEntity<T> where T : new()
{
public int Id { get; set; }
public bool IsDeleted { get; set; }
}
public class BondConfiguration : EntityTypeConfiguration<Bond>
{
public BondConfiguration()
{
Property(b => b.FaceValue)
.HasColumnName("BondFaceValue")
.IsRequired();
}
}
public enum AssetClass
{
Bond = 1
}
public class AssetConfiguration : EntityTypeConfiguration<Asset>
{
public AssetConfiguration()
{
Property(a => a.IsDeleted).HasColumnName("IsDeleted");
HasKey(a => a.Id);
ToTable("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));
}
}
class Db : DbContext
{
public DbSet<Bond> Bonds { get; set; }
public DbSet<Asset> Assets { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new AssetConfiguration());
modelBuilder.Configurations.Add(new BondConfiguration());
}
}
class Program
{
static void Main(string[] args)
{
Database.SetInitializer(new DropCreateDatabaseAlways<Db>());
using (var db = new Db())
{
db.Database.Log = m => Console.WriteLine(m);
db.Database.Initialize(true);
}
Console.WriteLine("Hit any key to exit");
Console.ReadKey();
}
}
}
产出(部分):
CREATE TABLE [dbo].[Asset] (
[AssetId] [int] NOT NULL IDENTITY,
[AssetName] [nvarchar](max) NOT NULL,
[AssetISIN] [nvarchar](max),
[IsDeleted] [bit] NOT NULL,
[IssueDate] [datetime],
[MaturityDate] [datetime],
[BondFaceValue] [decimal](18, 2),
[AssetClass] [int],
CONSTRAINT [PK_dbo.Asset] PRIMARY KEY ([AssetId])
)
使用任何 EF6 继承时都必须非常小心。 EF 使用反射来发现同一程序集中的所有 classes,它们直接或间接继承了一些作为 EF 继承一部分的实体,并认为它们是实体层次结构的一部分,即使它们不是 used/referenced/configured EF 模型中的任何位置。
所以只需添加另一个 class(在您的真实情况下它称为 Equity
)
public Asset2 : Asset { }
足以引入标准 Discriminator
列,因为它未配置为使用 Bond
class.
这种行为是像您这样的意外错误的来源,并且已在 EF Core 中更改,其中仅考虑显式配置的派生 classes。
在 EF6 中,使用 NotMapped
属性标记此类 classes,使用 Ignore
fluent API 或将它们正确映射为实体。