简体   繁体   English

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

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

What is a IRepository? 什么是IRepository? Why is it used, brief and simple examples won't hurt. 为什么使用它,简短而简单的例子不会受到伤害。

MVC promotes separation of concerns, but that doesn't stop at the MVC level. MVC促进了关注点的分离,但这并不止于MVC级别。

Data Access is a concern in itself. 数据访问本身就是一个问题。 It should be done in the M bit of MVC, ie the model. 它应该在MVC的M位,即模型中完成。 How you structure your model is up to you, but people usually follow tried and tested patterns (why reinvent the wheel?). 你如何构建你的模型取决于你,但人们通常会遵循久经考验的模式(为什么重新发明轮子?)。 The Repository Pattern is the current standard. 存储库模式是当前的标准。 Don't expect a simple formula, however, because the variations are as many as there are developers, almost. 然而,不要指望一个简单的公式,因为变化与开发人员差不多。

IRepository is just an interface that you create (it is not part of MVC or ASP.NET or .NET). IRepository只是您创建的接口(它不是MVC或ASP.NET或.NET的一部分)。 It allows you to "decouple" your repositories from real implementations. 它允许您将存储库与实际实现“分离”。 Decoupling is good because it means your code...: 解耦很好,因为它意味着你的代码...:

  1. Your code is much more reusable. 您的代码更可重用。 This is just plain good. 这简直太好了。
  2. Your code can use Inversion of Control (or Dependency Injection). 您的代码可以使用Inversion of Control(或依赖注入)。 This is good to keep your concerns well separated. 这样可以很好地保持您的关注点。 It is especially good because this allows Unit Testing... 它特别好,因为这允许单元测试......
  3. Your code can be Unit Tested. 您的代码可以进行单元测试。 This is especially good in large projects with complex algorithms. 这在具有复杂算法的大型项目中尤其有用。 It is good everywhere because it increases your understanding of the technologies you are working with and the domains you are trying to model in software. 它无处不在,因为它增加了您对所使用技术的理解以及您尝试在软件中建模的域。
  4. Your code becomes built around best practices, following a common pattern. 您的代码将围绕最佳实践构建,遵循通用模式。 This is good because it makes maintenance much easier. 这很好,因为它使维护更容易。

So, having sold you decoupling, the answer to your question is that IRepository is an interface that you create and that you make your Repositories inherit from. 因此,在卖给你脱钩之后,你的问题的答案是IRepository是你创建的一个接口,并且你使你的存储库继承。 It gives you a reliable class hierarchy to work with. 它为您提供了可靠的类层次结构。

I generally use a generic IRepository: 我通常使用通用的IRepository:

IRepository IRepository

Where TEntity is, well, an entity. TEntity就是一个实体。 The code I use is: 我使用的代码是:

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

A concrete implementation of this interface would be: 该接口的具体实现是:

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

This allows me to write: 这允许我写:

SqlRepository<UserCourse> UserCoursesRepository = new SqlRepository<UserCourse>(db);

Where db is a DataContext instance injected into, say, a Service. 其中db是一个注入到服务中的DataContext实例。

With UserCoursesRepository I can now write methods in my Service class like: 使用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();
        }

And now in my controllers, I can just write: 现在在我的控制器中,我可以写:

MyService.DeleteUserCourse(5);
MyService.Save();

With this pattern the development of your app becomes more of an assembly line that leads up to a VERY simple controller. 使用这种模式,您的应用程序的开发变得更像是一个装配线,导致一个非常简单的控制器。 Every piece of the assembly line can be tested independently of everything else, so bugs are nipped in the bud. 装配线的每一部分都可以独立于其他任何部分进行测试,因此虫子被扼杀在萌芽状态。

If this is a long, unwieldy answer it is because the real answer is: 如果这是一个冗长,笨拙的答案,那是因为真正的答案是:

Buy Steven Sanderson's book Pro ASP.NET MVC 2 Framework and learn to think in MVC. 购买Steven Sanderson的书Pro ASP.NET MVC 2 Framework并学习在MVC中思考。

