Несколько контекстов базы данных при использовании шаблона хранилища

Я немного растерялся прямо сейчас ... Я никогда не видел столь разнородной информации относительно решения проблемы. Но давайте начнем с самого начала.

Я использую ASP.NET MVC с репозиториями, внедренными в контроллеры, благодаря Ninject. У меня есть две простые сущности: администратор со списком созданных записей в блоге и записи с одним виртуальным полем администратора.

Администратор:

public class Admin
{
    [Key, ScaffoldColumn(false)]
    public int Id { get; set; }

    [Required(ErrorMessage = "Zły login.")]
    [StringLength(20), MinLength(3)]
    [RegularExpression(@"^[a-zA-Z0-9]*$", ErrorMessage = "Special characters are not allowed.")]
    public string Login { get; set; }

    [Required(ErrorMessage = "Złe hasło.")]
    [StringLength(20, MinimumLength = 3)]
    [DataType(DataType.Password)]
    [Display(Name = "Hasło")]
    public string Password { get; set; }

    public virtual List<Entry> CreatedEntries { get; set; } // napisane aktualności przez danego admina
}

Вступление:

public class Entry
{
    [Key, ScaffoldColumn(false)]
    public int Id { get; set; }

    [StringLength(200, MinimumLength = 2)]
    [DataType(DataType.Text)]
    [Display(Name = "Tytuł")]
    public string Title { get; set; }

    [Required, StringLength(2000), MinLength(3)]
    [Display(Name = "Treść")]
    [UIHint("tinymce_jquery_full"), AllowHtml]
    public string Text { get; set; }

    public virtual Admin Admin { get; set; }
}

Вы, наверное, знаете, куда это идет, так как эта проблема ... "классическая" в stackoverflow.

В контроллере я хочу привязать один объект к другому:

entry.Admin = repAdmins.GetAdmin(User.Identity.Name);

repEntries.AddEntry(entry);

В хранилище:

public void AddEntry(Entry entry)
    {
        db.Entries.Add(entry);
        db.SaveChanges();
    }

Конечно, я не могу этого сделать из-за знаменитого «На объект сущности нельзя ссылаться несколькими экземплярами IEntityChangeTracker», что является результатом наличия отдельных контекстов базы данных в каждом хранилище.

Когда я искал решение, я уже знал, что, вероятно, лучший способ решить его - это использовать один общий контекст. И тогда я обнаружил образец Единицы Работы. Но вот когда начинаются настоящие проблемы.

На многих сайтах решение этой проблемы несколько иное.Репозитории должны иметь общий универсальный интерфейс (который я не хочу использовать, потому что мне не нужно, чтобы каждая операция CRUD выполнялась на каждом объекте, плюс иногда мне нужны дополнительные методы, такие как «IfExists» и т. Д.)На нескольких сайтах я читал, что вся эта абстракция не нужна, поскольку абстракция уже предоставляется Entity Framework, а UoW реализован в DbContext (что бы это ни значило)Шаблон «Единица работы» (по крайней мере, из примеров в Интернете) кажется мне настоящей болью ...

Мне нужно руководство ... Я изучаю ASP.NET MVC всего год. Мне кажется, что это «триумф формы над содержанием». Так как...Мне просто нужно привязать один объект к другому. Я начинаю думать, что было бы лучше, когда у меня просто был объект контекста в контроллере, и мне не нужно было строить Эйфелеву башню, чтобы достичь того, что упомянуто выше: \ Однако мне нравится идея репозиториев ...

Ответы на вопрос(2)

Решение Вопроса

как использовать один и тот же контекст в нескольких репозиториях. Чтобы упростить его, я не использовал интерфейсы и не использовал контейнер для внедрения зависимостей.

Класс контроллера:

public class HomeController : Controller
{
    Context context;
    AdminRepository adminRepository;
    EntryRepository entryRepository;

    public HomeController()
    {
        context = new Context();
        adminRepository = new AdminRepository(context);
        entryRepository = new EntryRepository(context);
    }
    // GET: Home
    public ActionResult Index()
    {
        string login = "MyLogin";
        Admin admin = adminRepository.GetAdmin(login);
        Entry entry = new Entry() { Admin = admin};
        entryRepository.AddEntry(entry);
        return View(entry);
    }
}

