Gra*_*lin 12 c# design-patterns irepository
我想在一个小项目中使用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);
}
Run Code Online (Sandbox Code Playgroud)
有了基础实现:
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) { ... }
}
Run Code Online (Sandbox Code Playgroud)
然后有一个特定于域的界面:
public interface IMovieRepository
{
Movie[] GetByGenre(Genre genre);
Movie[] GetByYear(int year);
Movie[] GetByTitle(string title);
}
Run Code Online (Sandbox Code Playgroud)
使用也扩展基Repository类的实现:
public class MovieRepository : Repository<Movie>, IMovieRepository
{
public Movie[] GetByGenre(Genre genre) { ... }
public Movie[] GetByYear(int year) { ... }
public Movie[] GetByTitle(string title) { ... }
}
Run Code Online (Sandbox Code Playgroud)
我需要使用NHibernate添加必要的实现到基类以及具体的实现,但我想知道我是否在这个设置的正确轨道上.
对于一个域类似乎有相当大的开销,但如果涉及多个域类则不太明显.现在我正试图保持简单,所以我可以确定这个概念.
尽量不要传回来array.使用IEnumerable<T>,ICollection<T>或者IList<T>,这将进一步松散地耦合您的代码.
你的IMovieRepository接口.此存储库包括CRUD.因此成功
IMovieRepository : IRepository<Movie> {}
这不会改变您的MovieRepository类,因为它将正确实现接口.如果您希望在以后更改实施,它将允许您解耦您的课程.
最后.这对其中一种方法来说很好.因为你有专门的功能,你已经专门设置了适合的存储库.
还有其他方法,使您可以使用1个repositry类并传递所需的查询.这称为规范模式.我做了一个项目,使用它位于codeplex上,报告http://whiteboardchat.codeplex.com
另一种方法是有一个方法来传递标准.有一个名为Sharp Architecture的开源项目,我认为这个编码了.
希望这可以帮助
我想说,您接近我在运输公司的资源规划生产解决方案中使用的存储库(也使用 NHibernate) - 所以对于初学者来说,我认为您走在正确的道路上。我同意 dbones 使用 IEnumerables /IList 而不是数组的观点 - 你最终会多次编写 .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 :-)
}
Run Code Online (Sandbox Code Playgroud)
您可能想让特定的存储库能够访问 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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
用法:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
如果出现问题,您可能还想在您的方法上设置一个 bool 返回值 - 也许还需要为调用代码中有意义的任何错误设置一个 out IEnumerable 。
但总而言之 - 这些只是我随着时间的推移添加的花絮,以更好地符合我的使用情况 - 并且它们完全是可选的,只是值得深思的:-)。我认为您走在正确的道路上 - 我在您的代码中没有看到任何重大问题。
希望这是有道理的:-)