Три контекста, которые вы представляете, как правило, соответствуют 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
в каждом ограниченном контексте. Интерфейс обеспечивает общие свойства. Этот подход иллюстрируется Мартина Фаулера следующим образом:
пользователя ограниченный контекст:
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");
}
}
Не уверен, что это именно то, что вам нужно, но увидеть статью Lehrman ссылки здесь : http://stackoverflow.com/questions/25454395/bounded-contexts-and-ef-code-first-how-to-put-them-together –
Этот ответ выглядит интересным, но я могу предвидеть проблемы, когда они пытаются обновить миграции для каждого контекста. Возможно, что-то подобное работает. – SventoryMang
Она обошла это с помощью «uber-model», упомянутой в статье. Другие контексты не выполняют миграции. –