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

使用nHibernate实现灵活的搜索基础架构,nhibernate,design-patterns,linq-to-nhibernate,software-design,Nhibernate,Design Patterns,Linq To Nhibernate,Software Design,我的目标是实现一个非常通用的搜索机制。大致思路如下: 您可以根据正在搜索的实体的任何属性进行搜索(例如,根据员工的工资或部门名称等)。 可以搜索的每个特性都由一个类表示,该类继承自EntityProperty: public abstract class EntityProperty<T> where T:Entity { public enum Operator { In, No

我的目标是实现一个非常通用的搜索机制。大致思路如下:
您可以根据正在搜索的实体的任何属性进行搜索(例如,根据员工的工资或部门名称等)。
可以搜索的每个特性都由一个类表示,该类继承自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;
        }
    }
公共抽象类EntityProperty
其中T:实体
{
公共枚举运算符
{
在里面
诺丁,
}
/// 
///物业名称
/// 
公共抽象字符串名称{get;}
//使用给定的值向给定的查询添加搜索项
公共抽象IQueryable AddSearchTerm(IQueryable查询,IEnumerable值);
公开摘要IQueryable AddSortingTerm(IQueryable查询);
受保护的运算符_Operator=运算符.In;
受保护的bool\u sortAscending=false;
公共实体属性(操作员op)
{
_操作员=op;
}
//如果仅将属性用于排序,请使用此c'tor
公共实体属性(布尔分类)
{
_排序设置=排序设置;
}
}
搜索/排序所依据的所有属性都存储在一个简单的集合类中:

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


    }
公共类搜索参数集合
其中T:实体
{
公共IDictionary SearchProperties{get;private set;}
公共IList SortProperties{get;private set;}
公共搜索参数集合()
{
SearchProperties=newdictionary();
SortProperties=新列表();
}
public void AddSearchProperty(EntityProperty属性,IEnumerable值)
{
SearchProperties.Add(属性、值);
}
public void AddSortProperty(EntityProperty属性)
{
if(SortProperties.Contains(property))
{
抛出新的ArgumentException(string.Format(“属性{0}已按排序顺序存在”,property.Name));
}
SortProperties.Add(属性);
}
}
现在,repository类所要做的就是:

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();
        }
受保护的IEnumerable搜索(搜索参数集合参数)
其中T:实体
{
IQueryable query=this.Session.Linq();
foreach(parameters.SearchProperties中的var searchParam)
{
query=searchParam.Key.AddSearchTerm(查询,searchParam.Value);
}
//添加订单
foreach(parameters.SortProperties中的var sortpram)
{
query=sortParam.AddSortingTerm(查询);
}
返回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)
        {

        }
    }
公共类UserFullName:EntityProperty { 公共重写字符串名 { 获取{返回“全名”;} } 公共覆盖IQueryable AddSearchTerm(IQueryable查询,IEnumerable值) { 开关(_操作员) { 案例操作员。在: //顺便说一句-这不适用于nHibernate…:( 返回query.Where(u=>(values.Cast().Count(v=>u.FullName.Contains(v))>0)); case Operator.NotIn: 返回query.Where(u=>(values.Cast().Count(v=>u.FullName.Contains(v))==0)); 违约: 抛出新的InvalidOperationException(“无法识别的运算符”+_operator.ToString()); } } 公共覆盖IQueryable AddSortingTerm(IQueryable查询) { return(\u sortAscending)?query.OrderBy(u=>u.FullName):query.OrderByDescending(u=>u.FullName); } 公共用户全名(bool排序) :基本(排序) { } 公共用户全名(操作员op) :基本(op) { } } 我的问题是:
1.首先-我走对了吗?我不知道有什么众所周知的方法可以实现我想要的,但我可能错了…
2.在我看来,属性类应该在域层而不是DAL中,因为我希望控制器层能够使用它们。然而,这阻止了我使用任何特定于nHibernate的搜索实现(即除Linq之外的任何其他接口)。有谁能想出一个解决方案,使我能够充分利用nH的功能,同时使这些类对上层可见?我曾考虑将它们移到“通用”项目中,但“通用”不了解模型实体,我希望保持这种方式。
3.正如您从我对AddSearchTerm方法的评论中所看到的,我还没有真正能够使用nH实现“in”操作符(我在Linq provider中使用nH 2.1.2)。在这方面的任何建议都将受到欢迎。(另请参见)。

谢谢!

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


你让DAL在这些搜索中过于复杂了。Ayende有一个很好的建议,你不应该这样做,这大大简化了事情。

在使用你的库时,你能从前后两个角度给出一个用例的例子吗?我不确定所有这些增加的复杂度是否真的在实际情况下有用对不起,我不知道同意。你链接到的Ayende的帖子很有趣(和他平时一样),但与我的问题不太相关。