An IRepository is an interface you specify when you want to implement the Repository Pattern. IRepository是您希望实现存储库模式时指定的接口。 As @Brian Ball stated, it's not part of .NET it is an interface that you create. 正如@Brian Ball所说,它不是.NET的一部分,它是您创建的界面。

Developers using the Repository Pattern widely recommend the use of an interface for the implementation. 使用存储库模式的开发人员广泛推荐使用接口来实现。 For example, in the application I am developing right now, I have 5 repositories. 例如,在我正在开发的应用程序中,我有5个存储库。 4 specific and 1 generic. 4具体和1通用。 Each one inherits from an IRepository which ensures I will not have issues down the road with differences in implementations. 每一个都从一个IRepository继承而来,这确保了我不会在实施过程中遇到问题。

As far as code examples, I'll try: 至于代码示例,我会尝试:

interface IRepository<T> where T : class {
    IQueryable<T> Select();
}

Implemented as a generic repository: 实现为通用存储库:

public class Repository<T> : IRepository<T> where T : class {
    public IQueryable<T> Select() {
        return this.ObjectContext.CreateObjectSet<T>();
    }
}

Implemented as a specialized repository: 作为专门的存储库实现:

public class EmployeeRepository : IRepository<Employee> {
    public IQueryable<Employee> Select() {
        return this.ObjectContext.Employees;
    }
}

Both the Repository<T> and EmployeeRepository implement IRepository , however they go about performing the querying slightly differently. Repository<T>EmployeeRepository实现了IRepository ,但是它们执行查询的方式略有不同。 The generic repository has to create an object set of T before it can try to do anything. 通用存储库必须先创建T的对象集,然后才能尝试执行任何操作。

Keep in mind that Repository<T> is supposed to be locked to the interface, where as EmployeeRepository can implement more specialized methods to accomplish more complex logic. 请记住, Repository<T>应该被锁定到接口,而EmployeeRepository可以实现更专业的方法来完成更复杂的逻辑。

I hope this helps you a little. 我希望这对你有所帮助。

