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
,但你仍然在 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>())
}
));
现在您已经将 DbContext
一直抽象到 IoC,理论上可以在 web.config 中更改 IoC,甚至无需重新构建。考虑因素?好吧,想想可读性和可维护性。我更喜欢真正抽象的层,而其他人会争辩说它没有必要,因为 EF 已经是一个工作单元模式。此外,可能会有性能开销,而不是像现在那样只创建 DbContext
。从更哲学的角度来看,有人可能会争辩说 DbContext 的抽象本身将是一个工作单元,因为它现在处于抽象层,SaveChanges()
可以是 "passed around" 就像一个工作单元.但我把讨论留给你...
大部分内容是手写的,但如果您决定也抽象 DbContext,我希望它能对您有所帮助。
编辑:
添加 SaveChanges() 到 IDbContext
.
几天前开始学习使用 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
,但你仍然在 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>())
}
));
现在您已经将 DbContext
一直抽象到 IoC,理论上可以在 web.config 中更改 IoC,甚至无需重新构建。考虑因素?好吧,想想可读性和可维护性。我更喜欢真正抽象的层,而其他人会争辩说它没有必要,因为 EF 已经是一个工作单元模式。此外,可能会有性能开销,而不是像现在那样只创建 DbContext
。从更哲学的角度来看,有人可能会争辩说 DbContext 的抽象本身将是一个工作单元,因为它现在处于抽象层,SaveChanges()
可以是 "passed around" 就像一个工作单元.但我把讨论留给你...
大部分内容是手写的,但如果您决定也抽象 DbContext,我希望它能对您有所帮助。
编辑:
添加 SaveChanges() 到 IDbContext
.