使用nHibernate实现灵活的搜索基础架构

时间:2011-03-31 08:09:18

标签: nhibernate design-patterns linq-to-nhibernate software-design

我的目标是实现一个非常通用的搜索机制。这是一般的想法:
您可以根据您正在搜索的实体的任何属性进行搜索(例如,通过员工的工资或部门名称等)。
您可以搜索的每个属性都由一个类表示,该类继承自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;
        }
    }

您正在搜索/排序的所有属性都存储在一个简单的集合类中:

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);
        }


    }

现在,所有存储库类必须做的是:

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();
        }

例如,这是一个实现按用户名搜索用户的类:

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)
        {

        }
    }

我的问题是:
1.首先,我是否走在正确的轨道上?我不知道有任何众所周知的方法来实现我想要的东西,但我可能是错的...
2.在我看来,属性类应该在域层而不是在DAL中,因为我希望控制器层能够使用它们。但是,这阻止了我使用任何特定于nHibernate的搜索实现(即Linq之外的任何其他接口)。任何人都可以想到一个解决方案,使我能够利用nH的全部功能,同时保持这些类对上层可见吗?我已经考虑将它们移到'Common'项目中,但是'Common'不知道Model实体,我想保持这种方式。
3.正如你对AddSearchTerm方法的评论所看到的那样 - 我实际上并没有能够使用nH实现'in'运算符(我正在使用nH 2.1.2和Linq提供程序)。在这方面的任何建议都会受到批评。 (另见my question from yesterday) 谢谢!

2 个答案:

答案 0 :(得分:1)

如果您需要良好的API来查询NHIbernate对象,那么您应该使用ICriteria(对于NH 2.x)或QueryOver(对于NH 3.x)。

您通过这些搜索使DAL变得复杂。 Ayende有一个很好的postwhy你不应该这样做

答案 1 :(得分:0)

我最终使用query objects,这大大简化了事情。