【问题标题】:NHibernate QueryByExample including only certain propertiesNHibernate QueryByExample 仅包括某些属性
【发布时间】:2011-06-07 10:21:18
【问题描述】:

我创建了一个自定义属性选择器,以在构造函数中接受一个数组来说明哪些属性应包含在搜索中。只要没有组件类型,该方法就可以很好地工作,但是我该如何处理呢?这是一个例子:

public class Customer
{
    public virtual int Id { get; private set; }
    public virtual Name Name { get; set; }
    public virtual bool isPreferred { get; set; }


    //...etc
}

public class Name
{
        public string Title { get; set; }
        public string Firstname { get; set; }
        public string Lastname { get; set; }
        public string Fullname { get; }
}


public class CustomerPropertySelector : Example.IPropertySelector
    {
        private string[] _propertiesToInclude = { };

        public CustomerPropertySelector(string[] propertiesToInclude)
        {
            this._propertiesToInclude = propertiesToInclude;
        }

        public bool Include(object propertyValue, String propertyName, NHibernate.Type.IType type)
        {
            //...Checking for null and zeros etc, excluded for brevity

            if (!_propertiesToInclude.Contains(propertyName))
                return false;

            return true;
        }
   }

我希望能够按名字搜索,但不一定是最后一个。但是,属性名称是 Name,因此名字和姓氏似乎都是同一个属性的一部分,而像 Name.Firstname 这样通常用作标准的东西似乎在这里不起作用。最好的解决方法是什么?

示例:

Customer exampleCust = new Customer(FirstName: "Owen");
IList<Customer> matchedCustomers = _custRepo.GetByExample(exampleCust, new string[] { "Name.FirstName" });

鉴于 db 中有 2 个客户,只有一个名为“Owen”,但两者都有 isPreferred = false,我希望我的查询只返回第一个。标准 QBE 将根据 isPreferred 属性返回两者。

解决方案:

感谢您的回答,该解决方案主要基于 therealmitchconnors 的回答,但是如果没有 Mark Perry 的回答,我也无法做到。

诀窍是要意识到,我实际上想要排除 Name.LastName,而不是包含 Name.FirstName 属性,因为 QBE 只允许我们排除属性。我使用了一种改编自 therealmitchconnors 答案的方法来帮助我确定属性的完全限定名称。这是工作代码:

public IList<T> GetByExample(T exampleInstance, params string[] propertiesToInclude)
{
    ICriteria criteria = _session.CreateCriteria(typeof(T));
    Example example = Example.Create(exampleInstance);

    var props = typeof(T).GetProperties();
    foreach (var prop in props)
    {
        var childProperties = GetChildProperties(prop);
        foreach (var c in childProperties)
        {
            if (!propertiesToInclude.Contains(c))
                example.ExcludeProperty(c);
        }
    }
    criteria.Add(example);

    return criteria.List<T>();
}

private IEnumerable<string> GetChildProperties(System.Reflection.PropertyInfo property)
{
    var builtInTypes = new List<Type> { typeof(bool), typeof(byte), typeof(sbyte), typeof(char), 
        typeof(decimal), typeof(double), typeof(float), typeof(int), typeof(uint), typeof(long), 
        typeof(ulong), typeof(object), typeof(short), typeof(ushort), typeof(string), typeof(DateTime) };

    List<string> propertyNames = new List<string>();
    if (!builtInTypes.Contains(property.PropertyType) && !property.PropertyType.IsGenericType)
    {
        foreach (var subprop in property.PropertyType.GetProperties())
        {
            var childNames = GetChildProperties(subprop);
            propertyNames = propertyNames.Union(childNames.Select(r => property.Name + "." + r)).ToList();
        }
    }
    else
        propertyNames.Add(property.Name);

    return propertyNames;
}

我不确定确定属性是否为组件类的最佳方法,非常欢迎任何关于如何改进代码的建议。

