1

I write a multi-tier app (Data Access Layer,Business Layer, UILayer) in DAL I use NHibernate to access to DB

then in BusinessLayer I access to DAL to get some data using interface for repository.

And sometimes I need to get data by difficult query. Now I use Linq to extracted data.

I try to wrote metods in repository that implements Criterion logic,but grows up number of overridden methods is wrong way, I think.

public T GetBy(System.Linq.Expressions.Expression<Func<T, bool>> func)
{
    return _session.QueryOver<T>().Where(func).List().FirstOrDefault();
}
public IEnumerable<T> GetAllWhere(System.Linq.Expressions.Expression<Func<T, bool>> func)
{
    return _session.QueryOver<T>().Where(func).List();
}
public IEnumerable<T> GetAllIn(ICollection collection)
{
    return _session.QueryOver<T>().Where(x => x.Id.IsIn(collection)).List<T>();
}

but more and more I need to create difficult criteria to query (I dont like idea of extract all data and filtering it by Linq) example for criteria that I want to use in BL:

repository.GetMany(x=>
                     x.Prop1.IsIn(Collection1)&&
                     x.Prop2.IsIn(Collection2)&&
                     x.Prop3.IsLike("SomeData")).ToList();

So how I can to use NHibernate.Criterion logic in BL without referensing?

----SOLUTION---

I created class Filter

public class CustomFilter<T>
        where T : Entity
    {
        public class FilterQuery
        {
            public enum QueryType
            {
                In, Where, Like, LikeIn,Top
            }

            public QueryType QType { get; set; }
            public Expression<Func<T,object>> Selector { get; set; }
            public object Data { get; set; }

            public FilterQuery(QueryType qType, Expression<Func<T,object>> selector, object data)
            {
                QType = qType;
                Selector = selector;
                Data = data;
            }
        }

        public  List<FilterQuery> Queries = new List<FilterQuery>();
        public  CustomFilter<T> In(Expression<Func<T, object>> selector, ICollection collection) 
        {
            var query = new FilterQuery(FilterQuery.QueryType.In, selector, collection);
            Queries.Add(query);
            return this;
        }

        public CustomFilter<T> Top(int i)
        {
            Queries.Add(new FilterQuery(FilterQuery.QueryType.Top, null,i));
            return this;
        }
    }

And wrote Method in Repository

public IEnumerable<T> GetByFilter(CustomFilter<T> filter)
        {

            IQueryOver<T,T> query = _session.QueryOver<T>();

            foreach (var _filter in filter.Queries)
            {

                switch (_filter.QType)
                {
                   case CustomFilter<T>.FilterQuery.QueryType.In:
                        query = query.WhereRestrictionOn(_filter.Selector)
                                     .IsIn((ICollection)_filter.Data);
                        break;
                   case CustomFilter<T>.FilterQuery.QueryType.Top :
                        query = (IQueryOver<T, T>) query.Take((int)_filter.Data);
                        break;
                }
            }
            return query != null ? query.List<T>() : null;

        }

To Use filter I call

 var docs = docRepo.GetByFilter(
                        new CustomFilter<Doc>()
                        .In(x => x.Archive, arcs)
                        .Top(200)
                        ).ToList();
Dr_klo
  • 469
  • 6
  • 19

1 Answers1

1

There is not an easy way how to turn that feature on or off. In general, there are three approaches, I'd say.

The first is MS and its LINQ. To do that, just use

var query = session.Query<TEntity>()

we (upper layers) can now query that entity as needed...just using LINQ API. Disadvantages is not full coverage of LINQ in NHibernate provider - but it is not so bad at the end. What I do not like here is that you publish lot of your domain ... without some attributes or other AOP ... with one IQueryable you can get the whole system.

The second is the Ayendes approach: The DAL should go all the way to UI. Small cite:

... My current approach is quite different. I define queries, which contains the actual business logic for the query, but I pass that query onward to higher levels of my application. Any additional processing on the query (projections, paging, sorting, etc) can be done. In that way, my queries are closed for modification and open for extension, since they can be further manipulated by higher level code...

read more about it also here: How to organize database layer using NHibernate

The third option is to use some custom solution. E.g. my preferred is to introduce some abstraction, called IFilter, which is passed through layers. Filled on the UI (could be client over API), checked on the BL with all the rules (including some AOP for security) and consumed in DAL .. and converted into the NHibernate Criteria API.

Hope this review helps a bit ... because there is no easy way to say: turn it here on.

Community
  • 1
  • 1
Radim Köhler
  • 122,561
  • 47
  • 239
  • 335
  • The third option is best way for me. I Can write Linq-based abstraction for filter, but I don't know how to convert it to criteria. Can you suggest how to use the abstraction of a concrete example? – Dr_klo Sep 08 '15 at 11:23
  • I do use third approach as well. I was talking about a `class Filter {}` which instance filter will be passed through layers. It could in extreme be just `IDictionary()`. Then, we just fill it on upper layers e.g. `filter.FirstName = "Abc"` or filter["FirstName"] = "Abc" and iterate that on Data layer.. to create criteria (in case there is some value only) ... `criteria.Add(Expression.Like("PropertyName", value, MatchMode.Start))`. The base logic of this is as simple. Some holder of searched values - some parser and converter... Later it could get more complex (adding join...) – Radim Köhler Sep 08 '15 at 11:28