Repository Base Or Specific Method?
I've been developing an application and I'm trying to aply DDD and other tools (Nhibernate and asp.net MVC).
In this app we have a requirement to implement a 'search' by Name in the repository's Person entity. So, we have a repository base (RepositoryBase class) and in this implementation I have a generic method that works for this item.
public IEnumerable<T> FindAll(Expression<Func<T, bool>> condition) {
return Session.QueryOver<T>().Where(condition);
}
And in my asp.net mvc application, we could use it like:
var list = _repositoryPerson.FindAll(x => x.Name == "Felipe");
On the other hand, we could create a specific method (in RepositroyPerson class) for this task, like:
public IEnumerable<Person> FindByName(string name) {
return Session.QueryOver<Person>().Where(x => x.Name == name);
}
in my asp.net mvc app, we could use it like:
var list = _repositoryPerson.FindByName("Felipe");
My questions are:
1 - What is the recommended way to meet this requirement in accordance with DDD? Specific or Base class ?
2 - Someone recommended some nice implementation of Repository base (generic) with Nhibernate QueryOver?
if someone could help me, I would really appreciate it! Thanks
UPDATES:
If I need, for example, a complex search like combine the conditions... e.g.: name (optional) 开发者_运维百科and age (optional) and city (optional) and other fields.... every field would be optional and combine with other fields! Using expression would be the recommended or no ? How would you implement this code?
PS: Sorry for my english!
Thanks
In order to have the best of both worlds, you can implement the FindByName
method generically as you have in the base class, mark it as protected
or protected internal
(depending on whether you want to allow other assemblies to define Repository implementations), then call it from your specific repository objects.
protected internal IEnumerable<T> FindAll(Expression<Func<T, bool>> condition) {
return Session.QueryOver<T>().Where(condition);
}
public IEnumerable<Person> FindByName(string name) {
return base.FindAll(x => x.Name == name);
}
This will allow you to write specific tests for the cases you will be using the method, while also allowing you to change out your ORM implementation without needing to change it in a ton of places.
UPDATE: In order to combine multiple conditions in FindAll, you can simply combine them using Aggregate (I haven't tested this with NHibernate, but if it breaks, you can replace it with a foreach).
public IEnumerable<T> FindAll(IEnumerable<Expression<Func<T, bool>>> conditions)
{
return conditions.Aggregate(Session.QueryOver<T>(), (current, condition) => current.Where(condition)).List();
}
Then methods could take optional parameters, and create a list of conditions that are passed to FindAll.
public IEnumerable<T> FindByParams(string name=null, string city=null)
{
var wheres = new List<Expression<Func<T, bool>>>();
if (name != null)
{
wheres.Add(x => x.Name == name);
}
if (city != null)
{
wheres.Add(x => x.City == city);
}
return base.FindAll(wheres);
}
both methods works i will use the generic method but the findmyname gives you better and clean code. i think its a matters of tastes and how purist in DDD you want to be. i will create specific methods for more complex requirements like a search or something but keeping it simple is the way to go for me
I agree with Oscar - I prefer specific methods for the main reason that they are much more testable. It is very challenging to build reasonable tests around IQueryable and Expression objects.
Additional considerations:
Who are the consumers of your repository code? If you expose your repository to gui or other devs who may struggle or misuse a more abstract interface, then there is additional incentive to provide a clearer specific interface.
How bloated is the interface? Just because an expression can be almost anything doesn't mean it usually is. But in those cases where it is, perhaps because you can combine search criteria as in your update, then that tilts back to using more abstract methods, at least as helpers.
I personally favor a more specific interface wherever possible
Cheers,
Berryl
精彩评论