Entity Framework: совместное использование сущностей в разных DbContexts

Я разрабатываю приложение-плагин с EF6, сначала код.

У меня есть один основной контекст с сущностью под названием User:

public class MainDataContext : DbContext
{
    public MainDataContext(): base("MainDataContextCS") {}
    public DbSet<User> Users { get; set; }
}

А затем еще один контекст для PluginX в другом проекте, который ссылается на базовый:

public class PluginDataContext : DbContext
{
    public PluginDataContext () : base("MainDataContextCS") {
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder) {
        modelBuilder.HasDefaultSchema("PluginX");
        base.OnModelCreating(modelBuilder);
    }

    public DbSet<Booking> Bookings { get; set; }
}

И это аккуратно создает в той же базе данных (та же строка подключения) таблицу PluginX.Bookings.

Проблема здесь в том, что объект Booking содержит ссылку на объект User:

public class Booking
{
    public int Id { get; set;}
    public virtual User CreationUser { get; set;}
    public BookingStatus Status { get; set; }
}

И при запуске Add-Migration для контекста плагина EF попытается создать еще один объект User с именем PluginX.User.

Как это решить? Есть ли способ поделиться общим объектом в другом DbContext?


comment
почему бы не наследовать PluginContext от MainContext.   -  person hazimdikenli    schedule 16.01.2015


Ответы (4)


При работе с несколькими контекстами у вас есть два варианта:

  1. Относитесь к каждому контексту так, как будто это отдельные приложения. Представьте, что ваш пользователь — это внешний ресурс, который вы получаете от веб-службы. Вы не сможете добавить к этому внешний ключ. Что бы вы сделали в этом случае, так это либо добавить только userId в свои таблицы, и когда вам нужны данные пользователя, вызовите внешнюю службу, чтобы получить их, или иметь локальную легкую копию пользователя в контексте Bookings, которую вы будете обновлять время от времени из контекста пользователей. Этот подход хорош, когда вы работаете с большой системой и хотите изолировать ее части (читайте о DDD и ограниченных контекстах).
  2. Помимо двух контекстов, создайте третий контекст со всей моделью (пользователи, заказы и т. д.). Вы будете использовать полный контекст для создания миграций и поддержки структуры БД, но в приложении вы будете использовать меньшие контексты. Это очень простое решение. Миграции легко поддерживать с помощью одного контекста, и это по-прежнему позволяет вам изолировать операцию БД в небольших контекстах, у которых нет доступа к несвязанным сущностям.
person Francesc Castells    schedule 27.11.2015
comment
Это также помогло мне подумать о моей модели и попытаться денормализовать определенные части моего приложения. - person Steven Ryssaert; 07.04.2019
comment
Привет, Франсеск, какие-нибудь изменения в этом подходе? С EF Core 3.0 по-прежнему невозможно связывать объекты в разных контекстах? - person liqSTAR; 06.10.2020

При добавлении объекта Booking не используйте метод DbSet.Add(). Вместо этого используйте метод DbSet.Attach() и установите для свойства DbContext.Entry(Entity).State для бронирования значение EntityState.Added и убедитесь, что DbContext.Entry(Entity).State для пользователя остается EntityState.Unchanged.

Так, например, вместо этого:

pluginDataContext.dbBooking.Add(myNewBooking);

Сделай это:

pluginDataContext.dbBooking.Attach(myNewBooking);
pluginDataContext.Entry(myNewBooking).State = EntityState.Added;

Это связано с тем, что метод Add() помечает все объекты в графе объектов как EntityState.Added, что приведет к вставкам без проверки того, существует ли объект уже в базе данных. Метод Attach() просто заставляет контекст начать отслеживать объект.

Вот почему я почти никогда не использую DbSet.Add().

person JC Ford    schedule 15.01.2015

Вы можете попробовать использовать представления, объявить пользователя как представление в PluginDataContext и при выполнении миграции ввести метод "создать представление пользователя как...", это позволит связать книгу с пользователем.

person Juan Jimenez Cervantes Frigols    schedule 09.03.2020

Это решение может вам помочь:Первая миграция кода Entity Framework 6 с несколькими контекстами данных. Однако в этом случае оба контекста находятся в одном проекте. Я не знаю, работает ли с контекстами, которые находятся в двух разных проектах (я думаю, что это должно быть, если вы используете один и тот же класс для сопоставления пользователя). Как сказано в блоге, вам нужно прокомментировать сгенерированный код, связанный с таблицей Users, когда вы запускаете команду Add-Migration для контекста PluginX.

person octavioccl    schedule 15.01.2015
comment
Я знаю, что это старо, но... Я не мог перестать думать, что это решение совсем не лучшее. Каждый раз, когда добавляется новая миграция, вы должны комментировать код? Звучит как огромный взлом для меня. - person jpgrassi; 27.06.2019