I've been searching for quite some time for a good solution to the problems presented by the typical Repository pattern (growing list of methods for specialized queries, etc.. see: http://ayende.com/blog/3955/repository-is-the-new-singleton).
I really like the idea of using Command queries, particularly through use of the Specification pattern. However, my problem with specification is that it only relates to the criteria of simple selections (basically, the where clause), and does not deal with the other issues of queries, such as joining, grouping, subset selection or projection, etc.. basically, all the extra hoops many queries must go through to get the correct set of data.
(note: I use the term "command" as in the Command pattern, also known as query objects. I'm not talking about command as in command/query separation where there is a distinction made between queries and commands (update, delete, insert))
So I'm looking for alternatives that encapsulate the entire query, but still flexible enough that you're not just swapping spaghetti Repositories for an explosion of command classes.
I've used, for instance Linqspecs, and while I find some value in being able to assign meaningful names to selection criteria, it's just not enough. Perhaps i'm seeking a blended solution that combines multiple approaches.
I am looking for solutions that others may have developed to either address this problem, or address a different problem but still satisfies these requirements. In the linked article, Ayende suggests using the nHibernate context directly, but I feel that largely complicates your business layer because it now also has to contain query information.
I'll be offering a bounty on this, as soon as the waiting period elapses. So please make your solutions bounty worthy, with good explanations and I will select the best solution, and upvote the runners up.
NOTE: I'm looking for something that is ORM based. Doesn't have to be EF or nHibernate explicitly, but those are the most common and would fit the best. If it can be easily adapted to other ORM's that would be a bonus. Linq compatible would also be nice.
UPDATE: I'm really surprised that there aren't many good suggestions here. It seems like people are either totally CQRS, or they're completely in the Repository camp. Most of my apps are not complex enough to warrant CQRS (something with most CQRS advocates readily say that you should not use it for).
UPDATE: There seems to be a little confusion here. I'm not looking for a new data access technology, but rather a reasonably well designed interface between business and data.
Ideally, what i'm looking for is some kind of cross between Query objects, Specification pattern, and repository. As I said above, Specification pattern only deals with the where clause aspect, and not the other aspects of the query, such as joins, sub-selects, etc.. Repositories deal with the whole query, but get out of hand after a while. Query objects also deal with the whole query, but I don't want to simply replace repositories with explosions of query objects.
My way of dealing with that is actually simplistic and ORM agnostic. My view for a repository is this: The repository's job is to provide the app with the model required for the context, so the app just asks the repo for what it wants but doesn't tell it how to get it.
I supply the repository method with a Criteria (yes, DDD style), which will be used by the repo to create the query (or whatever is required - it may be a webservice request). Joins and groups imho are details of how, not the what and a criteria should be only the base to build a where clause.
Model = the final object or data structure neede by the app.
Probably you can use the ORM criteria (Nhibernate) directly if you want it. The repository implementation should know how to use the Criteria with the underlying storage or DAO.
I don't know your domain and the model requirements but it would be strange if the best way is that the app to build the query itself. The model changes so much that you can't define something stable?
This solution clearly requires some additional code but it doesn't couple the rest of the to an ORM or whatever you're using to access the storage. The repository does its job to act as a facade and IMO it's clean and the 'criteria translation' code is reusable
You can use a fluent interface. The basic idea is that methods of a class return the current instance this very class after having performed some action. This allows you to chain method calls.
By creating an appropriate class hierarchy, you can create a logical flow of accessible methods.
You would call it like this
You can only create a new instance of
Query
. The other classes have a protected constructor. The point of the hierarchy is to "disable" methods. For instance, theGroupBy
method returns aGroupedQuery
which is the base class ofQuery
and does not have aWhere
method (the where method is declared inQuery
). Therefore it is not possible to callWhere
afterGroupBy
.It is however not perfect. With this class hierarchy you can successively hide members, but not show new ones. Therefore
Having
throws an exception when it is called beforeGroupBy
.Note that it is possible to call
Where
several times. This adds new conditions with anAND
to the existing conditions. This makes it easier to construct filters programmatically from single conditions. The same is possible withHaving
.The methods accepting field lists have a parameter
params string[] fields
. It allows you to either pass single field names or a string array.Fluent interfaces are very flexible and do not require you to create a lot of overloads of methods with different combinations of parameters. My example works with strings, however the approach can be extended to other types. You could also declare predefined methods for special cases or methods accepting custom types. You could also add methods like
ExecuteReader
orExceuteScalar<T>
. This would allow you to define queries like thisEven SQL commands constructed this way can have command parameters and thus avoid SQL injection problems and at the same time allow commands to be cached by the database server. This is not a replacement for an O/R-mapper but can help in situations where you would create the commands using simple string concatenation otherwise.
I've done this, supported this and undone this.
The major problem is this: no matter how you do it, the added abstraction does not gain you independence. It will leak by definition. In essence, you're inventing an entire layer just to make your code look cute... but it does not reduce maintenance, improve readability or gain you any type of model agnosticism.
The fun part is that you answered your own question in response to Olivier's response: "this is essentially duplicating the functionality of Linq without all the benefits you get from Linq".
Ask yourself: how could it not be?
Disclaimer: Since there aren't any great answers yet, I decided to post a part from a great blog post I read a while ago, copied almost verbatim. You can find the full blog post here. So here it is:
We can define the following two interfaces:
The
IQuery<TResult>
specifies a message that defines a specific query with the data it returns using theTResult
generic type. With the previously defined interface we can define a query message like this:This class defines a query operation with two parameters, which will result in an array of
User
objects. The class that handles this message can be defined as follows:We can now let consumers depend upon the generic
IQueryHandler
interface:Immediately this model gives us a lot of flexibility, because we can now decide what to inject into the
UserController
. We can inject a completely different implementation, or one that wraps the real implementation, without having to make changes to theUserController
(and all other consumers of that interface).The
IQuery<TResult>
interface gives us compile-time support when specifying or injectingIQueryHandlers
in our code. When we change theFindUsersBySearchTextQuery
to returnUserInfo[]
instead (by implementingIQuery<UserInfo[]>
), theUserController
will fail to compile, since the generic type constraint onIQueryHandler<TQuery, TResult>
won't be able to mapFindUsersBySearchTextQuery
toUser[]
.Injecting the
IQueryHandler
interface into a consumer however, has some less obvious problems that still need to be addressed. The number of dependencies of our consumers might get too big and can lead to constructor over-injection - when a constructor takes too many arguments. The number of queries a class executes can change frequently, which would require constant changes into the number of constructor arguments.We can fix the problem of having to inject too many
IQueryHandlers
with an extra layer of abstraction. We create a mediator that sits between the consumers and the query handlers:The
IQueryProcessor
is a non-generic interface with one generic method. As you can see in the interface definition, theIQueryProcessor
depends on theIQuery<TResult>
interface. This allows us to have compile time support in our consumers that depend on theIQueryProcessor
. Let's rewrite theUserController
to use the newIQueryProcessor
:The
UserController
now depends on aIQueryProcessor
that can handle all of our queries. TheUserController
'sSearchUsers
method calls theIQueryProcessor.Process
method passing in an initialized query object. Since theFindUsersBySearchTextQuery
implements theIQuery<User[]>
interface, we can pass it to the genericExecute<TResult>(IQuery<TResult> query)
method. Thanks to C# type inference, the compiler is able to determine the generic type and this saves us having to explicitly state the type. The return type of theProcess
method is also known.It is now the responsibility of the implementation of the
IQueryProcessor
to find the rightIQueryHandler
. This requires some dynamic typing, and optionally the use of a Dependency Injection framework, and can all be done with just a few lines of code:The
QueryProcessor
class constructs a specificIQueryHandler<TQuery, TResult>
type based on the type of the supplied query instance. This type is used to ask the supplied container class to get an instance of that type. Unfortunately we need to call theHandle
method using reflection (by using the C# 4.0 dymamic keyword in this case), because at this point it is impossible to cast the handler instance, since the genericTQuery
argument is not available at compile time. However, unless theHandle
method is renamed or gets other arguments, this call will never fail and if you want to, it is very easy to write a unit test for this class. Using reflection will give a slight drop, but is nothing to really worry about.To answer one of your concerns:
A consequence of using this design is that there will be a lot of small classes in the system, but having a lot of small/focused classes (with clear names) is a good thing. This approach is clearly much better then having many overloads with different parameters for the same method in a repository, as you can group those in one query class. So you still get a lot less query classes than methods in a repository.