Repository Pattern 通用应用

Repository Pattern universal application

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

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

这意味着,如果应用程序当前将数据保存到序列化的 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();
    }
}

主存储库上下文:

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

Employee 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 函数下使用class.

目前此模型正在与数据库通信,但如果我想(出于任何原因)更改此模型以将数据保存在 XML 文件中,我将不得不完全重写我所有的 Repository class是吗?还是我在这里遗漏了什么?

如果我错了而且很容易做到,谁能告诉我如何更改代码以便我们将值序列化到 XML 文件中,好吗?我正在努力更好地理解这个存储库模式,但现在它对我来说是一大混乱。

如有任何关于此事的帮助/建议,我们将不胜感激。

我正在阅读这个问题:

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

我会将上下文抽象为接口,以努力拥抱 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.

的实现
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);
    }
}

我还会考虑在单独的 class 中抽象上下文创建的可能性。如此处所述: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,但你仍然在 DbContextFactoryDbContext 中有依赖关系。这对大多数人来说已经足够了,但是如果你真的想要 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>())
            }
        ));

现在您已经将 DbContext 一直抽象到 IoC,理论上可以在 web.config 中更改 IoC,甚至无需重新构建。考虑因素?好吧,想想可读性和可维护性。我更喜欢真正抽象的层,而其他人会争辩说它没有必要,因为 EF 已经是一个工作单元模式。此外,可能会有性能开销,而不是像现在那样只创建 DbContext 。从更哲学的角度来看,有人可能会争辩说 DbContext 的抽象本身将是一个工作单元,因为它现在处于抽象层,SaveChanges() 可以是 "passed around" 就像一个工作单元.但我把讨论留给你...

大部分内容是手写的,但如果您决定也抽象 DbContext,我希望它能对您有所帮助。

编辑: 添加 SaveChanges() 到 IDbContext.