NHibernate QueryByExample, включающий только определенные свойства - PullRequest
1 голос
/ 07 июня 2011

Я создал пользовательский селектор свойств для принятия массива в конструкторе, чтобы указать, какие свойства должны быть включены в поиск.Подход работает хорошо, пока нет типов компонентов, но как мне с ними бороться?Вот пример:

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" });

Учитывая, что в БД 2 клиента, только один с именем "Оуэн", но обаиметь isPreferred = false, я бы хотел, чтобы мой запрос возвращал только первый.Стандартный QBE будет возвращать оба, основываясь на свойстве isPreferred.

РЕШЕНИЕ:

Спасибо за ответы, решение в основном основано на ответе сторонних коннекторов, однако яне мог бы сделать это и без ответа Марка Перри.

Хитрость заключалась в том, чтобы понять, что вместо включения свойства Name.FirstName я на самом деле хочу исключить Name.LastName, поскольку QBE позволяет нам исключать только свойства.Я использовал метод, адаптированный на основе ответа от almonchconnors, чтобы помочь мне определить полностью определенные имена свойств.Вот рабочий код:

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;
}

Я не был уверен, что лучше всего определить, является ли свойство классом компонента или нет, любые предложения по улучшению кода приветствуются.

Ответы [ 2 ]

1 голос
/ 27 июня 2011

Я думал, что у меня что-то есть, но, прочитав ваш вопрос еще раз, вы хотите знать, почему код 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();
        }
    }

Исходный код ссылки

1 голос
/ 22 июня 2011

Следующий код заменит логику, которую вы используете для заполнения свойств ToInclude.Я изменил его из массива в список, чтобы я мог использовать метод 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);
        }
...