1

Я пытаюсь создать некоторые API-библиотеки классов классов, которые используют EF на моем уровне репозитория. Для любого из них для работы мне нужен класс dbcontext в каждой библиотеке классов. Но что происходит, когда мне нужен один класс для ссылки в каждом модуле? Возьмем, к примеру у меня есть модуль пользователей, чьи дб контексты включает в себя:Как обрабатывать один класс, используемый в нескольких контекстах базы данных для одной базы данных в EF-коде?

  • Пользователи
  • группы
  • Роли

Тогда у меня есть модуль месторасположении, который включает в себя:

  • Здания
  • Места проведения занятий
  • Номера

Тогда говорят, третий модуль для оборудования, которое имеет:

  • Оборудование
  • Типы оборудования
  • Работа Заказы

Последние два по-прежнему нуждаются ссылки на пользователей , что в значительной степени является неотъемлемой частью каждого модуля. Но я не могу добавить два отдельных пользовательских класса в два контекста, указывающих на один и тот же db, возможно, они могут перестать синхронизироваться. Таким образом, очевидное решение состоит только в том, что последние 2 модуля требуют пользовательский модуль, а любой класс в тех модулях, которые нужны пользователю, просто ссылается на идентификатор пользователя. Это нарушит нормализацию, хотя, поскольку это не будет внешний ключ, я не уверен, насколько хороша эта идея.

Другая возможность, которая появилась у меня в голове, заключалась в использовании интерфейса dbcontext каждого модуля и позволяет человеку, использующему модуль, объявить свой собственный dbcontext и реализовать всех этих членов, но я не уверен, что это сработает.

Я просто хочу создать коллекцию модулей библиотеки классов, которые определяют общий набор классов и вызовов API, доступных другим программистам, при использовании EF в качестве базы с намерением, чтобы все это было сохранено в одной БД. Но я не совсем уверен, как это сделать с тем, как работают DbContexts. Что происходит, когда у вас есть несколько модулей, требующих одного и того же объекта?

+0

Не уверен, что это именно то, что вам нужно, но увидеть статью Lehrman ссылки здесь : http://stackoverflow.com/questions/25454395/bounded-contexts-and-ef-code-first-how-to-put-them-together –

+0

Этот ответ выглядит интересным, но я могу предвидеть проблемы, когда они пытаются обновить миграции для каждого контекста. Возможно, что-то подобное работает. – SventoryMang

+0

Она обошла это с помощью «uber-model», упомянутой в статье. Другие контексты не выполняют миграции. –

ответ

2

Три контекста, которые вы представляете, как правило, соответствуют Bounded Contexts, как это определено в Domain-driven design, как указано в указателе Steeve.

Существует, очевидно, несколько способов реализации этого сценария, и каждый из них имеет свои плюсы и минусы.

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

Подход № 1: Soft-разделение

Я определяю User класс в первом ограниченном контексте и интерфейс, представляющий ссылку на пользователя во втором ограниченном контексте.

Давайте определим пользователь:

class User 
{ 
    [Key] 
    public Guid Id { get; set; } 

    public string Name { get; set; } 
} 

Другие модели, которые ссылаются пользователь реализует IUserRelated:

interface IUserRelated 
{ 
    [ForeignKey(nameof(User))] 
    Guid UserId { get; } 
} 

Шаблон проектирования рекомендует не напрямую связать два объекта из двух отдельных ограниченных контекстов, но магазин их соответствующую ссылку.

Building класс выглядит следующим образом:

class Building : IUserRelated 
{ 
    [Key] 
    public Guid Id { get; set; } 

    public string Location { get; set; } 
    public Guid UserId { get; set; } 
} 

Как вы можете видеть, Building модель знает только обращение в User. Тем не менее, интерфейс действует на внешний ключ и ограничивает значение, вставленное в это свойство UserId.

Давайте теперь определим ДБ контексты ...

class BaseContext<TContext> : DbContext where TContext : DbContext 
{ 
    static BaseContext() 
    { 
     Database.SetInitializer<TContext>(null); 
    } 

    protected BaseContext() : base("Demo") 
    { 

    } 
} 

class UserContext : BaseContext<UserContext> 
{ 
    public DbSet<User> Users { get; set; } 
} 

class BuildingContext : BaseContext<BuildingContext> 
{ 
    public DbSet<Building> Buildings { get; set; } 
} 

А контекст БД для инициализации базы данных:

class DatabaseContext : DbContext 
{ 
    public DbSet<Building> Buildings { get; set; } 
    public DbSet<User> Users { get; set; } 

    public DatabaseContext() : base("Demo") 
    { 
    } 
} 

И, наконец, код, который создает пользователя и здание:

// Defines some constants 
const string userName = "James"; 
var userGuid = Guid.NewGuid(); 

// Initialize the db 
using (var db = new DatabaseContext()) 
{ 
    db.Database.Initialize(true); 
} 

// Create a user 
using (var userContext = new UserContext()) 
{ 
    userContext.Users.Add(new User {Name = userName, Id = userGuid}); 
    userContext.SaveChanges(); 
} 

// Create a building linked to a user 
using (var buildingContext = new BuildingContext()) 
{ 
    buildingContext.Buildings.Add(new Building {Id = Guid.NewGuid(), Location = "Switzerland", UserId = userGuid}); 
    buildingContext.SaveChanges(); 
} 

