Как вы централизуете контекст данных Entity Framework в веб-приложении? - PullRequest
2 голосов
/ 19 июля 2011

В нашем приложении мы используем шаблон репозитория для извлечения и сохранения данных с нашего носителя данных. Средой, которую мы выбрали, является Entity Framework 4. Это, кажется, очень чистый способ сделать что-то, и он отлично работал в 99% случаев.

Теперь мы столкнулись с проблемой. У нас есть два хранилища, как это:

public class UserRepository : IUserRepository
{
    Entities dataContext = new Entities();

    public User GetUser(string username)
    {
        return dataContext.Users.SingleOrDefault(x => x.Username == username);
    }

    // ... more CRUD-style methods that are not relevant to this question.

    public void SaveChanges()
    {
        dataContext.SaveChanges();
    }
}

public RoleRepository : IRoleRepository
{
    Entities dataContext = new Entities();

    public Role GetRole(string name)
    {
        return dataContext.Roles.SingleOrDefault(x => x.Name == name);
    }

    // ... more CRUD-style methods that are not relevant to this question.

    public void SaveChanges()
    {
        dataContext.SaveChanges();
    }
}

Пользователи и роли на самом деле имеют отношение многие ко многим в модели Entity Framework. Иногда мы хотим взять существующего пользователя и существующую роль и связать их. Обычно это будет хорошо работать, если вы сделаете короткий пример кода, подобный этому:

Entities dataContext = new Entities();
Role roleToAdd = dataContext.Roles.Single(x => x.Name == "Admin");
User user = dataContext.Users.Single(x => x.Username == "Fred");
user.Roles.Add(roleToAdd);
dataContext.SaveChanges();

Это прекрасно работает, потому что оба объекта извлекаются из одного и того же объекта контекста данных EF. Однако в нашем приложении каждый репозиторий создает свой собственный объект контекста данных. Поэтому, когда мы пытаемся сделать то же самое, что и выше, с нашей собственной архитектурой:

UserRepository userRepo = new UserRepository();
RoleRepository roleRepo = new RoleRepository();
User user = userRepo.GetUser("Fred");
Role roleToAdd = roleRepo.GetRole("Admin");
user.Roles.Add(roleToAdd);
userRepo.SaveChanges();

Мы получаем эту ошибку:

Невозможно определить отношения между двумя объектами, поскольку они привязаны к разным объектам ObjectContext.

Каков наилучший способ централизовать этот контекст данных? Очевидно, я не хочу дублировать метод GetRole в UserRepository, потому что это было бы излишним и глупым. Я мог бы просто сделать более подробный метод в UserRepository, который принимает имя пользователя и имя пользователя, а затем использует тот же контекст данных для их извлечения и связывания, например:

public void AddUserToRole(string username, string role)
{
    User user = dataContext.Users.Single(x => x.Username == username);
    Role roleToAdd = dataContext.Roles.Single(x => x.Name == role);
    user.Roles.Add(roleToAdd);
}

Тогда я мог бы просто сделать:

userRepo.AddUserToRole("Fred", "Admin");
userRepo.SaveChanges();

Но разве это лучший способ сделать это? Есть ли лучший способ централизовать контекст данных EF для каждого запроса, чтобы все хранилища использовали один и тот же вместо создания своего собственного? Если да, то как бы я это сделал?

Ответы [ 3 ]

7 голосов
/ 19 июля 2011

Используйте инъекцию конструктора в хранилище для передачи контекста.

public class UserRepository : IUserRepository
{
    Entities dataContext;

    public UserRepository(Entities entities)
    {
       this.dataContext = entities;
    }

    public User GetUser(string username)
    {
        return dataContext.Users.SingleOrDefault(x => x.Username == username);
    }

    // ... more CRUD-style methods that are not relevant to this question.

    public void SaveChanges()
    {
        dataContext.SaveChanges();
    }
}

Скажите контейнеру DI запросить контекст контекста.

Например, с AutoFac вы бы:

builder.RegisterType<Entities>().InstancePerHttpRequest();
builder.RegisterType<UserRepository>().As<IUserRepository>().InstancePerHttpRequest();
builder.RegisterControllers(typeof(MvcApplication).Assembly);
0 голосов
/ 19 июля 2011

Лично я предпочитаю метод, в котором вы передаете имя пользователя и роль, и он имеет всю логику БД в хранилище для добавления пользователя в базу данных. Если вы вызываете этот метод 10 раз, вы не захотите получить роль и добавить ее к объекту пользователя в 10 разных местах в вашем приложении MVC.

Пусть хранилище сделает всю работу.

0 голосов
/ 19 июля 2011

У нас была точно такая же проблема.Вам следует использовать шаблон проектирования Unit of Work.Подробнее здесь: http://blogs.msdn.com/b/adonet/archive/2009/06/16/using-repository-and-unit-of-work-patterns-with-entity-framework-4-0.aspx

...