一段时间以来,我一直在玩使我的存储库更加流畅。当我不得不对多个条件进行查询时,我得到了疯狂的长方法名称。因此,我一直在研究一种能够流畅地查询的方法。
看起来和行为完全像一个普通的存储库。
var personRepository = new PersonRepository(dbContext);
personRepository.Insert(new Person {...});
personRepository.Update(person);
personRepository.Remove(person);允许您调用任何查询方法,随着时间的推移缩小查询范围,并且可以同步或异步地执行查询。
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();所以下面的代码是:
public abstract class BaseEntity
{
public int Id { get; set; }
public DateTime DateCreated { get; set; }
}public interface IPersistableRepository<TEntity> where TEntity : BaseEntity
{
void Insert(TEntity entity);
void Update(TEntity entity);
void Remove(TEntity entity);
}
public interface IQueryableRepository<TEntity, out TQueryBuilder>
where TEntity : BaseEntity
where TQueryBuilder : class, IQueryBuilder<TEntity, TQueryBuilder>
{
TQueryBuilder Query();
}
public interface IQueryBuilder<TEntity, out TQueryBuilder>
where TEntity : BaseEntity
where TQueryBuilder : class
{
TQueryBuilder ById(int id);
...
TQueryBuilder Include<T>(Expression<Func<TEntity, T>> prop);
TQueryBuilder OrderBy<T>(Expression<Func<TEntity, T>> prop);
TQueryBuilder OrderByDescending<T>(Expression<Func<TEntity, T>> prop);
TQueryBuilder Take(int count);
TQueryBuilder After(int id);
TQueryBuilder Before(int id);
TEntity ToEntity();
Task<TEntity> ToEntityAsync();
IEnumerable<TEntity> ToEntities();
Task<IEnumerable<TEntity>> ToEntitiesAsync();
}public abstract class PersistableRepository<TEntity> : IPersistableRepository<TEntity>
where TEntity : BaseEntity
{
private readonly IDbContext _context;
protected PersistableRepository(IDbContext context) {
_context = context;
}
protected IDbSet<TEntity> DbSet {
get { return _context.Set<TEntity>(); }
}
public void Insert(TEntity entity) {
DbSet.Add(entity);
}
public void Update(TEntity entity) {
_context.Entry(entity).State = EntityState.Modified;
}
public void Remove(TEntity entity) {
DbSet.Remove(entity);
}
}
public abstract class QueryBuilder<TEntity, TQueryBuilder>
: IQueryBuilder<TEntity, TQueryBuilder>
where TEntity : BaseEntity
where TQueryBuilder : class
{
protected QueryBuilder(IQueryable<TEntity> query) {
Query = query;
}
protected IQueryable<TEntity> Query { get; set; }
public TQueryBuilder ById(int id) {
Query = Query.Where(m => m.Id == id);
return this as TQueryBuilder;
}
public TQueryBuilder Include<T>(Expression<Func<TEntity, T>> prop) {
Query = Query.Include(prop);
return this as TQueryBuilder;
}
public TQueryBuilder OrderBy<T>(Expression<Func<TEntity, T>> prop) {
Query = Query.OrderBy(prop);
return this as TQueryBuilder;
}
public TQueryBuilder OrderByDescending<T>(Expression<Func<TEntity, T>> prop) {
Query = Query.OrderByDescending(prop);
return this as TQueryBuilder;
}
public TQueryBuilder Take(int count) {
Query = Query.Take(count);
return this as TQueryBuilder;
}
public TQueryBuilder After(int id) {
Query = Query.Where(m => m.Id >= id);
return this as TQueryBuilder;
}
public TQueryBuilder Before(int id) {
Query = Query.Where(m => m.Id <= id);
return this as TQueryBuilder;
}
public TEntity ToEntity() {
return Query.FirstOrDefault();
}
public async Task<TEntity> ToEntityAsync() {
return await Query.FirstOrDefaultAsync();
}
public IEnumerable<TEntity> ToEntities() {
return Query.ToList();
}
public async Task<IEnumerable<TEntity>> ToEntitiesAsync() {
return await Query.ToListAsync();
}
}public interface IPersonRepository
: IPersistableRepository<Person>, IQueryableRepository<Person, IPersonQueryBuilder>
{
}
public interface IPersonQueryBuilder
: IQueryBuilder<Person, IPersonQueryBuilder>
{
IPersonQueryBuilder ByFirstName(string firstName);
IPersonQueryBuilder ByLastName(string lastName);
IPersonQueryBuilder ByAge(int age);
IPersonQueryBuilder WhereFirstNameContains(string val);
...
}public class PersonRepository : PersistableRepository<Cohort>, IPersonRepository
{
public PersonRepository(IDbContext context) : base(context)
{
}
public IPersonQueryBuilder Query() {
return new PersonQueryBuilder(DbSet.AsQueryable());
}
}
public class PersonQueryBuilder
: QueryBuilder<Person, IPersonQueryBuilder>, IPersonQueryBuilder
{
public PersonQueryBuilder(IQueryable<Person> query) : base(query)
{
}
public IPersonQueryBuilder ByFirstName(string firstName) {
Query = Query.Where(m => m.FirstName == firstName);
return this;
}
public IPersonQueryBuilder ByLastName(string lastName) {
Query = Query.Where(m => m.LastName == lastName);
return this;
}
public IPersonQueryBuilder ByAge(int age) {
Query = Query.Where(m => m.Age == age);
return this;
}
}一切都有它的位置。所有实体类共享的查询方法属于抽象QueryBuilder。用于持久化数据的方法属于抽象PerstiableRepository。所有的存储库和QueryBuilder方法都由一个接口强制执行,因此它是可注入的和可测试的。我相信,使用任意组合的查询方法的能力使其具有更好的查询语法。每个存储库都可以实现IPersistableRepository、IQueryableRepository或两者。
但我不是专家。我有遗漏什么吗?我不应该这样做吗?你告诉我我的设计有什么问题。
发布于 2014-06-15 03:34:22
我很喜欢这个概念。对我来说,最初最突出的一点是,我可能会将包装方法与包装方法的名称相同。
即
TEntity FirstOrDefault()
Task<TEntity> FirstOrDefaultAsync()
IEnumerable<TEntity> All()
Task<IEnumerable<TEntity>> AsEnumerable();而不是
TEntity ToEntity();
Task<TEntity> ToEntityAsync();
IEnumerable<TEntity> ToEntities();
Task<IEnumerable<TEntity>> ToEntitiesAsync();我猜我的主要推理是,FirstOrDefault清楚地说明了我们正在检索的位置,因为ToEntity()让Single()和First()之间的思想开放。
不错的想法。想看看别人是怎么想的。
https://codereview.stackexchange.com/questions/54120
复制相似问题