подход № 2: Hard разделительного

Я определяю один класс User в каждом ограниченном контексте. Интерфейс обеспечивает общие свойства. Этот подход иллюстрируется Мартина Фаулера следующим образом:

enter image description here

пользователя ограниченный контекст:

public class User : IUser 
{ 
    [Key] 
    public Guid Id { get; set; } 

    public string Name { get; set; } 
} 

public class UserContext : BaseContext<UserContext> 
{ 
    public DbSet<User> Users { get; set; } 
} 

Строительство ограниченного контекста:

public class User : IUser 
{ 
    [Key] 
    public Guid Id { get; set; } 
} 

public class Building 
{ 
    [Key] 
    public Guid Id { get; set; } 

    public string Location { get; set; } 

    public virtual User User { get; set; } 
} 

public class BuildingContext : BaseContext<BuildingContext> 
{ 
    public DbSet<Building> Buildings { get; set; } 

    public DbSet<User> Users { get; set; } 
} 

В этом случае, это вполне приемлемо для имеют Users собственности в BuildingContext, так как пользователь существует в контексте здания.

Использование:

// Defines some constants 
    const string userName = "James"; 
    var userGuid = Guid.NewGuid(); 

    // Create a user 
    using (var userContext = new UserContext()) 
    { 
     userContext.Users.Add(new User { Name = userName, Id = userGuid }); 
     userContext.SaveChanges(); 
    } 

    // Create a building linked to a user 
    using (var buildingContext = new BuildingContext()) 
    { 
     var userReference = buildingContext.Users.First(user => user.Id == userGuid); 

     buildingContext.Buildings.Add(new Building { Id = Guid.NewGuid(), Location = "Switzerland", User = userReference }); 
     buildingContext.SaveChanges(); 
    } 

Игра с EF миграции очень легко. Сценарий миграции для пользователя, ограниченного контекста (сгенерированный EF):

public partial class Initial : DbMigration 
{ 
    public override void Up() 
    { 
     CreateTable(
      "dbo.Users", 
      c => new 
       { 
        Id = c.Guid(nullable: false), 
        Name = c.String(), 
       }) 
      .PrimaryKey(t => t.Id); 

    } 

    public override void Down() 
    { 
     DropTable("dbo.Users"); 
    } 
} 

сценария миграции для строительства ограниченного контекста (генерирует EF). Мне нужно удалить создание таблицы Users, поскольку другой ограниченный контекст несет ответственность за ее создание. Вы все еще можете проверить, если таблица не существует, прежде чем создавать его для модульного подхода:

public partial class Initial : DbMigration 
{ 
    public override void Up() 
    { 
     CreateTable(
      "dbo.Buildings", 
      c => new 
       { 
        Id = c.Guid(nullable: false), 
        Location = c.String(), 
        User_Id = c.Guid(), 
       }) 
      .PrimaryKey(t => t.Id) 
      .ForeignKey("dbo.Users", t => t.User_Id) 
      .Index(t => t.User_Id); 
    } 

    public override void Down() 
    { 
     DropForeignKey("dbo.Buildings", "User_Id", "dbo.Users"); 
     DropIndex("dbo.Buildings", new[] { "User_Id" }); 
     DropTable("dbo.Users"); 
     DropTable("dbo.Buildings"); 
    } 
} 

Нанести Upgrade-Database для двух контекстов и вы база данных готова!

EDIT для запроса OP о добавлении новых свойств в класс User.

Когда ограниченный контекст добавляет новое свойство классу User, он пошагово добавляет новый столбец под капотом. Он не переопределяет всю таблицу. Вот почему эта реализация очень универсальна.

Ниже приведен пример миграции сценария, где новое свойство Accreditation добавляется к классу User в ограниченном контексте Building:

public partial class Accreditation : DbMigration 
{ 
    public override void Up() 
    { 
     AddColumn("dbo.Users", "Accreditation", c => c.String()); 
    } 

    public override void Down() 
    { 
     DropColumn("dbo.Users", "Accreditation"); 
    } 
} 
+0

Проблема с этим подходом заключается в том, что по-прежнему требуется третий всеобъемлющий контекст «все в одном». для выполнения миграции. такого рода побеждает оценку на уровне модулей и выбор того, что вы хотите. Вам все равно придется создавать свой собственный контекст и вставлять в него все объекты. Это было бы не так уж плохо, за исключением того, что у вас уже были другие специфические контексты. – SventoryMang

+0

Эй @ DOTang, я отредактировал свой ответ, чтобы обеспечить подход * Hard-division *, который позволяет вам свободно играть с сценариями миграции EF. – ZenLulz

+0

Эй, это выглядит неплохо, но как бы вы справлялись с чем-то вроде того, что пользователь модуля здания имеет свойство и/или отношение, которое отсутствует в модуле «Пользователи»? Я предполагаю, что это будет работать, но вам придется запускать миграцию на строительный модуль после пользовательского модуля, но похоже, что он сломается, если у вас есть третий модуль, который также ссылается на пользователей, которым также требуются собственные специальные свойства для пользователя ? Что, в свою очередь, ответ должен был бы снова использовать огромный всеобъемлющий контекст? – SventoryMang

 Смежные вопросы

  • Нет связанных вопросов^_^