繁体   English   中英

什么是IRepository以及它用于什么?

[英]What is a IRepository and what is it used for?

什么是IRepository? 为什么使用它,简短而简单的例子不会受到伤害。

MVC促进了关注点的分离,但这并不止于MVC级别。

数据访问本身就是一个问题。 它应该在MVC的M位,即模型中完成。 你如何构建你的模型取决于你,但人们通常会遵循久经考验的模式(为什么重新发明轮子?)。 存储库模式是当前的标准。 然而,不要指望一个简单的公式,因为变化与开发人员差不多。

IRepository只是您创建的接口(它不是MVC或ASP.NET或.NET的一部分)。 它允许您将存储库与实际实现“分离”。 解耦很好,因为它意味着你的代码...:

  1. 您的代码更可重用。 这简直太好了。
  2. 您的代码可以使用Inversion of Control(或依赖注入)。 这样可以很好地保持您的关注点。 它特别好,因为这允许单元测试......
  3. 您的代码可以进行单元测试。 这在具有复杂算法的大型项目中尤其有用。 它无处不在,因为它增加了您对所使用技术的理解以及您尝试在软件中建模的域。
  4. 您的代码将围绕最佳实践构建,遵循通用模式。 这很好,因为它使维护更容易。

因此,在卖给你脱钩之后,你的问题的答案是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中思考。

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可以实现更专业的方法来完成更复杂的逻辑。

我希望这对你有所帮助。

IRepository不是.Net框架中的已定义类型。 通常,当您看到名为该接口的接口时,程序将使用存储库模式( https://web.archive.org/web/20110503184234/hbp://hiblogatingrhinos.com/nhibernate/archive/2008/10/08/ the-repository-pattern.aspx )。 通常,当人们使用此模式时,他们将创建一个所有存储库都遵循的接口。 这样做有很多好处。 一些好处是代码反汇编和单元测试。

这也很常见,因此可以利用IoC( http://en.wikipedia.org/wiki/Inversion_of_control )。

存储库是一种抽象,它表示任何底层和任意数据存储,就好像它是内存中的对象集合一样

由于常见的做法和系统限制,这个定义变成了一种更实用的形式,作为内存中对象的集合,代表了一些底层和任意数据存储,可能是一个断开连接的数据存储 在引擎盖下,存储库可以链接到数据库,平面文件,内存中的对象集合或者您可能想到的任何其他内容。 存储库的用户不关心。

所以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);

您需要这样做的原因是,在大多数情况下,同步断开连接的对象以进行删除与同步断开连接的对象以进行添加或修改是不兼容的。 (试试吧。你会亲眼看到对商店的删除要求与添加或修改的要求有很大差异)。 因此,接口应该定义一个契约,因此实现可以在两者之间进行辨别。

您可以针对任何基础数据存储的任何存储库实现此接口,连接或断开连接,包括对基础数据存储(如实体框架)的其他抽象。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM