简体   繁体   English

我正确使用IRepository吗?

[英]Am I using IRepository correctly?

I'm looking to use the IRepository pattern (backed by NHibernate, if it matters) in a small project. 我想在一个小项目中使用IRepository模式(由NHibernate支持,如果它很重要)。 The domain is a simple one, intentionally so to allow me to focus on understanding the IRepository pattern. 域是一个简单的域,故意让我专注于理解IRepository模式。 The lone domain class is Movie , with properties for Year , Genre , and Title . 单独的域类是Movie ,具有YearGenreTitle属性。 My intent would be to "get" movies whose properties match criteria of the aforementioned types. 我的意图是“获取”其属性符合上述类型标准的电影。

Convention seems to be to have a generic IRepository interface, similar to the following: 惯例似乎是具有通用的IRepository接口,类似于以下内容:

public interface IRepository<T>
{
    T Get(int id);
    T[] GetAll();
    void Add(T item);
    void Update(T item);
    void Delete(T item);
}

With a base implementation: 有了基础实现:

public abstract class Repository<T> : IRepository<T>
{
    public T Get(int id) { ... }
    public T[] GetAll() { ... }
    public void Add(T item) { ... }
    public void Update(T item) { ... }
    public void Delete(T item) { ... }
}

Then to have a domain-specific interface: 然后有一个特定于域的界面:

public interface IMovieRepository
{
    Movie[] GetByGenre(Genre genre);
    Movie[] GetByYear(int year);
    Movie[] GetByTitle(string title);
}

With an implementation that also extends the base Repository class: 使用也扩展基本Repository类的实现:

public class MovieRepository : Repository<Movie>, IMovieRepository
{
    public Movie[] GetByGenre(Genre genre) { ... }
    public Movie[] GetByYear(int year) { ... }
    public Movie[] GetByTitle(string title) { ... }
}

I would need to add necessary implementation to the base class as well as the concrete one, using NHibernate, but I would like to know if I am on the right track with this setup. 我需要使用NHibernate添加必要的实现到基类以及具体的实现,但我想知道我是否在这个设置的正确轨道上。

There seems to be a fair bit of overhead for just one domain class, though it would be less noticeable if there were multiple domain classes involved. 对于一个域类似乎有相当大的开销,但如果涉及多个域类则不太明显。 Right now I'm trying to keep it simple so I can pin down the concept. 现在我正试图保持简单,所以我可以确定这个概念。

  1. try not to pass back an array . 尽量不要传回一个array use IEnumerable<T> , ICollection<T> or IList<T> , this will loosely couple your code further. 使用IEnumerable<T>ICollection<T>IList<T> ,这将进一步松散地耦合您的代码。

  2. your IMovieRepository interface. 你的IMovieRepository接口。 this repository includes the CRUD. 此存储库包括CRUD。 therefore make it 因此成功

IMovieRepository : IRepository<Movie> {}

This will not change your MovieRepository class as that will implement the interface correctly. 这不会改变您的MovieRepository类,因为它将正确实现接口。 it will allow you to decouple your classes if you wish to change the implementation at a later date. 如果您希望在以后更改实施,它将允许您解耦您的课程。

finally. 最后。 this is fine for one of the methods. 这对其中一种方法来说很好。 as you have specialised functionality you have specialised the repository to suit. 因为你有专门的功能,你已经专门设置了适合的存储库。

there are other ways, which enable you to use 1 repositry class and pass in the required query. 还有其他方法,使您可以使用1个repositry类并传递所需的查询。 This is called the Specification pattern. 这称为规范模式。 I did a project which uses this located on codeplex with report http://whiteboardchat.codeplex.com 我做了一个项目,使用它位于codeplex上,报告http://whiteboardchat.codeplex.com

the other way would to to have a method to pass in the criteria. 另一种方法是有一个方法来传递标准。 there is a open source project called Sharp Architecture, which i believe has this coded up. 有一个名为Sharp Architecture的开源项目,我认为这个编码了。

Hope this helps 希望这可以帮助

I'd say, you are close to the repository that I use in a production solution for resource planning in Transport companies (using NHibernate as well) - so for starters you are on the right path in my opinion. 我会说,你接近我在生产解决方案中使用的存储库,用于运输公司的资源规划(也使用NHibernate) - 所以对于初学者来说,在我看来你是正确的道路。 I agree with dbones on using IEnumerables /IList instead of arrays - you'll end up writing .ToArray() many times over :-). 我同意dbones使用IEnumerables / IList而不是数组 - 你最终会多次编写.ToArray():-)。

