patterncsharpMinor
Generic Entity Framework Query in CQRS
Viewed 0 times
genericcqrsqueryframeworkentity
Problem
I've been working on our application (CQRS and DDD) for awhile now. The application architecture is well layered and thought through.
However we are having difficulties in decide where to put the below code, in such a way that it makes sense from an architectural point of view.
What the code does: It's a generic method which executes a query against the dataset, specified by the generic parameter which inherits a particular interface.
Note: we are not using repositories, since the query is, as the name indicates, for our read layer.
The interface, used by the generic method:
The generic method, which lives inside our Entity Framework DbContext:
As you may be thinking, we have several EF Entities inheriting the `IOrganisationalDbEntry
However we are having difficulties in decide where to put the below code, in such a way that it makes sense from an architectural point of view.
What the code does: It's a generic method which executes a query against the dataset, specified by the generic parameter which inherits a particular interface.
Note: we are not using repositories, since the query is, as the name indicates, for our read layer.
The interface, used by the generic method:
public interface IOrganisationalDbEntry
{
string OrganisationLevelId { get; set; }
OrganisationLevelDbEntry OrganisationLevel { get; set; }
}The generic method, which lives inside our Entity Framework DbContext:
public IList GetByOrganisationLevel(Guid? organisationLevelId,
Func filter = null)
where TDbEntry : class, IOrganisationalDbEntry
{
var organisationLevel = organisationLevelId.HasValue ? organisationLevelId.Value.ToString() : null;
var query = filter == null ? Set() : Set().Where(filter);
IList result = query.Where(x => x.OrganisationLevelId == organisationLevel).ToList();
// If there are no results for the current organisation level
// => recursive check for entries for parent organisation level
if (!result.Any() && !string.IsNullOrWhiteSpace(organisationLevel))
{
// Get parentOrganisationLevel
var parentOrganisationLevelId = Set()
.Where(x => x.Id == organisationLevel)
.Select(x => x.ParentOrganisationLevelId)
.SingleOrDefault();
return
GetByOrganisationLevel(
string.IsNullOrWhiteSpace(parentOrganisationLevelId)
? (Guid?)null
: new Guid(parentOrganisationLevelId), filter);
}
return result;
}As you may be thinking, we have several EF Entities inheriting the `IOrganisationalDbEntry
Solution
I'll give it a shot since CQRS is something I'm still learning.
In CQRS we are supposed to separate our read model from our write model. Is this what you have done?
Then the read model should be designed to be a perfect fit for the thing that needs to read it:
Here are some suggestions to simplify things:
I meant to post some code examples, but Chrome crashed again and edits got lost :-(
In CQRS we are supposed to separate our read model from our write model. Is this what you have done?
Then the read model should be designed to be a perfect fit for the thing that needs to read it:
- Your query looks very complex. Is it derived from the write model, or do you indeed have separate tables for the queries?
- Why a
Funcas a parameter? It seems strange to me that the criteria should be a negotiation between client and query.
- Why do you put the query method onto the
DbContext? That class will soon enough be cluttered with too many queries.
Here are some suggestions to simplify things:
- Let the
DbContextmimic the database tables only and nothing else. That means yourDbContextwill only have public properties for the tables you are going to query.
- Don't call it a
Repository, but aQuery. Split theQuery-class when it grows too big.
- Let the
Queryclass take theDbContextas a dependency. Query theDbContext-properties, don't useSet.
- Let each query-method accept one criteria object as parameter, then build the query expression from that inside the method.
- Linq is tricky. While not entirely true, a rule of thumb is that a Func runs client side, while an Expression runs server side. That's also why I don't like the
Func-parameter.
- Consider using only non-generic query-methods. Yes, there will be some duplication, but you want your peers and the future you to understand the queries two months from now.
I meant to post some code examples, but Chrome crashed again and edits got lost :-(
Context
StackExchange Code Review Q#60464, answer score: 3
Revisions (0)
No revisions yet.