Сложность в отношении EF Code First Fluent API, TPH и внешних ключей - PullRequest
3 голосов
/ 18 января 2012

У меня есть две таблицы в моей базе данных. Один называется Users, а другой - Widgets. Таблица Widgets представляет 3 объекта в моей модели кода. Одна из сущностей, Widget, является родительским классом для двух других сущностей, WidgetTypeA и WidgetTypeB. И WidgetTypeA, и WidgetTypeB имеют свойства навигации для объекта «Пользователь», который сохраняется в таблице «Пользователи» в базе данных. У меня возникают проблемы при получении Code First для использования одного и того же внешнего ключа для сущностей WidgetTypeA и WidgetTypeB (UserId). Кто-нибудь знает как это сделать? Похоже, что это должно быть распространенной проблемой с отображением таблицы на иерархию.

Мои классы сущностей таковы:

public class Widget
{
    public int Id { get; set; }

    public string Name { get; set; }
}

class WidgetMap : EntityTypeConfiguration<Widget>
{
    public WidgetMap()
    {
        ToTable("Widgets");

        HasKey(w => w.Id);
        Property(w => w.Id)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

        Property(w => w.Name)
            .IsRequired()
            .HasMaxLength(75)
            .IsUnicode(true);
    }
}

public class WidgetTypeA : Widget
{
    public int UserId { get; set; }
    public virtual User User { get; set; }

    public string Color { get; set; }
    public int DepthLevel { get; set; }
}

class WidgetTypeAMap : EntityTypeConfiguration<WidgetTypeA>
{
    public WidgetTypeAMap()
    {
        Map(w => w.Requires("WidgetTypeId").HasValue(1));

        HasRequired(w => w.User)
            .WithMany(u => u.WidgetTypeAs)
            .HasForeignKey(w => w.UserId)
            .WillCascadeOnDelete(false);

        Property(w => w.Color)
            .IsOptional()
            .IsUnicode(true)
            .HasMaxLength(75);

        Property(w => w.DepthLevel)
            .IsOptional();
    }
}

public class WidgetTypeB : Widget
{
    public int UserId { get; set; }
    public virtual User User { get; set; }
}

class WidgetTypeBMap : EntityTypeConfiguration<WidgetTypeB>
{
    public WidgetTypeBMap()
    {
        Map(w => w.Requires("WidgetTypeId").HasValue(2));

        HasRequired(w => w.User)
            .WithMany(u => u.WidgetTypeBs)
            .HasForeignKey(w => w.UserId)
            .WillCascadeOnDelete(false);
    }
}

public class User
{
    public int Id { get; set; }
    public string Username { get; set; }
    public int Age { get; set; }

    public virtual ICollection<WidgetTypeA> WidgetTypeAs { get; set; }
    public virtual ICollection<WidgetTypeB> WidgetTypeBs { get; set; }
}

class UserMap : EntityTypeConfiguration<User>
{
    public UserMap()
    {
        ToTable("Users");

        HasKey(u => u.Id);

        Property(u => u.Username)
            .IsRequired()
            .HasMaxLength(75)
            .IsUnicode(true);

        Property(u => u.Age)
            .IsRequired();
    }
}

Во всяком случае, я продолжаю получать ошибку

Неверное имя столбца 'UserId1'

при попытке выполнить следующие операции:

using (var entities = new MyEntities())
{
    User u = new User
    {
        Username = "Frank",
        Age = 14
    };
    entities.Users.Add(u);
    entities.SaveChanges();

    WidgetTypeA wa1 = new WidgetTypeA
    {
        Name = "0SDF81",
        UserId = u.Id,
        DepthLevel = 6
    };
    entities.WidgetTypeAs.Add(wa1);
    entities.SaveChanges();
}

Не уверен, можно ли это исправить или нет. Я всегда могу указать второй внешний ключ UserId для таблицы виджетов, но это кажется бессмысленным. Возможно, есть способ сделать это с помощью Fluent API?

Ответы [ 2 ]

2 голосов
/ 18 января 2012

Невозможно отобразить свойства, определенные в разных производных объектах, в один и тот же столбец.Это ограничение в EF.Если ваш WidgetTypeA имеет свойство UserId, а ваш WidgetTypeB имеет свойство UserId, они должны быть разными столбцами в базе данных.Это должно работать, если вы перемещаете свойства UserId и User из производных типов в родительский тип Widget.

0 голосов
/ 24 мая 2015

Я знаю, что уже поздно, но, надеюсь, может помочь другим читателям.

Хотя Ладислав был прав, что использование сопоставленного внешнего ключа не поддерживается в EF6, я нашел полезный обходной путь.

Можно определить спецификацию вычисляемого столбца, выражение которого просто ссылается на исходный столбец.Идентификатор пользователя в описании выше.Это может быть использовано как дискриминатор для отображения TPH.При таком подходе столбец не нужно сохранять, но его можно использовать для TPH, а исходный столбец доступен для использования в качестве внешнего ключа.

...