存储库模式通用应用程序

时间:2016-03-14 18:34:30

标签: c# entity-framework unity-container repository-pattern

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

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

这意味着,如果应用程序当前将数据保存到序列化的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函数下使用。

目前这个模型正在与数据库进行通信,但如果我想(无论出于何种原因)更改此模型以将数据保存在XML文件中,我将不得不完全重写所有Repository类?或者我在这里遗漏了什么?

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

有关此事的任何帮助/建议都将受到高度赞赏。

1 个答案:

答案 0 :(得分: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接口。正如您的评论中所提到的,您可能还需要重写部分repo,但如果新数据层可以公开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,但您仍然在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中进行更改,甚至不需要重新构建。注意事项?好吧,考虑可读性和可维护性。我更喜欢一个真正抽象的层,而其他人则认为它不是必需的,因为EF已经是一个工作单元模式。此外,可能会有性能开销,而不是像现在一样创建DbContext。从更哲学的角度来看,人们可能会争辩说DbContext的抽象将是一个工作单元本身,因为它现在处于抽象层,SaveChanges()可以传递&#34; 34;就像一个工作单元。但是我把这个讨论留给你了......

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

修改 将SaveChanges()添加到IDbContext