Таблица на иерархию и составной первичный ключ
У меня есть две таблицы в устаревшей базе данных (которую я не могу изменить) со следующими данными:
Таблица1 имеет составной первичный ключ (код, аббревиатура), но аббревиатура также используется в качестве дискриминатора (см. Ниже). Таблица 2 имеет два столбца внешнего ключа (CodeA, CodeB), оба ссылаются на одно и то же поле Code в Таблице 1. В поле Table1.Code есть дубликаты.
Я хотел бы использовать подход "таблица на иерархию" с Entity Framework 6. Итак, я создал следующие классы моделей:
[Table("Table1")]
public class MyBaseClass
{
[Key]
public string Code { get; set; }
}
public class MyBaseClassA : MyBaseClass
{
}
public class MyBaseClassB: MyBaseClass
{
}
[Table("Table2")]
public class SubClass
{
[Key]
public int Id { get; set; }
[Required]
[ForeignKey("MyBaseClassA")]
public string CodeA { get; set; }
public virtual MyBaseClassA ClassA { get; set; }
[Required]
[ForeignKey("MyBaseClassB")]
public string CodeA { get; set; }
public virtual MyBaseClassB ClassB { get; set; }
}
Я определил таблицу на иерархию в своем классе DataContext: DbContext следующим образом:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<MyBaseClass>().Map<MyBaseClassA>(m => m.Requires("Abbrev").HasValue("A"))
.Map<MyBaseClassB>(m => m.Requires("Abbrev").HasValue("B"));
}
Проблема в том, что когда я хочу использовать такое сопоставление - я не могу использовать поле дискриминатора (Table1.Abbrev) как часть составного ключа в MyBaseClass - я получаю следующую ошибку:
Все объекты в EntitySet DataContext.MyBaseClass должны иметь уникальные первичные ключи. Однако экземпляр типа "MyBaseClassA" и экземпляр типа "MyBaseClassB" имеют одинаковое значение первичного ключа: "EntitySet=MyBaseClass;Code=1".
Можно ли отобразить модель выше с Entity Framework 6 (или новее)?
2 ответа
Боюсь, что это невозможно с Entity Framework.
Для начала вы должны сопоставить полный ключ Table1
потому что EF не может идентифицировать Table1
объекты по Code
только. И дискриминатор, который является частью составного первичного ключа, просто не поддерживается.
Так что вы не можете подтип Table1
, Теперь, если это все, вы можете не использовать наследование. Но Table2
настоящий демпфер EF требует, чтобы внешние ключи ссылались на полный первичный ключ. Итак, с Table1
должен иметь составной ключ, Table2
ключ двух посторонних также должен выглядеть { Code, Abbrev }
, Ну, нет даже одного Abbrev
поле в Table2
,
Единственное, что вы можете сделать, это карта Table1
как есть (без наследования) а также Table2
без какой-либо связи между ними. Вам придется вручную писать объединения (своего рода), чтобы получить связанные записи из базы данных в одном запросе.
Например, чтобы получить Table2
с Table1
как A
:
from t1 in context.Table1s
join t2 in context.Table2s on t1.Code equals t2.CodeA
where t1.Abbrev == "A"
select new { A = t1, t2 }
Или Table2
с обоими Table1
как A
и Table1
как B
:
from t2 in context.Table2s
select new
{
t2,
A = (from t1 in context.Table1s
where t1.Code == t2.CodeA && t1.Abbrev == "A")
.FirstOrDefault(),
B = (from t1 in context.Table1s
where t1.Code == t2.CodeB && t1.Abbrev == "B")
.FirstOrDefault(),
}
Создайте новую таблицу, в которой первичным ключом является столбец "Аббревиатура". (Эта таблица будет иметь только две строки в вашем примере со значениями столбцов "Abbrev" "A" и "B".) Затем определите отношение внешнего ключа между этой новой таблицей и существующей таблицей Table1. В коде обновите MyBaseClass для Table1, добавив столбец "Abbrev" как часть существующего определения первичного ключа.
Это должно устранить ошибку "должны иметь уникальные первичные ключи", сгенерированную MyBaseClass.