A few things you might consider: 您可以考虑以下几点:

Favour Composition over inheritance - instead of inheriting from the abstract repository - let it be non-abstract and inject it in the 'ctor and delegate the calls - this makes your design more robust in certain situations (eg for a Query-only repository etc.) That way you also have the option of letting the abstract repository be instantiatable (is that a word?) and control whether it should be shared across all repositories. 优先于继承 - 而不是继承自抽象存储库 - 让它是非抽象的并将其注入'ctor并委托调用 - 这使得您的设计在某些情况下更加健壮(例如,对于仅查询存储库等。那么你也可以选择让抽象存储库可以实例化(是一个单词吗?)并控制它是否应该在所有存储库中共享。

Following up on that point - you might want to change the base Repository to have generic methods instead of inheriting from the generic interface: 关注这一点 - 您可能希望更改基本存储库以使用通用方法,而不是从通用接口继承:

public class Repository
{
    public void Add<T>(T entity)
    {
        using(var session = GetSession())
        using(var tx = session.BeginTransaction())
        {
             session.Save(entity)
             //Transaction handling etc.
        }
    }
    .... //repeat ad nasseum :-)
}

You might want to let the specific repositories have access to the ISession - this greatly improves how flexible you can make your queries and control eager/lazy fetching and you get full advantage of NHibernate etc. Eg 您可能希望让特定的存储库可以访问ISession - 这极大地提高了您查询和控制渴望/延迟获取的灵活性,并且您可以充分利用NHibernate等。

public class Repository
{
    public IList<T> WrapQueryInSession<T>(Func<ISession,IList<T> query)
    {
        using(var session = GetSession())
        using(var tx = session.BeginTransaction())
        {
             var items = query(session);
             //Handle exceptions transacitons etc.
             return items;
        }
     }
 }

Usage: 用法:

public class MovieRepository : IMovieRepository
{
    private Repository _repository;
    public MovieRepository(Repository repository)
    {
        _repository = repository;
    }
    public IList<Movie> GetByYear(int year)
    {
        Func<ISession, IList<Movie> query = session =>
        {
            var query = session.CreateQuery("from Movie"); //or
            var query = session.CreateCriteria("from Movie"); //or
            var query = session.Linq<Movie>();
            //set criteria etc.
            return query.List<Movie>(); //ToList<Movie>() if you're using Linq2NHibernate
        }:
        return _repository.WrapQueryInSession(query);
    }
}

You might also want to set a bool return value on your methods if something goes wrong - and maybe an out IEnumerable for any errors that will make sense in the calling code. 如果出现问题,您可能还想在方法上设置bool返回值 - 对于在调用代码中有意义的任何错误,可能需要输出IEnumerable。

But all in all - these are just my tidbits that I have added over time to comply better with my usage - and they are entirely optional, just food for thought :-). 但总而言之 - 这些只是我随着时间的推移而增加的花絮,以更好地遵守我的用法 - 它们完全是可选的,只是值得思考的食物:-)。 I think you are on the right path - I don't see any major problems in your code. 我认为你走的是正确的道路 - 我没有看到你的代码中有任何重大问题。

Hope this makes sense :-) 希望这是有道理的:-)

As food for thought, if your ORM of choice has a LINQ provider (and NH has one) you can try the queryable repository that is quite similar to a collection: 作为思考的食物,如果您选择的ORM具有LINQ提供程序(并且NH有一个),您可以尝试与集合非常相似的可查询存储库:

public interface IRepository<T> : ICollection<T>, IQueryable<T>

I have written some about it on my site: Repository or DAO?: Repository It has similarities to your construction (just because a collection supports CRUD as well), the approach I tries means that you can have code that isn't necessarily aware of dealing with a repository as it can be programmed against the ICollection or IQueryable interface... 我在我的网站上写了一些关于它的内容: 存储库或DAO?:存储库它与你的构造有相似之处(仅仅因为集合也支持CRUD),我尝试的方法意味着你可以拥有不一定知道的代码处理存储库,因为它可以针对ICollectionIQueryable接口进行编程...

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

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