Моя цель - реализовать довольно общий механизм поиска. Вот общая идея:
Вы можете осуществлять поиск по любому свойству объекта, который ищете (например, по зарплате сотрудника, по названию отдела и т. д.).
Каждое свойство, по которому вы можете искать, представлено классом, который наследуется от 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. мне кажется, что классы Properties должны быть на уровне домена, а не в DAL, так как я бы хотел, чтобы уровни контроллера могли использовать их. Однако это мешает мне использовать любую специфичную для nHibernate реализацию поиска (то есть любой другой интерфейс, кроме Linq). Кто-нибудь может придумать решение, которое позволило бы мне использовать всю мощь nH, сохраняя эти классы видимыми для верхних уровней? Я думал о том, чтобы перенести их в проект «Общий», но «Общий» не знает о сущностях Модели, и я бы хотел, чтобы это продолжалось.
3. как вы можете видеть из моего комментария к методу AddSearchTerm - я действительно не смог реализовать оператор «in», используя nH (я использую nH 2.1.2 с провайдером Linq). любые предложения в этом отношении будут оценены. (см. также мой вопрос со вчерашнего дня ).
Спасибо!