tags:

views:

82

answers:

3

Hi,

I have an 3-layered project, so that all Linq-Querys himself are in the DAL. Now I have a filter function implemented in the Desig-Layer and want easily filter there, but how?

            Business.UserHandling uh = new Business.UserHandling();
            List<DAL.Benutzer> users = uh.GetUserOverview();
            gridUserOverview.AutoGenerateColumns = false;
            gridUserOverview.DataSource = users;
            gridUserOverview.DataBind();

How can I user something like "users = users.Where("bla = 1")"?


I tried:

                Business.UserHandling uh = new Business.UserHandling();
            List<DAL.Benutzer> users = uh.GetUserOverview();

            var filters = new Dictionary<string, object>();
            filters.Add(Request.QueryString["value"], Request.QueryString["text"]);
            users = users.Where(user => filters.All(filter => user.GetType().GetProperty(filter.Key).GetValue(user, null) == filter.Value)).ToList();

            gridUserOverview.AutoGenerateColumns = false;
            gridUserOverview.DataSource = users;
            gridUserOverview.DataBind();

But it fails... I filtered on "UserID = 1", I have 2 Users, the filter should be filter 1 of these 2. But the return is 0 users.

+2  A: 

In order to use a dynamic filter you can build a dictionary and verify the filter by reflection:

var filters = new Dictionary<string, object>();
filters.Add("bla", 1);
users.Where(user => filters.All(filter => user.GetType().GetProperty(filter.Key).GetValue(user, null) == filter.Value));

Edit Support all types convertible from string:

var users = new List<User>();
users.Add(new User {Age = 1 });
users.Add(new User { Age = 2 });
var filters = new Dictionary<string, string>();
filters.Add("Age", "1");
var filtered = users.Where(user => filters.All(filter =>
       {
           var propertyInfo = user.GetType().GetProperty(filter.Key);
           return Equals(propertyInfo.GetValue(user, null) , Convert.ChangeType(filter.Value, propertyInfo.PropertyType));
       }));

Assert.AreEqual(1, filtered.Count());
Elisha
Nearly perfect, but I want to have a generic search, so I wand to enter the text "bla", beacuse it can be 10 different coloums, you know?!
Kovu
In what form do you get the filter? As string with property name and value?
Elisha
Correct, 2 strings with value and text
Kovu
To get two strings, parameterName and parameterValue using LINQ, you might be stuck with a large switch statement or the joy that is reflection.
Nate Bross
See my post edit please
Kovu
Added example in the middle, it works here.Are you sure the values are OK (property name is case sensitive)
Elisha
I'm sure, here my data:http://localhost:60074/Admin/Users/Overview.aspx?value=BenutzerIDAnd here are my veriables of User: http://s2.imgimg.de/uploads/Capture6a469d22PNG.png
Kovu
If user ID is int it must be parsed first...int.Parse(Request.QueryString["text"])
Elisha
That's not good, because I have different data types here to search for, who can I, without an endless switch, know if I must convert or not?And I tried, it's not the reason!
Kovu
Added example that supports multiple types
Elisha
Ok, found the problem and solve it! Thank you very much!
Kovu
+1  A: 

You could change GetUserOverview to accept an expression, which would allow you to do custom filtering.

Example

public List<Benutzer> GetUserOverview(Func<Benutzer, bool> filter)
{
    //get your users however you were getting them
    var _result = new List<Benutzer>();

    _result = _result.Where(filter);
    return _result;
}

And that would let you do this:

List<DAL.Benutzer> users = uh.GetUserOverview(user => user.bla == 1);

UPDATE

If you need to dynamically build your filter you can do it this way:

Expression<Func<Benutzer, bool> finalFilter;

if (doFirstFilter)
{
    Expression<Func<Benutzer, bool> firstFilter = (user) => user.blah == 1;
}

if (doSecondFilter)
{
    Expression<Func<Benutzer, bool> secondFitler = 
        (user) => user.other == "whatever";
    finalFilter = Expression.Lambda<Func<Benutzer, bool>>(
        Expression.And(firstFilter, secondFilter));
}

var users = uh.GetUserOverview(finalFilter.Compile());

You can do all kinds of crazy stuff with Expressions.

Joseph
Okay, that is nearly the same as Elisha postet, thank you.But see the problem in his comment, how to solve?
Kovu
Since you're filtering criteria is dynamic, you'll have to build it dynamically. I'll update my answer with an example of how to do that.
Joseph
I'm sorry, it's not the count of filter that should be dynamic, it is the name of the to filter property.
Kovu
@Kovu That's fine, using Expressions you can build all *kinds* of dynamic filters. It's really just a matter of adapting the Expressions to meeting your business requirements.
Joseph
A: 

Have you tried this useful bit of code taken from here

public static IQueryable<T> AddEqualityCondition<T, V>(this IQueryable<T> queryable,string propertyName, V propertyValue)
        {
            ParameterExpression pe = Expression.Parameter(typeof(T), "p");
            IQueryable<T> x = queryable.Where<T>(
              Expression.Lambda<Func<T, bool>>(
                Expression.Equal(Expression.Property(
                  pe,
                  typeof(T).GetProperty(propertyName)),
                  Expression.Constant(propertyValue, typeof(V)),
                  false,
                  typeof(T).GetMethod("op_Equality")),
              new ParameterExpression[] { pe }));

            return (x);
        }
Richard Friend