简体   繁体   English

存储库模式通用应用

[英]Repository Pattern universal application

When I started learning Repository Pattern with Unity few days ago I was under impression that the main benefit of this pattern is the separation of data layer from the business layer. 几天前,当我开始使用Unity学习存储库模式时,我印象深刻的是,这种模式的主要好处是将数据层与业务层分离。

In other words, if there is a need to change the way, how application stores the data, it's very easy as only one main model takes care of the communication. 换句话说,如果需要更改方式,应用程序如何存储数据,这很容易,因为只有一个主要模型负责通信。

This means, that if application currently saves data into a serialized XML files, it would not be very difficult to change this logic to connect to database instead. 这意味着,如果应用程序当前将数据保存到序列化的XML文件中,那么更改此逻辑以连接到数据库并不是很困难。

I have found few nice demos that are also using Unit Of Work layer, which seemed very handy. 我发现一些不错的演示也使用了Unit Of Work层,这似乎非常方便。 Let me show you a bit of the code I have. 让我向您展示一些我的代码。

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();
    }
}

Main Repository Context: 主要存储库上下文:

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

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

And here is the demo EmployeeRepository: 这是演示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; }
    }
}

Employee Repository derives from a generic Repository which looks like this: 员工存储库派生自一个通用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);
    }
}

Here is the question: 这是问题:

As far as my understanding goes, we are directly declaring, that under our Repository expects in it's constructor DbContext , which is afterwards used under all Add / Remove / Find functions under that particular class. 就我的理解而言,我们直接声明,在我们的Repository中,它需要构造函数DbContext ,该构造DbContext随后Add / Remove / Find在该特定类下的所有Add / Remove / Find函数中使用。

Currently this model is communicating with the database, but if I wanted (for whatever reason) to change this model to save data in the XML file, I would have to completely rewrite all my Repository classes? 当前,该模型正在与数据库通信,但是如果我出于某种原因想要更改此模型以将数据保存在XML文件中,则必须完全重写所有的Repository类吗? Or am I missing something here? 还是我在这里想念什么?

If I am wrong and it is easily doable, could anyone show me how to change the code so that we are serializing values into the XML files, please? 如果我错了并且很容易做到,请问有人可以告诉我如何更改代码,以便我们将值序列化为XML文件吗? I am trying to better understand this Repository Pattern, yet for now it's one big chaos for me. 我试图更好地理解这种存储库模式,但是现在对我来说这是一个很大的混乱。

Any help / suggestions regarding this matter would be highly appreciated. 关于此事的任何帮助/建议将不胜感激。

I'm reading the question as this: 我正在阅读这样的问题:

How can I abstract DbContext so there are no dependencies to it? 我如何抽象DbContext以便没有依赖关系?

I would abstract the context to an interface in an effort to embrace the 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; }
}

Then try to inject the IDbContext interface instead. 然后尝试注入IDbContext接口。 As mentioned in your comments you will probably need to rewrite parts of your repo anyway, but if the new datalayer can expose an IDbSet you should be able to simply change the implementation of IDbContext . 如评论中所述,您可能仍然需要重写部分仓库,但是如果新的数据层可以公开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);
    }
}

I would also look at the possibility to abstract the creating of the context in a separate class. 我还将研究在单独的类中抽象上下文创建的可能性。 As mentioned here: 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;
    }
}

This way you can inject IDbContextFactory into the Unit of work. 这样,您可以将IDbContextFactory注入到工作单元中。 Then you have abstracted the DbContext to the DbContextFactory , but you still have a dependency in DbContextFactory to DbContext . 然后,您已经将DbContext抽象到DbContextFactory ,但是在DbContextFactory仍然有对DbContext的依赖。 This will be enough for most people, but if you want to really go SOLID then you can abstract that as well with a generic IInstanceFactory . 这对于大多数人来说已经足够了,但是如果您想真正变成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();
        }
    }

And in my Unity registrations: 在我的Unity注册中:

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

Now you have abstracted the DbContext all the way to the IoC, which in theory can be changed in web.config without even re-building. 现在,您已经将DbContext一直抽象到IoC,理论上可以在web.config中对其进行更改,而无需重新构建。 Considerations? 考虑因素? Well, think about readability vs maintainability. 好吧,考虑一下可读性与可维护性。 I prefer a really abstracted layer, while others will argue that it's not necessary as EF already is a Unit of Work-pattern. 我更喜欢一个真正抽象的层,而其他人则认为这是没有必要的,因为EF已经是一个工作单元模式。 Also, there will probably be an performance overhead instead of just creating the DbContext as you do now. 另外,可能会产生性能开销,而不是像现在那样仅创建DbContext From a more philosophical point of view one may argue that the abstraction of DbContext will be a Unit of Work itself since it's now at the abstracted layer with a SaveChanges() that can be "passed around" just as a Unit of Work. 从更哲学的角度来看,有人可能会争辩说DbContext的抽象本身就是工作单元,因为它现在位于带有SaveChanges()的抽象层,可以像工作单元一样“传递”。 But i leave that discussion to you... 但是我把那个讨论留给你...

Most of this is wrote by hand, but I hope it will help you on the way, if you decide to abstract the DbContext as well. 其中大部分是手工编写的,但是如果您决定抽象DbContext,希望它能对您有所帮助。

EDIT: Added SaveChanges() to IDbContext . 编辑:添加SaveChanges()到IDbContext

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM