2016-03-14 2 views
0

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

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

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

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

public class UnitOfWork : IUnitOfWork 
{ 
    private readonly RepositoryContext _context; 
    public IEmployeeRepository Employees { get; set; } 

    public UnitOfWork(RepositoryContext context) 
    { 
     _context = context; 
     Employees = new EmployeeRepository(_context); 
    } 


    public int Complete() 
    { 
     return _context.SaveChanges(); 
    } 

    public void Dispose() 
    { 
     _context.Dispose(); 
    } 
} 

Главная Repository Контекст:

public class RepositoryContext : DbContext 
{ 
    public RepositoryContext() : base("name=RepositoryContext") 
    { 
    } 

    public virtual DbSet<Employee> Employees { get; set; } 
    public virtual DbSet<Equipment> Furniture { get; set; } 
} 

А вот демо EmployeeRepository:

public class EmployeeRepository:Repository<Employee>, IEmployeeRepository 
{ 
    public EmployeeRepository(RepositoryContext context) : base(context) { } 

    public Employee GetEmployeeByName(string sName) 
    { 
     return MyContext.Employees.FirstOrDefault(n => n.Name == sName); 
    } 

    public RepositoryContext MyContext 
    { 
     get { return Context as RepositoryContext; } 
    } 
} 

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

public class Repository<T> : Interfaces.Repositories.IRepository<T> where T : class 
{ 
    protected readonly DbContext Context; 

    public Repository(DbContext context) 
    { 
     Context = context; 
    } 

    public void Add(T item) 
    { 
     Context.Set<T>().Add(item); 
    } 

    public IEnumerable<T> Find(Expression<Func<T, bool>> predicate) 
    { 
     return Context.Set<T>().Where(predicate); 
    } 

    public T Get(int ID) 
    { 
     return Context.Set<T>().Find(ID); 
    } 

    public IEnumerable<T> GetAll() 
    { 
     return Context.Set<T>().ToList(); 
    } 

    public void Remove(T item) 
    { 
     Context.Set<T>().Remove(item); 
    } 
} 

Вот вопрос:

Насколько я понимаю, идет, мы прямо заявляя, что в наших Repository предпологает в его конструктор DbContext, который впоследствии используется при всех Add/Remove/Find функций под конкретного класса.

В настоящее время эта модель взаимодействует с базой данных, но если бы я хотел (по какой-либо причине) изменить эту модель для сохранения данных в XML-файле, мне пришлось бы полностью переписать все мои классы Repository? Или я чего-то не хватает?

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

Любая помощь/предложения по этому вопросу были бы высоко оценены.

+1

Если вы изменили способ хранения данных, может потребоваться переписать класс репозитория (это, очевидно, ожидается), но не потребитель IRepository, так как интерфейс остается прежним, независимо от того, как хранятся данные. Поэтому вам не нужно менять бизнес-классы, это то, что называется «разделение уровня данных с бизнес-уровня». –

ответ

0

Я читаю вопрос, как это:

Как я отвлекаюсь DbContext поэтому нет никаких зависимостей к нему?

Я бы отвлек контекст к интерфейсу, чтобы обнять Dependency inversion principle.

public interface IDbContext : IDisposable 
{ 
    int SaveChanges(); 
    IDbSet<Employee> Employees { get; set; } 
    IDbSet<Equipment> Furniture { get; set; } 
} 

public class RepositoryContext : DbContext, IDbContext 
{ 
    public RepositoryContext() : base("name=RepositoryContext") 
    { 
    } 

    public virtual DbSet<Employee> Employees { get; set; } 
    public virtual DbSet<Equipment> Furniture { get; set; } 
} 

Затем попытаться придать интерфейс IDbContext вместо этого. Как уже упоминалось в ваших комментариях, вам, вероятно, потребуется переписать части вашего репо, но если новый datalayer может выставить IDbSet, вы сможете просто изменить реализацию IDbContext.

public class Repository<T> : Interfaces.Repositories.IRepository<T> where T : class 
{ 
    protected readonly IDbContext Context; 

    public Repository(IDbContext context) 
    { 
     Context = context; 
    } 

    public void Add(T item) 
    { 
     Context.Set<T>().Add(item); 
    } 

    public IEnumerable<T> Find(Expression<Func<T, bool>> predicate) 
    { 
     return Context.Set<T>().Where(predicate); 
    } 

    public T Get(int ID) 
    { 
     return Context.Set<T>().Find(ID); 
    } 