【问题讨论】:

    标签: c# nhibernate query-by-example


    【解决方案1】:

    以下代码将替换您用于填充 propertiesToInclude 的逻辑。我将它从一个数组更改为一个列表,所以我可以使用 Add 方法,因为我很懒,但我想你明白了。这仅适用于一个子级别的属性。对于 n 个级别,您需要递归。

            List<string> _propertiesToInclude = new List<string>();
    
            Type t;
            var props = t.GetProperties();
            foreach (var prop in props)
            {
                if (prop.PropertyType.IsClass)
                    foreach (var subprop in prop.PropertyType.GetProperties())
                        _propertiesToInclude.Add(string.Format("{0}.{1}", prop.Name, subprop.Name));
                else
                    _propertiesToInclude.Add(prop.Name);
            }
    

    【讨论】:

    • 我认为你误解了这个问题 - 问题不在于决定如何填充 _propertiesToInclude,它给出了该数组如何告诉 QBE 我想从比较中排除 Name.LastName,但是 不是 Name.FirstName。我认为这可能是不可能的,因为就 QBE 而言Name 是一个整体,我可以包含或不包含它,但我不能部分包含它。
    • 那么,您希望CustomerPropertySelector.Include(null, "Name.FirstName", IType.Something); 返回true 和CustomerPropertySelector.Include(null, "Name.LastName", IType.Something); 返回false?基于什么标准?难道你不能硬编码排除 Name.LastName 的方法吗?我想我一定没有抓住重点。
    • 我也是这么认为的,也许我没有正确表达这个问题。我想创建一个示例客户new Customer(FirstName: "Owen"),然后使用propertiesToInclude: new string[] { "Name.FirstName" } 将其传递给我的方法,然后它应该只比较基于该字段的对象。
    • typeof(string).IsClass 返回 true,因此这不适用于具有字符串属性的类。我想不出比循环检查所有原始类型更好的方法
    【解决方案2】:

    我以为我有问题,但再次阅读您的问题,您想知道为什么 QBE NHibernate 代码不适用于组件属性。

    我认为您需要为名称部分创建一个子条件查询。

    大概是这样的:

    public IList<Customer> GetByExample(Customer customer, string[] propertiesToExclude){
        Example customerQuery = Example.Create(customer);
        Criteria nameCriteria = customerQuery.CreateCriteria<Name>();
        nameCriteria.Add(Example.create(customer.Name));
        propertiesToExclude.ForEach(x=> customerQuery.ExcludeProperty(x));
        propertiesToExclude.ForEach(x=> nameCriteria.ExcludeProperty(x));
        return customerQuery.list();
    }
    

    这是 NHibernate 测试项目中的一个示例,它展示了如何排除组件属性。

    [Test]
    public void TestExcludingQBE()
    {
            using (ISession s = OpenSession())
            using (ITransaction t = s.BeginTransaction())
            {
                Componentizable master = GetMaster("hibernate", null, "ope%");
                ICriteria crit = s.CreateCriteria(typeof(Componentizable));
                Example ex = Example.Create(master).EnableLike()
                    .ExcludeProperty("Component.SubComponent");
                crit.Add(ex);
                IList result = crit.List();
                Assert.IsNotNull(result);
                Assert.AreEqual(3, result.Count);
    
                master = GetMaster("hibernate", "ORM tool", "fake stuff");
                crit = s.CreateCriteria(typeof(Componentizable));
                ex = Example.Create(master).EnableLike()
                    .ExcludeProperty("Component.SubComponent.SubName1");
                crit.Add(ex);
                result = crit.List();
                Assert.IsNotNull(result);
                Assert.AreEqual(1, result.Count);
                t.Commit();
            }
        }
    

    Source code link

    【讨论】:

    • 我认为您的“QBE NHibernate 代码不适用于组件属性”一针见血。有没有可以证实这一点的参考资料?
    • 问题是 QBE 不知道包含/排除对象的组件部分,除非您专门将组件条件添加到查询中。我已经完全放弃在我的应用程序中使用 QBE,而是使用以下 API(按优先顺序)。 Linq、QueryOver、标准、HQL。
    • 在我上一条评论的基础上,我在 NHibernate 测试项目中找到了一些代码,它似乎显示了如何从 QBE 查询中排除组件属性。
    • 啊哈!非常感谢后续跟进,这让我意识到了答案。 QBE 确实支持组件属性,我只是对如何做到这一点感到困惑。不幸的是,realmitchconnors 的答案更接近真相,尽管没有 NHTest 示例,我永远不会弄明白。
    猜你喜欢
    • 1970-01-01
    • 2018-07-23
    • 2012-06-07
    • 1970-01-01
    • 1970-01-01
    • 2023-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多