5
In my last questions I was creating some methods to automate some queries. It’s legal, but now I need to control access to methods by specifying a sequence.
When using the method Select(), i cannot have access to it again. After the Where() method I cannot have access to the methods Leftjoin() and Innerjoin(). That is until the method is finally arrived at Tolist();
My class will work by creating sql s commands in a chained way like Linq to Lambda.
Thus:
var pessoas = pessoaDAO.Select()
.LeftJoin(x => x.Telefones, (telefones, pessoa) => telefones.PessoaId == pessoa.Id)
.Where(x => x.Ativo)
.OrderBy(x => x.Nome)
.ToList();
It would be wrong to allow this:
var pessoas = pessoaDAO
.Select(x =>
x.Id,
x => x.Pessoa,
x => x.Ativo,
x => x.Telefones)
.Where(x => x.Ativo)
.Select()
.ToList();
That is, a Select() after a Where() and even a Select() have already been called.
To solve this question, I am trying to separate the methods by interface. So:
public interface IQueryBuilder<TModel> : ICustomQueryBuilder<TModel>
where TModel : class
{
ICustomQueryBuilder<TModel> Select();
ICustomQueryBuilder<TModel> Select(params Expression<Func<TModel, object>>[] members);
}
public interface ICustomQueryBuilder<TModel> :
IQueryBuilderJoin<TModel>,
IQueryBuilderFilter<TModel>,
IQueryBuilderOrder<TModel>,
IQueryBuilderExecute<TModel>
where TModel : class
{
}
Iquerybuilder which will be the interface used to implement my inherited class of Icustomquerybuilder, and the methods Select() return the type Icustomquerybuilder so that then I cannot have access to the method Select() again.
Iquerybuilder inherits from Icustomquerybuilder and this from the other interfaces because I need to have a type implemented in the class to make the return.
Already, Icustomquerybuilder inherits from:
public interface IQueryBuilderJoin<TModel> :
IQueryBuilderFilter<TModel>,
IQueryBuilderOrder<TModel>,
IQueryBuilderExecute<TModel>
where TModel : class
{
IQueryBuilderJoin<TModel> LeftJoin<TMemberJoin>(
Expression<Func<TModel, TMemberJoin>> member,
Expression<Func<TMemberJoin, TModel, bool>> filter);
IQueryBuilderJoin<TModel> LeftJoin<TMemberJoin>(
Expression<Func<TModel, IEnumerable<TMemberJoin>>> member,
Expression<Func<TMemberJoin, TModel, bool>> filter);
IQueryBuilderJoin<TModel> InnerJoin<TMemberJoin>(
Expression<Func<TModel, TMemberJoin>> member,
Expression<Func<TMemberJoin, TModel, bool>> filter);
IQueryBuilderJoin<TModel> InnerJoin<TMemberJoin>(
Expression<Func<TModel, IEnumerable<TMemberJoin>>> member,
Expression<Func<TMemberJoin, TModel, bool>> filter);
}
In sequence of:
public interface IQueryBuilderFilter<TModel> : IQueryBuilderOrder<TModel>
where TModel : class
{
IQueryBuilderFilter<TModel> Where(Expression<Func<TModel, bool>> filter);
}
public interface IQueryBuilderOrder<TModel> : IQueryBuilderExecute<TModel>
where TModel : class
{
IQueryBuilderOrder<TModel> OrderBy(params Expression<Func<TModel, object>>[] members);
IQueryBuilderOrder<TModel> OrderByDesc(params Expression<Func<TModel, object>>[] members);
}
public interface IQueryBuilderExecute<TModel>
where TModel : class
{
List<TModel> ToList();
}
Finally, it would have:
public class QueryBuilder<TModel> : IQueryBuilder<TModel>
where TModel : class
{
// ... implementações
}
It is correct to do this kind of implementation?
To better illustrate the need for return, example:
public class PessoaDAO : GenericDAO<Pessoa>
{
// ... outros métodos
public IQueryBuilderFilter<Pessoa> Where(Expression<Func<TModel, bool>> filter)
{
// ... implementações
return this;
}
// ... outros métodos
}
That is, this is necessary for the chaining of the methods and the type should be a type already implemented, otherwise the automatic cast will not be accepted.
Interface segregation is a good OO modeling practice. Applying this to DSL construction is very interesting. Several frameworks use the technique and I even used it a few times. Depending on the complexity of the solution it is interesting to make a state diagram to illustrate the behavior. With each state representing an interface you can make it very clear through arrows which methods continue on the same interface (state) and which cause the transition to a new state with no return. You can also see in this if the code meets everything you need and is consistent.
– utluiz