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

发布于 2024-10-28 14:58:05 字数 4745 浏览 8 评论 0原文

我的目标是实现一个非常通用的搜索机制。总体思路如下:
您可以根据您要搜索的实体的任何属性进行搜索(例如,按员工的工资或按部门名称等)。
您可以搜索的每个属性都由一个类表示,该类继承自 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 的功能,同时保持这些类对上层可见吗?我考虑过将它们移至“Common”项目,但“Common”不了解模型实体,我想保持这种状态。
3. 正如您通过我对 AddSearchTerm 方法的评论所看到的 - 我还没有真正能够使用 nH 实现“in”运算符(我正在使用带有 Linq 提供程序的 nH 2.1.2)。如有这方面的建议,我们将予以采纳。 (另请参阅我昨天的问题)。
谢谢!

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!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

苏辞 2024-11-04 14:58:05

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

您使 DAL 的这些搜索变得过于复杂。 Ayende 有一个很好的 帖子关于为什么你不应该这样做

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

離殇 2024-11-04 14:58:05

我最终使用了查询对象,这极大地简化的事情。

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文