我希望在一个小项目中使用IRepository模式(由NHibernate支持,如果重要)。域是一个简单的域,故意让我专注于理解IRepository模式。单个域类为Movie
,其中包含Year
,Genre
和Title
的属性。我的目的是“获取”其属性符合上述类型标准的电影。
惯例似乎是具有通用IRepository
接口,类似于以下内容:
public interface IRepository<T>
{
T Get(int id);
T[] GetAll();
void Add(T item);
void Update(T item);
void Delete(T item);
}
使用基础实现:
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) { ... }
}
然后有一个特定于域的界面:
public interface IMovieRepository
{
Movie[] GetByGenre(Genre genre);
Movie[] GetByYear(int year);
Movie[] GetByTitle(string title);
}
使用也扩展基类Repository
类的实现:
public class MovieRepository : Repository<Movie>, IMovieRepository
{
public Movie[] GetByGenre(Genre genre) { ... }
public Movie[] GetByYear(int year) { ... }
public Movie[] GetByTitle(string title) { ... }
}
我需要使用NHibernate将必要的实现添加到基类以及具体的实现,但我想知道我是否在使用此设置进入正确的轨道。
对于一个域类似乎有相当大的开销,但如果涉及多个域类则不太明显。现在我正试图保持简单,所以我可以确定这个概念。
答案 0 :(得分:6)
尽量不传回array
。使用IEnumerable<T>
,ICollection<T>
或IList<T>
,这将进一步松散地耦合您的代码。
您的IMovieRepository界面。此存储库包括CRUD。因此,
IMovieRepository : IRepository<Movie> {}
这不会改变您的MovieRepository
类,因为它将正确实现接口。如果您希望在以后更改实施,它将允许您解耦您的课程。
最后。这对其中一种方法来说很好。因为你有专门的功能,所以你专门设置了适合的存储库。
还有其他方法,可以使用1个repositry类并传入所需的查询。这称为规范模式。我做了一个项目,该项目使用位于codeplex上的报告http://whiteboardchat.codeplex.com
另一种方法是有一个传递标准的方法。有一个名为Sharp Architecture的开源项目,我认为这个编码了。
希望这有帮助
答案 1 :(得分:2)
我会说,您接近我在运输公司的资源规划生产解决方案中使用的存储库(也使用NHibernate) - 所以对于初学者来说,我认为你走的是正确的道路。我同意使用IEnumerables / IList而不是数组的dbones - 你最终会多次编写.ToArray(): - )。
您可能会考虑的一些事项:
将组合优先于继承 - 而不是从抽象存储库继承 - 让它是非抽象的并将其注入'ctor并委托调用 - 这使得您的设计在某些情况下更加健壮(例如,仅限查询存储库等。)那样你也可以选择让抽象存储库可以实例化(是一个单词吗?)并控制它是否应该在所有存储库中共享。
关注这一点 - 您可能希望将基本存储库更改为具有通用方法,而不是从通用接口继承:
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 :-)
}
您可能希望让特定的存储库可以访问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;
}
}
}
用法:
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);
}
}
如果出现问题,您可能还希望在方法上设置bool返回值 - 对于在调用代码中有意义的任何错误,可能需要输出IEnumerable。
但总而言之 - 这些只是我随着时间的推移而添加的花絮,以更好地遵守我的用法 - 而且它们完全是可选的,只是值得思考的食物:-)。我认为你走的是正确的道路 - 我认为你的代码中没有任何重大问题。
希望这是有道理的: - )
答案 2 :(得分:0)
作为思考的食物,如果您选择的ORM有一个LINQ提供者(而NH有一个),您可以尝试与集合非常相似的可查询存储库:
public interface IRepository<T> : ICollection<T>, IQueryable<T>
我在我的网站上写过一些关于它的文章:Repository or DAO?: Repository
它与你的构造有相似之处(只是因为集合也支持CRUD),我尝试的方法意味着你可以拥有不一定知道处理存储库的代码,因为它可以针对ICollection
进行编程或IQueryable
界面......