patterncsharpMinor
Query Builder pattern with use of FluentApi
Viewed 0 times
builderwithqueryfluentapiusepattern
Problem
Recently, I've come up with an idea of building a custom query builder combine with FluentApi.
Expected usage:
As you see from the expected behavior, my purpose is to make the code descriptive and easy to understand for all developers and most importantly, the code need to be flexible and welcome-for-changes. For instance, I could add
QueryBuilder.cs (base class)
```
public abstract class QueryBuilder : IDisposable where TEntity : class, new()
{
protected DbContext Context { get; set; }
///
/// The query object
///
protected static IQueryable Query;
///
/// Initializes a new instance of the class.
///
protected QueryBuilder(DbContext context)
{
Context = context;
Query = Context.Set();
}
///
/// Performs an implicit conversion from to .
///
/// The query builder.
///
/// The result of the conversion.
///
public static implicit operator List(QueryBuilder queryBuilder)
{
return Query.ToList();
}
///
/// Performs an implicit conversion from to .
///
/// The query builder.
///
/// The result of the conversion.
///
public static implicit operator TEntity(QueryBuilder queryBuilder)
{
return Query.FirstOrDefault();
}
///
/// Execute query and return the result as list
///
///
public List ToList()
{
return Query.ToList();
}
///
/// Joins with the specified t key.
///
/// The type of the targ
Expected usage:
List users = userQueryBuilder
.Active()
.IsMale()
.AgeGreaterThan(25)
.Include(u => u.UserProfile, u => u.Purchase);
User user = userQueryBuilder
.Active()
.HasIds(new List{1})
.Include(u => u.UserProfile);As you see from the expected behavior, my purpose is to make the code descriptive and easy to understand for all developers and most importantly, the code need to be flexible and welcome-for-changes. For instance, I could add
IsMale() to any of existing query without changing any lines of code.QueryBuilder.cs (base class)
```
public abstract class QueryBuilder : IDisposable where TEntity : class, new()
{
protected DbContext Context { get; set; }
///
/// The query object
///
protected static IQueryable Query;
///
/// Initializes a new instance of the class.
///
protected QueryBuilder(DbContext context)
{
Context = context;
Query = Context.Set();
}
///
/// Performs an implicit conversion from to .
///
/// The query builder.
///
/// The result of the conversion.
///
public static implicit operator List(QueryBuilder queryBuilder)
{
return Query.ToList();
}
///
/// Performs an implicit conversion from to .
///
/// The query builder.
///
/// The result of the conversion.
///
public static implicit operator TEntity(QueryBuilder queryBuilder)
{
return Query.FirstOrDefault();
}
///
/// Execute query and return the result as list
///
///
public List ToList()
{
return Query.ToList();
}
///
/// Joins with the specified t key.
///
/// The type of the targ
Solution
Casting should never have side effects. Right now, calling
You should have an
The
Don't make
I want to point out that
I think your
ToList in your cast will send the query to the database. This is the most obscure way to execute a query and that's dangerous.You should have an
Execute method, which well.. executes the query and returns the result.Active should be IsActive, to respect the boolean type of the method.The
Dispose of your query shouldn't dispose the DbContext. The QueryBuilder is dependant on DbContext, not upside down. When disposing, you should dispose of dependant objects. So now, the QueryBuilder shouldn't implement IDisposable, it serves no purpose.Don't make
Query static, that's something you'll regret. Imagine you have two queries to run at the same time, both your queries will step on each other and you'll have a mess of a query. Keep Query an instance property. Because while we're at it, you should make it a protected property. Instance variable shouldn't be anything other than private.I want to point out that
Contains using Entity Framework Linq To Entities is pretty slow. There's nothing much you can do about it, but just be aware that you might have problems with this someday.I think your
Context property should be private set;. You offer the possibility to your child classes to set the Context by the protected constructor, don't let them set it back.Context
StackExchange Code Review Q#114126, answer score: 6
Revisions (0)
No revisions yet.