IRepository is not a defined type in the .Net framework. IRepository不是.Net框架中的已定义类型。 Usually when you see an interface named that, the program uses the Repository Pattern ( https://web.archive.org/web/20110503184234/http://blogs.hibernatingrhinos.com/nhibernate/archive/2008/10/08/the-repository-pattern.aspx ). 通常,当您看到名为该接口的接口时,程序将使用存储库模式( https://web.archive.org/web/20110503184234/hbp://hiblogatingrhinos.com/nhibernate/archive/2008/10/08/ the-repository-pattern.aspx )。 Generally when people use this pattern, they will create an interface that all the repositories adhere to. 通常,当人们使用此模式时,他们将创建一个所有存储库都遵循的接口。 There are many benefits to doing this. 这样做有很多好处。 Some of the benefits are code de-copupling, and unit testing. 一些好处是代码反汇编和单元测试。

It is also common for this to be done so it can be taken advantage of with IoC ( http://en.wikipedia.org/wiki/Inversion_of_control ). 这也很常见,因此可以利用IoC( http://en.wikipedia.org/wiki/Inversion_of_control )。

A repository is an abstraction which represents any underlying and arbitrary data store as if it were an in memory collection of objects . 存储库是一种抽象,它表示任何底层和任意数据存储,就好像它是内存中的对象集合一样

This definition is morphed into a more practical form due to common practices and system limitations as a collection of objects in memory which represent some underlying and arbitrary data store, possibly a disconnected one . 由于常见的做法和系统限制,这个定义变成了一种更实用的形式,作为内存中对象的集合,代表了一些底层和任意数据存储,可能是一个断开连接的数据存储 Under the hood the repository may be linked to a database, a flat file, an in-memory collection of objects, or whatever else you may imagine. 在引擎盖下,存储库可以链接到数据库,平面文件,内存中的对象集合或者您可能想到的任何其他内容。 The user of a repository doesn't care. 存储库的用户不关心。

So an IRepository is the interface contract which defines how Api code wishes client code to interact with the repository. 所以IRepository是接口契约,它定义了Api代码如何希望客户端代码与存储库交互。 This often includes add, update, delete, and get contracts as for example, this very common example of a repository contract: 这通常包括添加,更新,删除和获取合同,例如,存储库合同的这个非常常见的示例:

public interface IRepository<TEntity> where TEntity : class
{
    List<TEntity> GetAll();
    void Add(TEntity entity);
    void Delete(TEntity entity);
    void Save();
}

But I prefer to use a different interface for a few reasons. 但出于几个原因,我更喜欢使用不同的界面。

First, you typically wont be using a repository by itself, you will probably be using it with a unit of work pattern, so the repository shouldn't have a Save() method. 首先,您通常不会单独使用存储库,您可能会将其与工作单元格一起使用,因此存储库不应具有Save()方法。 It might have an Update(T entity) method - but why? 它可能有一个Update(T entity)方法 - 但为什么呢? The object which you receive from the repository will automatically be updatable/updated just like any other object you would receive from any kind of collection of objects because you have retrieved references to the objects themselves. 您从存储库收到的对象将自动更新/更新,就像您从任何类型的对象集合中收到的任何其他对象一样,因为您已检索到对象本身的引用。 (For example: if your TEntity is a Person object, and you get person "Chuck", and you change his last name from "Bartowski" to "Carmichael", the repository has presumably already updated said entity. If this seems tenuous in your mind, there is nothing wrong with implementing an Update(T entity) method.) (例如:如果你的TEntity是一个Person对象,你得到了人“Chuck”,并且你将他的姓氏从“Bartowski”更改为“Carmichael”,那么该存储库可能已经更新了所述实体。如果这看起来很脆弱请注意,实现Update(T entity)方法没有任何问题。)

Second, most repositories should be able to handle disconnected environments. 其次,大多数存储库应该能够处理断开连接的环境。 If your solution does not have this requirement, you can still create an interface that handles disconnected scenarios and simply leave it unimplemented. 如果您的解决方案没有此要求,您仍然可以创建一个处理断开连接方案的界面,并且只是将其保留为未实现。 Now you are ready for the future. 现在你已经为未来做好了准备。

At last, our contract makes more sense to the true nature of a repository - a collection of objects in memory which represent some arbitrary data store, possibly a disconnected one . 最后,我们的契约对存储库的真实性质更有意义 - 存储库中的对象集合代表一些任意数据存储,可能是一个断开连接的数据存储

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

If you define a base class for all of your entities, let's call it DomainObject , and you give it an Id field, then you can do the following: 如果为所有实体定义基类,让我们将其称为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);
}

If you don't like the optional parameter forDeletion , you can add a method which allow syncing deleted objects as well: 如果您不喜欢可选参数forDeletion ,则可以添加允许同步已删除对象的方法:

    void SyncDisconnectedForDeletion(TEntity entity);

The reason you need to do this is because in most cases, syncing disconnected objects for deletion is incompatible with syncing disconnected objects for addition or modification. 您需要这样做的原因是,在大多数情况下,同步断开连接的对象以进行删除与同步断开连接的对象以进行添加或修改是不兼容的。 (Try it. You will see for yourself the requirements for deletion against a store vary wildly from that of addition or modification). (试试吧。你会亲眼看到对商店的删除要求与添加或修改的要求有很大差异)。 Hence, the interface should define a contract so the implementation can discern between the two. 因此,接口应该定义一个契约,因此实现可以在两者之间进行辨别。

You can implement this interface against ANY repository of ANY underlying data store, connected or disconnected, including other abstractions to underlying data stores such as Entity Framework. 您可以针对任何基础数据存储的任何存储库实现此接口,连接或断开连接,包括对基础数据存储(如实体框架)的其他抽象。

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

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