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

问题描述:

我的目标是实现一个非常通用的搜索机制。总体思路如下:
您可以基于您正在搜索的实体的任何属性(例如,按员工的薪水或部门名称等)进行搜索。
您可以通过搜索每个属性的一类,它从EntityProperty继承表示:使用nHibernate实现灵活的搜索基础架构

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. firstly-是我甚至在正确的轨道?我不知道任何已知的方法来实现我想要的,但我可能是错的...
2.在我看来,属性类应该在域层,而不是在DAL中,因为我希望控制器层能够使用它们。但是,这阻止了我使用任何nHibernate特定的搜索实现(即除Linq之外的任何其他接口)。任何人都可以想到一个解决方案,使我能够利用nH的全部力量,同时保持这些类对上层可见?我曾考虑将它们移到'Common'项目中,但'Common'对Model实体没有任何了解,我想保留它。
3.正如你可以通过我对AddSearchTerm方法的评论看到的那样 - 我真的没有能够使用nH实现'in'运算符(我使用nH 2.1.2和Linq提供程序)。在这方面的任何建议都会受到关注。 (另见my question from yesterday)。
谢谢!

+0

你能否举一个使用你的库的前后视角用例的例子。我不确定是否所有这些增加的复杂性实际上有助于在实际情况下 – 2011-03-31 08:14:12

我结束了使用query objects,这大大简化了事情。

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

你用这些搜索复杂化DAL。 Ayende有一个不错的postwhy你不应该这样做

+0

对不起,我不同意。和Ayende的帖子很有趣(像往常一样),但与我的问题不太相关。 – 2011-04-03 06:44:28