简体   繁体   中英

What is a IRepository and what is it used for?

What is a 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.

Data Access is a concern in itself. It should be done in the M bit of MVC, ie the model. 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). 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). 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. It gives you a reliable class hierarchy to work with.

I generally use a generic IRepository:

IRepository

Where TEntity is, well, an entity. 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.

With UserCoursesRepository I can now write methods in my Service class like:

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.

An IRepository is an interface you specify when you want to implement the Repository Pattern. As @Brian Ball stated, it's not part of .NET it is an interface that you create.

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. 4 specific and 1 generic. Each one inherits from an IRepository which ensures I will not have issues down the road with differences in implementations.

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. The generic repository has to create an object set of T before it can try to do anything.

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.

I hope this helps you a little.

IRepository is not a defined type in the .Net framework. 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 ). 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 ).

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. 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. It might have an Update(T entity) method - but why? 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.)

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:

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:

    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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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