Хранилища:

public class AdminRepository
{
    Context context;
    public AdminRepository(Context context)
    {
        this.context = context;

        // This seeds the database
        Admin admin = new Admin() { Login = "MyLogin" };
        this.context.Admins.Add(admin);
        this.context.SaveChanges();
    }

    public Admin GetAdmin(string login)
    {
        return context.Admins.Where(a => a.Login == login).FirstOrDefault();
    }
}

public class EntryRepository
{
    Context context;
    public EntryRepository(Context context)
    {
        this.context = context;
    }

    public void AddEntry(Entry entry){
        context.Entrys.Add(entry);
        context.SaveChanges();
    }
}

Контекстный класс:

public class Context : DbContext
{
    public Context()
    {
        Database.SetInitializer<Context>(new DropCreateDatabaseAlways<Context>());
        Database.Initialize(true);
    }

    public DbSet<Admin> Admins { get; set; }
    public DbSet<Entry> Entrys { get; set; }
}

Модифицированные модели:

public class Admin
{
    public int Id { get; set; }
    public string Login { get; set; }

}

public class Entry
{
    public int Id { get; set; }
    public virtual Admin Admin { get; set; }
}
 Ap0st0l06 июн. 2016 г., 17:42
Спасибо за этот замечательный пример! Это должно быть показано в каждом уроке репозитория!

просто ответив на вопрос прямо. Проще говоря, ваш репозиторий должен воспринимать контекст как зависимость (он должен иметь конструктор, который принимает параметр типаDbContext). Ваш контекст должен управляться Ninject, а затем вводиться в ваш репозиторий и / или ваш контроллер. Таким образом, все всегда использует один и тот же контекст. Вы должны делать все это в области «запроса», чтобы контекст был специфичным для текущего запроса.

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

public interface ICreateable
{
}

public interface IUpdateable : ICreateable
{
}

Тогда ваша сущность, которую не следует обновлять, будет реализовывать толькоICreateable в то время как другие объекты (которые позволяют обновление) будут реализовыватьIUpdateable (который по наследованию интерфейса, также реализоватьICreateable). Наконец, вы бы добавили ограничения на ваши методы репозитория:

public void Create<TEntity>(TEntity entity)
    where TEntity : class, ICreateable

public void Update<TEntity>(TEntity entity>)
    where TEntity : class, IUpdateable

Поскольку рассматриваемая сущность реализует толькоICreatable, он не может быть использован в качестве параметра типа дляUpdateтак что тогда нет способа использовать этот метод.

Далее, совет не использовать шаблоны репозитория / UoW с Entity Framework действительно потому, что Entity Framework уже реализует эти шаблоны. Шаблон репозитория существует как способ хранения всей логики запросов к базе данных (конструирования операторов SQL и т. Д.) В одном месте. Это та абстракция, о которой мы здесь говорим. Другими словами, вместо непосредственного конструирования операторов SQL в коде приложения этот кодрассеянный прочь в хранилище. Однако это именно то, что делает Entity Framework, поэтому вам не нужно делать это снова. Паттерн «Единица работы» существует как метод для управления работой нескольких репозиториев, позволяя такие вещи, как транзакции. Однако опять же Entity Framework делает все это.

Единственная причина для добавления какой-либо дополнительной абстракции - это если вы хотите абстрагироваться от фактического провайдера, то есть от самой Entity Framework. Например, вы можете иметь такой интерфейсIRepository а затем создать реализации, такие какEntityFrameworkRepository, NHibernateRepository, WebApiRepositoryи т. д. Ваша заявка будет зависеть только отIRepositoryи вы могли бы затем переходить в другие реализации по мере необходимости. Если вы не собираетесь этого делать или вы всегда будете использовать Entity Framework, то вы могли бы просто использовать свой контекст напрямую. Любая дальнейшая абстракция - это просто что-то, что нужно поддерживать без какой-либо выгоды для вашего приложения.

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

 Ap0st0l06 июн. 2016 г., 17:43
Если бы я мог, я бы отметил ваш подробный ответ как решение ... Вы оба очень помогли мне в понимании! Спасибо!

Ваш ответ на вопрос