Entity Framework: как избежать столбца дискриминатора из таблицы?
У меня есть следующая таблица, созданная с использованием подхода Entity Framework Code First.
- Как мне изменить код С#, чтобы в базе данных не был создан нежелательный столбец дискриминатора? Есть ли какие-либо атрибуты для достижения этого?
- Как сделать имя столбца внешнего ключа "PaymentID" вместо "Payment_ PaymentID"? Есть ли какие-либо атрибуты для достижения этого?
Примечание. Версия исполнения для EntityFramework.dll v4.0.30XXX
![enter image description here]()
CODE
public abstract class PaymentComponent
{
public int PaymentComponentID { get; set; }
public int MyValue { get; set; }
public string MyType { get; set; }
public abstract int GetEffectiveValue();
}
public partial class GiftCouponPayment : PaymentComponent
{
public override int GetEffectiveValue()
{
if (MyValue < 2000)
{
return 0;
}
return MyValue;
}
}
public partial class ClubCardPayment : PaymentComponent
{
public override int GetEffectiveValue()
{
return MyValue;
}
}
public partial class Payment
{
public int PaymentID { get; set; }
public List<PaymentComponent> PaymentComponents { get; set; }
public DateTime PayedTime { get; set; }
}
//System.Data.Entity.DbContext is from EntityFramework.dll
public class NerdDinners : System.Data.Entity.DbContext
{
public NerdDinners(string connString): base(connString)
{
}
protected override void OnModelCreating(DbModelBuilder modelbuilder)
{
modelbuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
public DbSet<GiftCouponPayment> GiftCouponPayments { get; set; }
public DbSet<ClubCardPayment> ClubCardPayments { get; set; }
public DbSet<Payment> Payments { get; set; }
}
КЛИЕНТ
static void Main(string[] args)
{
string connectionstring = "Data Source=.;Initial Catalog=NerdDinners;Integrated Security=True;Connect Timeout=30";
using (var db = new NerdDinners(connectionstring))
{
GiftCouponPayment giftCouponPayment = new GiftCouponPayment();
giftCouponPayment.MyValue=250;
giftCouponPayment.MyType = "GiftCouponPayment";
ClubCardPayment clubCardPayment = new ClubCardPayment();
clubCardPayment.MyValue = 5000;
clubCardPayment.MyType = "ClubCardPayment";
List<PaymentComponent> comps = new List<PaymentComponent>();
comps.Add(giftCouponPayment);
comps.Add(clubCardPayment);
var payment = new Payment { PaymentComponents = comps, PayedTime=DateTime.Now };
db.Payments.Add(payment);
int recordsAffected = db.SaveChanges();
}
}
Ответы
Ответ 1
Наследование TPH требуется специальный столбец, который используется для идентификации типа объекта. По умолчанию этот столбец называется Discriminator
и содержит имена производных объектов. Вы можете использовать Fluent-API для определения имени столбца и разных значений. Вы также можете использовать свой столбец MyType напрямую, потому что он на самом деле является дискриминатором, но в таком случае вы не можете иметь этот столбец в своей сущности (столбец может отображаться только один раз, а если вы используете его как дискриминатор, он уже рассматривается как сопоставление).
Имя столбца внешнего ключа можно снова контролировать с помощью Fluent-API:
protected override void OnModelCreating(DbModelBuilder modelbuilder)
{
modelbuilder.Conventions.Remove<PluralizingTableNameConvention>();
// Example of controlling TPH iheritance:
modelBuilder.Entity<PaymentComponent>()
.Map<GiftPaymentComponent>(m => m.Requires("MyType").HasValue("G"))
.Map<ClubPaymentComponent>(m => m.Requires("MyType").HasValue("C"));
// Example of controlling Foreign key:
modelBuilder.Entity<Payment>()
.HasMany(p => p.PaymentComponents)
.WithRequired()
.Map(m => m.MapKey("PaymentId"));
}
Ответ 2
Добавить атрибут [NotMapped], если свойство не будет отображаться в столбце.
Ответ 3
Также можно использовать таблицу для каждого типа (TPT).
http://weblogs.asp.net/manavi/inheritance-mapping-strategies-with-entity-framework-code-first-ctp5-part-2-table-per-type-tpt
Таблица для каждого типа (TPT)
Таблица для каждого типа представляет собой представление отношений наследования как реляционные ассоциации внешних ключей. Каждый класс/подкласс, который объявляет постоянные свойства, включая абстрактные классы, имеет свои собственные Таблица. Таблица для подклассов содержит столбцы только для каждого noninherited property (каждое свойство, объявленное самим подклассом) наряду с первичным ключом, который также является внешним ключом базового класса таблица.
Внедрение TPT в коде EF First
Мы можем создать сопоставление TPT, просто разместив атрибут Table на подклассы для указания отображаемого имени таблицы (атрибут таблицы - новый аннотации данных и была добавлена к Пространство имен System.ComponentModel.DataAnnotations в CTP5.
Если вы предпочитаете свободный API, то вы можете создать сопоставление TPT, используя Метод ToTable():
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<BankAccount>().ToTable("BankAccounts");
modelBuilder.Entity<CreditCard>().ToTable("CreditCards");
}
Ответ 4
Поскольку вы используете подклассы, столбцу Discriminator необходимо различать каждый тип ваших подклассов.
Ответ 5
Поскольку и "GiftCouponPayment", и "ClubCardPayment" происходит от "PaymentComponent", EF не будет использовать отдельные таблицы и будет нуждаться в этом столбце. Если вам нужно другое поведение, вам придется переопределить доступ к таблице по умолчанию и сопоставить поля с вашими классами (что, я думаю, вы не хотите делать). Не уверен, что есть простой способ этого. Сначала из сущности я знаю, что есть путь через шаблон, который создает таблицы.
То же самое для имени столбца внешнего ключа. EF использует этот способ создания имени для имен ключевых/чужих ключей. Если вы хотите отформатировать таблицу по своему усмотрению, вам нужно все это сделать самостоятельно, что приводит к вопросу, почему именно использовать EF.
Есть ли какая-то особая причина, почему вы хотите это сделать, кроме косметики?
Ответ 6
Пример кода для удаления столбца дискриминатора и получения столбца с именем PaymentId как дискриминатора вместо этого, поэтому решение обоих вопросов. На основе исходной документации Microsofts Fluent Api.
https://msdn.microsoft.com/en-us/library/jj591617%28v=vs.113%29.aspx?f=255&MSPPError=-2147217396
public enum MyEnum
{
Value1, Value2
}
public class MyBaseClass
{
[NotMapped]
public MyEnum PaymentId { get; protected set; }
}
public class DerivedOne: MyBaseClass
{
public DerivedOne()
{
PaymentId = MyEnum.Value1;
}
}
public class DerivedTwo: MyBaseClass
{
public DerivedTwo()
{
PaymentId = MyEnum.Value2;
}
}
public class MyDbContext : DbContext
{
DbSet<MyBaseClass> MyBaseClass { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<MyBaseClass>()
.Map<DerivedOne>(x => x.Requires("PaymentId").HasValue((int)PaymentId.Value1))
.Map<DerivedTwo>(x => x.Requires("PaymentId").HasValue((int)PaymentId.Value2));
}
}