Create ICriterion by iterating through properties via PropertyInfo for NHibernate session

138 Views Asked by At

I've got a client model that has many properties like first name, last name, address, ...

Via the ClientEditViewModel I can change the property values in TextBoxes.

public class ClientEditViewModel : EditableViewModelBase
{
    public int ClientID
    {
        get { return this.currentClient.ClientID; }
        set { this.SetProperty(newValue => this.currentClient.ClientID = newValue, value); }
    }

    public string Title
    {
        get { return this.currentClient.Title; }
        set { this.SetProperty(newValue => this.currentClient.Title = newValue, value); }
    }

    public string FirstName
    {
        get { return this.currentClient.FirstName; }
        set { this.SetProperty(newValue => this.currentClient.FirstName = newValue, value); }
    }

    public string LastName
    {
        get { return this.currentClient.LastName; }
        set { this.SetProperty(newValue => this.currentClient.LastName = newValue, value); }
    }

    ...
}

When the user is pressing the search button, I want to iterate through all properties. If the property is not empty or null, I want to add them to the query with the 'LIKE' restriction. Instead of checking every property manually, I wanted to iterate all properties via Reflection.

public ICriterion GetSearchClientCriteria()
{
    var conjunction = Restrictions.Conjunction();

    if (this.ClientID != 0)
    {
        conjunction.Add(Restrictions.Where<Client>(x => x.ClientID == this.ClientID));
        return conjunction;
    }

    foreach (PropertyInfo propertyInfo in this.PropertyInfosWithPublicGetterAndSetter)
    {
        if (propertyInfo.PropertyType == typeof(string))
        {
            string currentPropertyValue = propertyInfo.GetValue(this) as string;

            if (!string.IsNullOrEmpty(currentPropertyValue))
            {
                /* This statement can be executed */
                // conjunction.Add(Restrictions.Where<Client>(c => c.FirstName.IsLike(this.FirstName, MatchMode.Anywhere)));
                conjunction.Add(Restrictions.Where<Client>(c => c.GetType().GetProperty(propertyInfo.Name).GetValue(c, null).ToString()
                .IsLike(this.GetType().GetProperty(propertyInfo.Name).GetValue(this).ToString())));

                return conjunction;
            }
        }
    }

    return conjunction;
}

Unfotunately, when I use this conjunction in the following code, I get an error. How can I iterate through all my properties without checking every single property manually?

public class NHibernateRepository : IRepository
{
    public ICollection<T> GetByCriteria<T>(ICriterion criterion) where T : class
    {
        using (var session = this.sessionManager.OpenSession())
        {
            return session.QueryOver<T>().Where(criterion).List();
        }
    }
}

System.InvalidOperationException: Auf die Variable "c" vom Typ "Rechnungsprogramm.Model.Client" wird vom Bereich "" verwiesen, sie ist jedoch nicht definiert.

System.InvalidOperationException: variable 'c' of type 'Rechnungsprogramm.Model.Client' referenced from scope '', but it is not defined

Own solution:

Not the prettiest solution, but it works.

private ICriterion GetClientSearchCriterion()
{
    Conjunction conjunction = Restrictions.Conjunction();

    if (this.CurrentClientDetailViewModel.ClientId != 0)
    {
        conjunction.Add(Restrictions.Where<Client>(c => c.ClientId == this.CurrentClientDetailViewModel.ClientId));
        return conjunction;
    }

    if (!string.IsNullOrEmpty(this.CurrentClientDetailViewModel.Title))
    {
        conjunction.Add(Restrictions.Where<Client>(c => c.Title.IsLike(this.CurrentClientDetailViewModel.Title, MatchMode.Anywhere)));
    }
    if (!string.IsNullOrEmpty(this.CurrentClientDetailViewModel.FirstName))
    {
        conjunction.Add(Restrictions.Where<Client>(c => c.FirstName.IsLike(this.CurrentClientDetailViewModel.FirstName, MatchMode.Anywhere)));
    }
    if (!string.IsNullOrEmpty(this.CurrentClientDetailViewModel.LastName))
    {
        conjunction.Add(Restrictions.Where<Client>(c => c.LastName.IsLike(this.CurrentClientDetailViewModel.LastName, MatchMode.Anywhere)));
    }
    if (!string.IsNullOrEmpty(this.CurrentClientDetailViewModel.Street))
    {
        conjunction.Add(Restrictions.Where<Client>(c => c.Street.IsLike(this.CurrentClientDetailViewModel.Street, MatchMode.Anywhere)));
    }
    if (!string.IsNullOrEmpty(this.CurrentClientDetailViewModel.HouseNumber))
    {
        conjunction.Add(Restrictions.Where<Client>(c => c.HouseNumber.IsLike(this.CurrentClientDetailViewModel.HouseNumber, MatchMode.Anywhere)));
    }
    if (!string.IsNullOrEmpty(this.CurrentClientDetailViewModel.PostalCode))
    {
        conjunction.Add(Restrictions.Where<Client>(c => c.PostalCode.IsLike(this.CurrentClientDetailViewModel.PostalCode, MatchMode.Anywhere)));
    }
    if (!string.IsNullOrEmpty(this.CurrentClientDetailViewModel.City))
    {
        conjunction.Add(Restrictions.Where<Client>(c => c.City.IsLike(this.CurrentClientDetailViewModel.City, MatchMode.Anywhere)));
    }
    if (!string.IsNullOrEmpty(this.CurrentClientDetailViewModel.DateOfBirth))
    {
        conjunction.Add(Restrictions.Where<Client>(c => c.DateOfBirth.IsLike(this.CurrentClientDetailViewModel.DateOfBirth, MatchMode.Anywhere)));
    }
    if (!string.IsNullOrEmpty(this.CurrentClientDetailViewModel.PhoneNumber1))
    {
        conjunction.Add(Restrictions.Where<Client>(c => c.PhoneNumber1.IsLike(this.CurrentClientDetailViewModel.PhoneNumber1, MatchMode.Anywhere)));
    }
    if (!string.IsNullOrEmpty(this.CurrentClientDetailViewModel.PhoneNumber2))
    {
        conjunction.Add(Restrictions.Where<Client>(c => c.PhoneNumber2.IsLike(this.CurrentClientDetailViewModel.PhoneNumber2, MatchMode.Anywhere)));
    }
    if (!string.IsNullOrEmpty(this.CurrentClientDetailViewModel.MobileNumber))
    {
        conjunction.Add(Restrictions.Where<Client>(c => c.MobileNumber.IsLike(this.CurrentClientDetailViewModel.MobileNumber, MatchMode.Anywhere)));
    }
    if (!string.IsNullOrEmpty(this.CurrentClientDetailViewModel.Telefax))
    {
        conjunction.Add(Restrictions.Where<Client>(c => c.Telefax.IsLike(this.CurrentClientDetailViewModel.Telefax, MatchMode.Anywhere)));
    }
    if (!string.IsNullOrEmpty(this.CurrentClientDetailViewModel.Email))
    {
        conjunction.Add(Restrictions.Where<Client>(c => c.Email.IsLike(this.CurrentClientDetailViewModel.Email, MatchMode.Anywhere)));
    }

    return conjunction;
}
1

There are 1 best solutions below

2
Frédéric On

You cannot put anything you like into lambda having to be translated to SQL by an ORM.
Your lambda must be able to be translated to SQL, which has no equivalent to things such as GetType, GetProperty, GetValue.

You may, instead of using lambda, try to build Expression dynamically, a bit like this question answers.