NHibernate ICriteria查询包含用于高级搜索的组件和集合

时间:2009-05-21 18:09:53

标签: nhibernate fluent-nhibernate icriteria

我正在为我的ASP.NET MVC应用程序构建一个高级搜索表单。

我有一个Customer对象,带有一个地址组件: 流畅的NHibernate映射:

       public CustomerMap()
    {
        WithTable("Customers");

        Id(x => x.Id)
            .WithUnsavedValue(0)
            .GeneratedBy.Identity();

        Map(x => x.Name);
        Map(x => x.Industry);

        Component(x => x.Address, m =>
        {
            m.Map(x => x.AddressLine);
            m.Map(x => x.City);
            m.Map(x => x.State);
            m.Map(x => x.Zip);
        });

在我的Customer类ctor中,为了防止空对象,我有以下内容:

public Customer()
{
    Address = new Address();
}

我的搜索表单提供了以下字段供用户搜索:

  • 客户名称
  • 国家
  • 行业

所有这些字段都是可选的。

我的NHibernate Criteria看起来像这样(客户正在使用ASP.NET MVC Model Binder从表单传入):

            var p = Session.CreateCriteria(typeof(Customer))
            .Add(Example.Create(customer).ExcludeZeroes().IgnoreCase().EnableLike())
            .SetProjection(Projections.ProjectionList()
                               .Add(Projections.Property("Id"), "Id")
                               .Add(Projections.Property("Name"), "Name")
                               .Add(Projections.Property("Address.City"), "City")
                               .Add(Projections.Property("Address.State"), "State")
                               .Add(Projections.Property("PhoneNumber"), "PhoneNumber"))
            .AddOrder(Order.Asc("Name"))
            .SetResultTransformer(NHibernate.Transform.Transformers.AliasToBean(typeof(CustomerDTO)));

        return p.List<CustomerDTO>() as List<CustomerDTO>;

请注意,我使用.ExcludeZeroes()来排除空值和零默认值。这是必需的,因为我的Customer对象有一些INT(在本文中为了简洁而排除),在查询中默认为零(0),导致查询不正确。

如果我在所有字段为空的情况下运行它(确定,因为它们是可选的),结果SQL如下所示:

SELECT   this_.Id          as y0_,
         this_.Name        as y1_,
         this_.City        as y2_,
         this_.State       as y3_,
         this_.PhoneNumber as y4_
FROM     Customers this_
WHERE    (lower(this_.Industry) like '' /* @p0 */
          and lower(this_.State) like '' /* @p1 */)
ORDER BY y1_ asc

行业和国家是网络形式的下拉,但在上面的例子中,我把它们留空了。但ExcludeZeroes()声明似乎不适用于那些字段。

如果我在标准之前手动检查:

if (customer.Address.State == "")
{
    customer.Address.State = null;
}

并为行业做同样的事情,然后标准将起作用。

我假设这与我在Customer ctor中初始化Address对象有关。我不想改变这种情况,但我不知道另一种使Criteria工作的方法,而无需手动检查表单中的空字符串值(从而消除了使用带有ICriteria的Example对象的优势)。

为什么呢?如何使此Criteria查询生效?

2 个答案:

答案 0 :(得分:1)

使用属性选择器忽略空字符串或空字符串。

using System;
using NHibernate.Criterion;
using NHibernate.Type;


namespace Sample
{

    /// <summary>
    /// Implementation of <see cref="Example.IPropertySelector"/> that includes the
    /// properties that are not <c>null</c> and do not have an <see cref="String.Empty"/>
    /// returned by <c>propertyValue.ToString()</c>.
    /// </summary>
    /// <remarks>
    /// This selector is not present in H2.1. It may be useful if nullable types
    /// are used for some properties.
    /// </remarks>
    public class NoValuePropertySelector : Example.IPropertySelector
    {
        #region [ Methods (2) ]

        // [ Public Methods (1) ]

        /// <summary>
        /// Determine if the Property should be included.
        /// </summary>
        /// <param name="propertyValue">The value of the property that is being checked for inclusion.</param>
        /// <param name="propertyName">The name of the property that is being checked for inclusion.</param>
        /// <param name="type">The <see cref="T:NHibernate.Type.IType"/> of the property.</param>
        /// <returns>
        ///     <see langword="true"/> if the Property should be included in the Query,
        /// <see langword="false"/> otherwise.
        /// </returns>
        public bool Include(object propertyValue, String propertyName, IType type)
        {
            if (propertyValue == null)
            {
                return false;
            }

            if (propertyValue is string)
            {
                return ((string)propertyValue).Length != 0;
            }

            if (IsZero(propertyValue))
            {
                return false;
            }
            else
            {
                return true;
            }
        }

        // [ Private Methods (1) ]

        private static bool IsZero(object value)
        {
            // Only try to check IConvertibles, to be able to handle various flavors
            // of nullable numbers, etc. Skip strings.
            if (value is IConvertible && !(value is string))
            {
                try
                {
                    return Convert.ToInt64(value) == 0L;
                }
                catch (FormatException)
                {
                    // Ignore
                }
                catch (InvalidCastException)
                {
                    // Ignore
                }
            }

            return false;
        }


        #endregion [ Methods ]
    }

}

答案 1 :(得分:0)

我和QBE有同样的问题。我还认为Query by Example对于对象(和关联)的通用搜索是一种非常好的方法。已经有ExcludeNones / Nulls / Zeros。应该有一个选项来排除空字符串(“”)。