存储库模式通用应用 [英] Repository Pattern universal application

查看:69
本文介绍了存储库模式通用应用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

几天前,当我开始使用Unity学习存储库模式时,我印象深刻的是,这种模式的主要好处是将数据层与业务层分离。

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.

换句话说,如果需要改变方式,应用程序如何存储数据,这很容易,因为只有一个主要模型负责通信。

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.

这意味着,如果应用程序当前将数据保存到序列化的XML文件中,那么更改此逻辑以连接到数据库并不是很困难。

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.

我发现很少不错的演示,它们也使用工作单位图层,这似乎非常方便。让我向您展示我拥有的一些代码。

I have found few nice demos that are also using Unit Of Work layer, which seemed very handy. 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();
    }
}

主存储库上下文:

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

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

这是演示EmployeeRepository:

And here is the demo 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:

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

这是问题:

据我所知,我们直接声明,在我们的存储库下,期望它的构造函数 DbContext ,此后将在该特定类下的所有添加/删除/查找函数中使用。

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.

当前,该模型正在与数据库进行通信,但是如果我(出于某种原因)想要更改此模型以将数据保存在XML文件中,则必须完全重写我的所有存储库类?还是我在这里想念什么?

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? Or am I missing something here?

如果我错了并且很容易做到,请问有人可以告诉我如何更改代码,以便我们将值序列化为XML文件吗?我试图更好地理解这种存储库模式,但是现在对我来说这是一个大混乱。

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? 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.

推荐答案

我正在这样阅读问题:


我如何抽象DbContext,以使其没有依赖项?

How can I abstract DbContext so there are no dependencies to it?

我会尽力将上下文抽象到接口包含依赖反转原理

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

然后尝试注入 IDbContext 接口代替。如评论中所述,您可能仍然需要重写部分仓库,但是如果新数据层可以公开IDbSet,则您应该能够简单地更改 IDbContext 的实现。

Then try to inject the IDbContext interface instead. 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.

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

这样您可以注入 IDbContextFactory 进入工作单元。然后,您已经将DbContext抽象到 DbContextFactory ,但是您仍然在 DbContextFactory DbContext 。这对于大多数人来说就足够了,但是如果您想真正使用 SOLID 然后您也可以使用通用 IInstanceFactory 对其进行抽象。

This way you can inject IDbContextFactory into the Unit of work. Then you have abstracted the DbContext to the DbContextFactory, but you still have a dependency in DbContextFactory to 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.

    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注册中:

And in my Unity registrations:

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

现在您已经将 DbContext 全部IoC的方式,理论上可以在web.config中更改它,甚至无需重新构建。考虑因素?好吧,考虑一下可读性与可维护性。我更喜欢一个真正抽象的层,而其他人则认为这是没有必要的,因为EF已经是一个工作单元模式。另外,可能会产生性能开销,而不是像现在那样仅创建 DbContext 。从更哲学的角度来看,有人可能会说DbContext的抽象本身就是一个工作单元,因为它现在位于具有 SaveChanges()的抽象层。像工作单位一样绕过。但是我将讨论留给您...

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. 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. Also, there will probably be an performance overhead instead of just creating the DbContext as you do now. 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. 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.

编辑:
将SaveChanges()添加到 IDbContext

这篇关于存储库模式通用应用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