什么是IRepository?为什么使用它,简短而简单的例子不会受到伤害。
答案 0 :(得分:46)
MVC促进了关注点的分离,但这并不止于M V C级别。
数据访问本身就是一个问题。它应该在MVC的M位,即模型中完成。你如何构建你的模型取决于你,但人们通常会遵循久经考验的模式(为什么重新发明轮子?)。存储库模式是当前的标准。但是,不要指望一个简单的公式,因为差异与开发人员一样多,差不多。
IRepository只是您创建的接口(它不是MVC或ASP.NET或.NET的一部分)。它允许您将存储库与实际实现“分离”。解耦很好,因为它意味着你的代码......:
所以,在卖掉了你的解耦之后,你的问题的答案是IRepository是你创建的一个界面,你让你的存储库继承。它为您提供了可靠的类层次结构。
我通常使用通用的IRepository。即:
IRepository
Tentity是一个实体。我使用的代码是:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Wingspan.Web.Mvc
{
public interface IRepository<TEntity> where TEntity : class
{
List<TEntity> FetchAll();
IQueryable<TEntity> Query {get;}
void Add(TEntity entity);
void Delete(TEntity entity);
void Save();
}
}
此接口的具体实现是:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Linq;
using Wingspan.Web.Mvc;
namespace ES.eLearning.Domain
{
public class SqlRepository<T> : IRepository<T> where T : class
{
DataContext db;
public SqlRepository(DataContext db)
{
this.db = db;
}
#region IRepository<T> Members
public IQueryable<T> Query
{
get { return db.GetTable<T>(); }
}
public List<T> FetchAll()
{
return Query.ToList();
}
public void Add(T entity)
{
db.GetTable<T>().InsertOnSubmit(entity);
}
public void Delete(T entity)
{
db.GetTable<T>().DeleteOnSubmit(entity);
}
public void Save()
{
db.SubmitChanges();
}
#endregion
}
}
这允许我写:
SqlRepository<UserCourse> UserCoursesRepository = new SqlRepository<UserCourse>(db);
其中db是一个注入到服务中的DataContext实例。
使用UserCoursesRepository我现在可以在我的Service类中编写方法,如:
public void DeleteUserCourse(int courseId)
{
var uc = (UserCoursesRepository.Query.Where(x => x.IdUser == UserId && x.IdCourse == courseId)).Single();
UserCoursesRepository.Delete(uc);
UserCoursesRepository.Save();
}
现在在我的控制器中,我可以写:
MyService.DeleteUserCourse(5);
MyService.Save();
即,您的应用程序的开发变得更像是一个装配线,导致一个非常简单的控制器。装配线的每一部分都可以独立于其他任何部分进行测试,因此虫子被扼杀在萌芽状态。
如果这是一个漫长而笨拙的回答,那是因为真正的答案是:
购买Steven Sanderson的书Pro ASP.NET MVC 2 Framework并学会在MVC中思考。
答案 1 :(得分:14)
IRepository
是您希望实现存储库模式时指定的接口。正如@Brian Ball所说,它不是.NET的一部分,它是您创建的界面。
使用存储库模式的开发人员广泛建议使用接口来实现。例如,在我正在开发的应用程序中,我有5个存储库。 4具体和1通用。每一个都继承自IRepository
,这可以确保我在实施过程中不会出现问题。
就代码示例而言,我会尝试:
interface IRepository<T> where T : class {
IQueryable<T> Select();
}
作为通用存储库实现:
public class Repository<T> : IRepository<T> where T : class {
public IQueryable<T> Select() {
return this.ObjectContext.CreateObjectSet<T>();
}
}
作为专业存储库实现:
public class EmployeeRepository : IRepository<Employee> {
public IQueryable<Employee> Select() {
return this.ObjectContext.Employees;
}
}
Repository<T>
和EmployeeRepository
都实现IRepository
,但是他们执行查询的方式略有不同。通用存储库必须先创建一个T对象集,然后才能尝试执行任何操作。
请记住,Repository<T>
应该被锁定到接口,而EmployeeRepository
可以实现更专业的方法来完成更复杂的逻辑。
我希望这对你有所帮助。
答案 2 :(得分:3)
IRepository
不是.Net框架中的已定义类型。通常,当您看到名为的接口时,程序将使用存储库模式(https://web.archive.org/web/20110503184234/http://blogs.hibernatingrhinos.com/nhibernate/archive/2008/10/08/the-repository-pattern.aspx)。通常,当人们使用此模式时,他们将创建一个所有存储库都遵循的接口。这样做有很多好处。一些好处是代码反堆积和单元测试。
通常这样做也可以利用IoC(http://en.wikipedia.org/wiki/Inversion_of_control)。
答案 3 :(得分:2)
存储库是抽象,它表示任何底层和任意数据存储,就好像它是内存中的对象集合。
由于常见做法和系统限制,此定义变为更实用的形式,因为内存中的对象集合代表一些底层和任意数据存储,可能是断开连接的。在引擎盖下,存储库可以链接到数据库,平面文件,内存中的对象集合或者您可能想到的任何其他内容。存储库的用户不关心。
所以IRepository
是接口契约,它定义了Api代码希望客户端代码如何与存储库交互。这通常包括添加,更新,删除和获取合同,例如,存储库合同的这个非常常见的示例:
public interface IRepository<TEntity> where TEntity : class
{
List<TEntity> GetAll();
void Add(TEntity entity);
void Delete(TEntity entity);
void Save();
}
但我更喜欢使用不同的界面,原因有几个。
首先,您通常不会单独使用存储库,您可能会将其与工作单元格一起使用,因此存储库不应具有Save()方法。它可能有一个Update(T entity)
方法 - 但为什么呢?您从存储库收到的对象将自动更新/更新,就像您从任何类型的对象集合中收到的任何其他对象一样,因为您已检索到对象本身的引用。 (例如:如果您的TEntity
是Person
对象,并且您获得了“Chuck”,并且您将其姓氏从“Bartowski”更改为“Carmichael”,则可能已更新存储库说如果你觉得这看起来很脆弱,那么实施Update(T entity)
方法没有任何问题。)
其次,大多数存储库应该能够处理断开连接的环境。如果您的解决方案没有此要求,您仍然可以创建一个处理断开连接方案的界面,并且只是将其保留为未实现。现在你已经为未来做好了准备。
最后,我们的契约对存储库的真实性质更有意义 - 内存中的对象集合,它们代表一些任意数据存储,可能是一个连接的。
public interface IRepository<TEntity> where TEntity : class
{
List<TEntity> GetAll();
List<TEntity> Get(Func<TEntity, bool> where);
void Insert(TEntity entity);
void Insert(IEnumerable<TEntity> entities);
void Remove(TEntity entity);
void Remove(IEnumerable<TEntity> entities);
void SyncDisconnected(TEntity entity, bool forDeletion = false);
void SyncDisconnected(IEnumerable<TEntity> entities, bool forDeletion = false);
}
如果您为所有实体定义基类,我们称之为DomainObject
,并为其指定Id
字段,然后您可以执行以下操作:
public interface IRepository<TEntity> where TEntity : DomainObject
{
TEntity GetById(object Id);
List<TEntity> GetAll();
List<TEntity> Get(Func<TEntity, bool> where);
void Insert(TEntity entity);
void Insert(IEnumerable<TEntity> entities);
void Remove(TEntity entity);
void Remove(IEnumerable<TEntity> entities);
void SyncDisconnected(TEntity entity, bool forDeletion = false);
void SyncDisconnected(IEnumerable<TEntity> entities, bool forDeletion = false);
}
如果您不喜欢可选参数forDeletion
,则可以添加允许同步已删除对象的方法:
void SyncDisconnectedForDeletion(TEntity entity);
您需要执行此操作的原因是,在大多数情况下,同步断开连接的对象以进行删除与同步断开连接的对象以进行添加或修改不兼容。 (试试吧。你会亲眼看到对商店的删除要求与添加或修改的要求有很大差异)。因此,接口应该定义一个契约,因此实现可以在两者之间进行辨别。
您可以针对任何基础数据存储的任何存储库实现此接口,连接或断开连接,包括对基础数据存储(如实体框架)的其他抽象。