patterncsharpMinor
Fluent Repository/QueryBuilder
Viewed 0 times
querybuilderfluentrepository
Problem
I've been toying around making my repositories a bit more fluent for a while. I was ending up with crazy long method names when I had to query on multiple conditions. So I've been working on a way to query fluently.
Usage
Insert/Update/Delete
Looks and behaves exactly like a normal repository.
Querying
Allows you to call any query methods, narrowing down the query more and more as you go and can execute the query synchronously or asynchronously.
So the code behind this:
Entity Base Class
Repository Contracts
```
public interface IPersistableRepository where TEntity : BaseEntity
{
void Insert(TEntity entity);
void Update(TEntity entity);
void Remove(TEntity entity);
}
public interface IQueryableRepository
where TEntity : BaseEntity
where TQueryBuilder : class, IQueryBuilder
{
TQueryBuilder Query();
}
public interface IQueryBuilder
where TEntity : BaseEntity
where TQueryBuilder : class
{
TQueryBuilder ById(int id);
...
TQueryBuilder Include(Expression> prop);
TQueryBuilder OrderBy(Expression> prop);
TQueryBuilder OrderByDescending(Expression> prop);
TQueryBuilder Take(int count);
TQueryBuilder After(int id);
TQueryBuilder Before(int id);
TEntity ToEntity();
Task ToEntityAsync();
IEnumerable ToEntities();
Task> To
Usage
Insert/Update/Delete
Looks and behaves exactly like a normal repository.
var personRepository = new PersonRepository(dbContext);
personRepository.Insert(new Person {...});
personRepository.Update(person);
personRepository.Remove(person);Querying
Allows you to call any query methods, narrowing down the query more and more as you go and can execute the query synchronously or asynchronously.
var person = await personRepository.Query()
.ByFirstName("neil")
.ByLastName("smith")
.Include(m => m.Addresses)
.OrderBy(m => m.LastName)
.Take(5)
.ToEntitiesAsync();
var person = personRepository.Query()
.ById(99)
.ToEntity();
var person = await personRepository.Query()
.WhereFirstNameContains("jr")
.ToEntitiesAsync();So the code behind this:
Entity Base Class
public abstract class BaseEntity
{
public int Id { get; set; }
public DateTime DateCreated { get; set; }
}Repository Contracts
```
public interface IPersistableRepository where TEntity : BaseEntity
{
void Insert(TEntity entity);
void Update(TEntity entity);
void Remove(TEntity entity);
}
public interface IQueryableRepository
where TEntity : BaseEntity
where TQueryBuilder : class, IQueryBuilder
{
TQueryBuilder Query();
}
public interface IQueryBuilder
where TEntity : BaseEntity
where TQueryBuilder : class
{
TQueryBuilder ById(int id);
...
TQueryBuilder Include(Expression> prop);
TQueryBuilder OrderBy(Expression> prop);
TQueryBuilder OrderByDescending(Expression> prop);
TQueryBuilder Take(int count);
TQueryBuilder After(int id);
TQueryBuilder Before(int id);
TEntity ToEntity();
Task ToEntityAsync();
IEnumerable ToEntities();
Task> To
Solution
I quite like the concept. The only thing that stands out for me initially is that I would probably leave the wrapping methods the same as their wrapped name.
i.e.
rather than
I guess my main reasoning is that
Neat idea thought. Be interested to see what others think.
i.e.
TEntity FirstOrDefault()
Task FirstOrDefaultAsync()
IEnumerable All()
Task> AsEnumerable();rather than
TEntity ToEntity();
Task ToEntityAsync();
IEnumerable ToEntities();
Task> ToEntitiesAsync();I guess my main reasoning is that
FirstOrDefault makes it clear what we are retrieving where as ToEntity() leaves it open to thought between Single() and First().Neat idea thought. Be interested to see what others think.
Code Snippets
TEntity FirstOrDefault()
Task<TEntity> FirstOrDefaultAsync()
IEnumerable<TEntity> All()
Task<IEnumerable<TEntity>> AsEnumerable();TEntity ToEntity();
Task<TEntity> ToEntityAsync();
IEnumerable<TEntity> ToEntities();
Task<IEnumerable<TEntity>> ToEntitiesAsync();Context
StackExchange Code Review Q#54120, answer score: 2
Revisions (0)
No revisions yet.