首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Fluent Repository/QueryBuilder

Fluent Repository/QueryBuilder
EN

Code Review用户
提问于 2014-06-13 02:17:52
回答 1查看 3.7K关注 0票数 7

一段时间以来,我一直在玩使我的存储库更加流畅。当我不得不对多个条件进行查询时,我得到了疯狂的长方法名称。因此,我一直在研究一种能够流畅地查询的方法。

使用

Insert/Update/Delete

看起来和行为完全像一个普通的存储库。

代码语言:javascript
复制
var personRepository = new PersonRepository(dbContext);

personRepository.Insert(new Person {...});
personRepository.Update(person);
personRepository.Remove(person);

查询

允许您调用任何查询方法,随着时间的推移缩小查询范围,并且可以同步或异步地执行查询。

代码语言:javascript
复制
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();

所以下面的代码是:

实体基类

代码语言:javascript
复制
public abstract class BaseEntity
{
    public int Id { get; set; }
    public DateTime DateCreated { get; set; }
}

仓库合同

代码语言:javascript
复制
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();
}

库基类

代码语言:javascript
复制
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();
    }
}

存储库& QueryBuilder接口

代码语言:javascript
复制
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);
    ...
}

库实现

代码语言:javascript
复制
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方法都由一个接口强制执行,因此它是可注入的和可测试的。我相信,使用任意组合的查询方法的能力使其具有更好的查询语法。每个存储库都可以实现IPersistableRepositoryIQueryableRepository或两者。

但我不是专家。我有遗漏什么吗?我不应该这样做吗?你告诉我我的设计有什么问题。

EN

回答 1

Code Review用户

回答已采纳

发布于 2014-06-15 03:34:22

我很喜欢这个概念。对我来说,最初最突出的一点是,我可能会将包装方法与包装方法的名称相同。

代码语言:javascript
复制
TEntity FirstOrDefault()
Task<TEntity> FirstOrDefaultAsync()
IEnumerable<TEntity> All()
Task<IEnumerable<TEntity>> AsEnumerable();

而不是

代码语言:javascript
复制
TEntity ToEntity();
Task<TEntity> ToEntityAsync();
IEnumerable<TEntity> ToEntities();
Task<IEnumerable<TEntity>> ToEntitiesAsync();

我猜我的主要推理是,FirstOrDefault清楚地说明了我们正在检索的位置,因为ToEntity()Single()First()之间的思想开放。

不错的想法。想看看别人是怎么想的。

票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/54120

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档