Таблица на иерархию и составной первичный ключ

У меня есть две таблицы в устаревшей базе данных (которую я не могу изменить) со следующими данными:

две старые таблицы

Таблица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.