开发者

Implementing a flexible searching infrastructure using nHibernate

My aim is to implement a quite generic search mechanism. Here's the general idea:

you can search based on any property of the entity you're searching for (for example- by Employee's salary, or by Department name etc.).

Each property you can search by is represented by a class, which inherits from EntityProperty:

public abstract class EntityProperty<T>
        where T:Entity
    {
        public enum Operator
        {
            In,
            NotIn,
        }

        /// <summary>
        /// Name of the property
        /// </summary>
        public abstract string Name { get; }
        //Add a search term to the given query, using the given values
        public abstract IQueryable<T> AddSearchTerm(IQueryable<T> query, IEnumerable<object> values);

        public abstract IQueryable<T> AddSortingTerm(IQueryable<T> query);


        protected Operator _operator = Operator.In;
        protected bool _sortAscending = false;

        public EntityProperty(Operator op)
        {
            _operator = op;
        }

        //use this c'tor if you're using the property for sorting only
        public EntityProperty(bool sortAscending)
        {
            _sortAscending = sortAscending;
        }
    }

all of the properties you're searching / sorting by are stored in a simple collection class:

public class SearchParametersCollection<T>
        where T: Entity
    {

        public IDictionary<EntityProperty<T>,IEnumerable<object>> SearchProperties { get; private set; }
        public IList<EntityProperty<T>> SortProperties { get; private set; }

        public SearchParametersCollection()
        {
            SearchProperties = new Dictionary<EntityProperty<T>, IEnumerable<object>>();
            SortProperties = new  List<EntityProperty<T>>();
        }

        public void AddSearchProperty(EntityProperty<T> property, IEnumerable<object> values)
        {
            SearchProperties.Add(property, values);
        }

        public void AddSortProperty(EntityProperty<T> property)
        {
            if (SortProperties.Contains(property))
            {
                throw new ArgumentException(string.Format("property {0} already exists in sorting order", property.Name));
            }
            SortProperties.Add(property);
        }


    }

now, all the repository class has to do is:

protected IEnumerable<T> Search<T>(SearchParametersCollection<T> parameters)
            where T : Entity
        {
            IQueryable<T> query = this.Session.Linq<T>();
            foreach (var searchParam in parameters.SearchProperties)
            {
                query = searchParam.Key.AddSearchTerm(query, searchParam.Value);
            }

            //add order
            foreach (var sortParam in parameters.SortProperties)
   开发者_如何转开发         {
                query = sortParam.AddSortingTerm(query);
            }

            return query.AsEnumerable();
        }

for example, here's a class which implements searching a user by their full name:

public class UserFullName : EntityProperty<User>
    {

        public override string Name
        {
            get { return "Full Name"; }
        }

        public override IQueryable<User> AddSearchTerm(IQueryable<User> query, IEnumerable<object> values)
        {
            switch (_operator)
            {
                case Operator.In:
                    //btw- this doesn't work with nHibernate... :(
                    return query.Where(u => (values.Cast<string>().Count(v => u.FullName.Contains(v)) > 0));
                case Operator.NotIn:
                    return query.Where(u => (values.Cast<string>().Count(v => u.FullName.Contains(v)) == 0));
                default:
                    throw new InvalidOperationException("Unrecognized operator " + _operator.ToString());
            }

        }

        public override IQueryable<User> AddSortingTerm(IQueryable<User> query)
        {
            return (_sortAscending) ? query.OrderBy(u => u.FullName) : query.OrderByDescending(u => u.FullName);
        }


        public UserFullName(bool sortAscending)
            : base(sortAscending)
        {

        }

        public UserFullName(Operator op)
            : base(op)
        {

        }
    }

my questions are:

1. firstly- am I even on the right track? I don't know of any well-known method for achieving what I want, but I may be wrong...

2. it seems to me that the Properties classes should be in the domain layer and not in the DAL, since I'd like the controller layers to be able to use them. However, that prevents me from using any nHibernate-specific implementation of the search (i.e any other interface but Linq). Can anybody think of a solution that would enable me to utilize the full power of nH while keeping these classes visible to upper layers? I've thought about moving them to the 'Common' project, but 'Common' has no knowledge of the Model entities, and I'd like to keep it that way.

3. as you can see by my comment for the AddSearchTerm method- I haven't really been able to implement 'in' operator using nH (I'm using nH 2.1.2 with Linq provider). any sugggestions in that respect would be appriciated. (see also my question from yesterday).

thanks!


If you need good API to query NHIbernate objects then you should use ICriteria (for NH 2.x) or QueryOver (for NH 3.x).

You over complicating DAL with these searches. Ayende has a nice post about why you should not do it


I ended up using query objects, which greatly simplified things.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