HighwayFramework / Highway.Data

The fastest and smoothest way to great architecture

Home Page:http://hwyfwk.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Concat for specification

forcewake opened this issue · comments

Hello.
It is an issue-question - what do you thing about the concatenation for several Queries?
For example:

    public class DriversByName : Query<Driver>
    {
        public DriversByName(string name)
        {
            ContextQuery = c => c.AsQueryable<Driver>().Where(x => x.Name == name);
        }
    }
    public class DriversByAge : Query<Driver>
    {
        public DriversByAge(int minAge, int maxAge)
        {
            ContextQuery = c => c.AsQueryable<Driver>().Where(x => x.Age >= minAge && x.Age <= maxAge);
        }
    }
        public IEnumerable<Driver> GetDrivers(string name, int minAge, int maxAge)
        {
            var byNameQuery = new DriverByName(name);
            var byAgeQuery = new DriversByAge(minAge, maxAge);
            return _repository.Find(byNameQuery.And(byAgeQuery));
        }

I'm asking about .And() funtionality.
For example:

  • OrSpecification
  • NotSpecification
  • FilterSpecification
  • AndSpecification

It is possible with one big caveat, the Queryable under the specification would have to be compatible. Other than that, it is fairly easy to implement.

By compatible I mean that it would have to have the same root type and the same return type.

Ok, great.

One more thing - what do you thing about the moving parameters for the Query from constructor to properties - it will give us possibility for creating fluent queries, for example:

public class OrderSalesQuery : Query<Order>
{
    public string Country { get; set; }
    public DateTime FromDate { get; set; }
    public DateTime ToDate { get; set; }

    public override Expression<Func<Order, bool>> Query()
    {
        return (x => 
            x.OrderDate >= FromDate &&
            x.OrderDate <= ToDate &&
            x.ShipCountry == Country);
    }
}

And then, if you will accept the idea about And and other specification we can write such code:

    public class OrderSalesQuery : Query<Order>
    {
        public CustomerLogisticsQuery FromCountry(string country)
        {
            Add(x => x.Country == country);
            return this;
        }

        public CustomerLogisticsQuery FromDate(DateTime fromDate)
        {   
            Add(x =>  x.OrderDate >= fromDate);
            return this;
        }

        public CustomerLogisticsQuery ToDate(DateTime toDate)
        {   
            Add(x =>  x.OrderDate <= ToDate);
            return this;
        }
    }

And query will look like this:

var query2 = new OrderSalesQuery()
    .FromCountry("Minsk")
    .FromDate(DateTime.Now.AddDays(-1)
    .ToDate(DateTime.Now);

I'm a bit concerned about this path, honestly. It seems to just be us
re-inventing passing the full Queryable back to the caller from the
repository, with some minor window dressing. If that is what you're after,
and getting away from the limited scope of queries that Query objects are
meant to enforce, then I'd just suggest using this extension method:

public static IQueryable Get(this IRepository repo) { return
((IDataContext) repo.Context).AsQueryable(); }

That will allow you to make calls like:

repo.Get().Where(e => e.OrderDate => DateTime.Now);

or the like. But we deliberately put in blocks to avoid this model (hence
IUnitOfWork) in the last major revision, so I'm a bit skeptical to the
value of these added methods as described above.

Tim Rayburn
Principal Consultant, Improving Enterprises - Microsoft Integration MVP -
Cell 817 760 0002 - Blog http://TimRayburn.net - Tim@TimRayburn.net

On Wed, Aug 26, 2015 at 11:16 AM, Pavel Nosovich notifications@github.com
wrote:

Ok, great.

One more thing - what do you thing about the moving parameters for the
Query from constructor to properties - it will give us possibility for
creating fluent queries, for example:

public class OrderSalesQuery : Query
{
public string Country { get; set; }
public DateTime FromDate { get; set; }
public DateTime ToDate { get; set; }

public override Expression<Func<Order, bool>> Query()
{
    return (x =>
        x.OrderDate >= FromDate &&
        x.OrderDate <= ToDate &&
        x.ShipCountry == Country);
}

}

And then, if you will accept the idea about And and other specification
we can write such code:

public class OrderSalesQuery : Query<Order>
{
    public CustomerLogisticsQuery FromCountry(string country)
    {
        Add(x => x.Country == country);
        return this;
    }

    public CustomerLogisticsQuery FromDate(DateTime fromDate)
    {
        Add(x =>  x.OrderDate >= fromDate);
        return this;
    }

    public CustomerLogisticsQuery ToDate(DateTime toDate)
    {
        Add(x =>  x.OrderDate <= ToDate);
        return this;
    }
}

And query will look like this:

var query2 = new OrderSalesQuery()
.FromCountry("Minsk")
.FromDate(DateTime.Now.AddDays(-1)
.ToDate(DateTime.Now);


Reply to this email directly or view it on GitHub
#89 (comment)
.

I will have to agree with Tim on the Fluent syntax. I do see some value in combining query objects where you want the intersect of two sets, or the exception set.