    public IEnumerable<T> GetAll() 
    { 
     return Context.Set<T>().ToList(); 
    } 

    public void Remove(T item) 
    { 
     Context.Set<T>().Remove(item); 
    } 
} 

Я также хотел бы рассмотреть возможность абстрактного создания контекста в отдельном классе. Как уже упоминалось здесь: Entity Framework using Repository Pattern, Unit of Work and Unity

public interface IDbContextFactory 
{ 
    IDbContext GetContext(); 
} 

public class DbContextFactory : IDbContextFactory 
{ 
    private readonly IDbContext _context; 

    public DbContextFactory() 
    { 
     _context = new MyDbContext("ConnectionStringName"); 
    } 

    public IDbContext GetContext() 
    { 
     return _context; 
    } 
} 

Таким образом, вы можете вводить IDbContextFactory в единицу работы. Затем вы отделили DbContext до DbContextFactory, но у вас все еще есть зависимость от DbContextFactory до DbContext. Этого будет достаточно для большинства людей, но если вы хотите по-настоящему пойти SOLID, тогда вы можете абстрагироваться, а также с общим IInstanceFactory.

public interface IDbContextFactory 
    { 
     /// <summary> 
     /// Creates a new context. 
     /// </summary> 
     /// <returns></returns> 
     IDbContext GenerateContext(); 

     /// <summary> 
     /// Returns the previously created context. 
     /// </summary> 
     /// <returns></returns> 
     IDbContext GetCurrentContext(); 
    } 

    public class DbContextFactory : IDbContextFactory 
    { 
     private readonly IInstanceFactory _instanceFactory; 
     private IDbContext _context; 

     public DbContextFactory(IInstanceFactory instanceFactory) 
     { 
      _instanceFactory = instanceFactory; 
     } 

     public IDbContext GenerateContext() 
     { 
      _context = _instanceFactory.CreateInstance<IDbContext>(); 
      return _context; 
     } 

     public IDbContext GetCurrentContext() 
     { 
      if (_context == null) 
       _context = GenerateContext(); 
      return _context; 
     } 
    } 

    /// <summary> 
    /// Creates an instance of a specific model. 
    /// </summary> 
    public interface IInstanceFactory 
    { 
     /// <summary> 
     /// Creates an instance of type T. 
     /// </summary> 
     T CreateInstance<T>(); 
    } 

    /// <summary> 
    /// Creates an instance based on the model defined by Unity. 
    /// </summary> 
    public class InstanceFactory : IInstanceFactory 
    { 
     private readonly IDictionary<Type, Func<object>> _funcs; 

     public InstanceFactory(IEnumerable<Func<object>> createFunc) 
     { 
      // To remove the dependency to Unity we will receive a list of funcs that will create the instance. 

      _funcs = new Dictionary<Type, Func<object>>(); 

      foreach (var func in createFunc) 
      { 
       var type = func.Method.ReturnType; 
       _funcs.Add(type, func); 
      } 
     } 

     /// <summary> 
     /// Creates an instance of T. 
     /// </summary> 
     /// <typeparam name="T"></typeparam> 
     /// <returns></returns> 
     public T CreateInstance<T>() 
     { 
      var func = _funcs[typeof(T)]; 
      return (T) func(); 
     } 
    } 

И в моих регистраций Unity:

container.RegisterType<IDbContext, YourDbContext>(new TransientLifetimeManager()); 
    container.RegisterType<IInstanceFactory, InstanceFactory>(
      new InjectionConstructor(new List<Func<object>> 
      { 
       new Func<IDbContext>(() => container.Resolve<IDbContext>()) 
      } 
     )); 

Теперь вы отведенной весь путь к IoC, который теоретически может быть изменена в web.config даже без повторного строительства DbContext. Вопросы? Ну, подумайте о читаемости и ремонтопригодности. Я предпочитаю действительно абстрагированный слой, в то время как другие утверждают, что это необязательно, поскольку EF уже является модулем Work-pattern. Кроме того, вероятно, будет накладные расходы на производительность, а не просто создание DbContext, как и сейчас. С более философской точки зрения можно утверждать, что абстракция DbContext будет самой Единицей работы, так как теперь она находится на абстрагированном слое с SaveChanges(), который может быть «пройден» так же, как Единица работы. Но я оставляю эту дискуссию вам ...

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

EDIT: Добавлено SaveChanges() для IDbContext.

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