Showing posts with label dynamic. Show all posts
Showing posts with label dynamic. Show all posts

Wednesday, October 1, 2008

OrderBy using a Property Name

Now this is kind of dangerous to do since there is no compile time check (Like most things set in markup) but say you want to sort a collection, using the Linq extension methods, but you don't know what you what to sort on at any given time. On top of that, you have a datagrid and a bunch of sort expressions to deal with. Now you could do something like create a hashtable full of lambda expressions that the key is the sort expression:
Dictionary<String, Func<User, IComparable>> list;

userList = User.GetUserList();
list = new Dictionary<String, Func<User, IComparable>>();
list.Add("UserName", currentUser => currentUser.UserName);
list.Add("UserID", currentUser => currentUser.UserID);
userList.OrderBy(list["UserID"]);
Works just fine, and might be preferable to what I'm about to show. OooOoOO sound eerie?
//This is just to get the property info using reflection.  In order to get the value
//from a property dynamically, we need the property info from the class
public static PropertyInfo[] GetInfo<K>(K item) where K : class
{
  PropertyInfo[] propertyList;
  Type typeInfo;
        
  typeInfo = item.GetType();
  propertyList = typeInfo.GetProperties();
        
  return propertyList;
}

//This is the dynamic order by func that the OrderBy method needs to work
public static IComparable OrderByProperty<T>(String propertyName, T item)
  where T : class
{
  PropertyInfo[] propertyList;
        
  propertyList = GetInfo(item);

  //Here we get the value of that property of the passed in item and make sure
  //to type the object (Which is what GetValue returns) into an IComparable
  return (IComparable)propertyList.First(currentProperty
    => currentProperty.Name == propertyName).GetValue(item, null);
}
And use:
//This takes the current user and calls the OrderByProperty method which in turn
//gives us the Func OrderBy is requesting.
var test = userList.OrderBy(currentUser
  => DynamicPropertySort.OrderByProperty("UserID", currentUser)).ToList();
Ok so what the hell? I mean intellisense on the OrderBy method doesn't give much help. Func<<User, TKey>>. Yeah ok. So basically the return type is open. Well this kind of sucks right? Because I would have to return a Func that already knows the return type. (Be it string, int, ect) Of course, this would mean we would have to handle each sort expression in code. NOT VERY DYNAMIC IS IT? Well f that. Truth is, what the order by is looking for is a Func that takes in a User and returns something it can compare. This is where IComparable comes in. The OrderBy has to take the returned value, say UserID which is an int, and figure out how to compare it to another value. Pretty simple. So as long as the property you are ordering by uses IComparable, you're good to go. Pretty nice huh? Now I would suggest, if you use this (HAHAHAHA), is to cache a dictionary of the property info with the class type as the key so that you don't have to use as much reflection everytime. I just didn't put that in. U U USING

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

Tuesday, August 26, 2008

When is a field not a field?

Ok so for the last few things I've been showing a more dynamic approach to linq queries , mostly dealing with collections rather than say linq to sql. Now take the new method:
public List<K> SelectFromUserList<K, L>(Func<User, K> selectMethod, Func<User, L> orderBy, List<User> userList)
{
    List<K> userList = new List<K>();
    userList = userList.OrderBy(orderBy).Select(selectMethod).ToList();

    return userList;
}
and the call was this:
  List<String> newList = userList.Select(selectUser => selectUser.Name, orderUser => orderUser.ID, userList);
Let's say you have two needs, selecting all the userNames and all the IDs. You could go ahead and call that method twice and inserting the lambda expressions. But let's say you want to be able to mix and match things. Say select user names and order by user id or maybe select user names and order by use names. Well there's a solution to avoid rewriting the lambda expressions:
  Func<User, Int32> selectUserID = currentUser => currentUser.UserID;
  Func<User, String>selectUserName = currentUser => currentUser.UserName;

  Func<User, Int32> orderByUserID = currentUser => currentUser.UserID;
  Func<User, String> orderByUserName = currentUser => currentUser.UserName;
What the? See a while ago I had a method to pass back the expression, but in this case there's nothing to create the expression from (a passed in parameter) since they are really simple expressions. How would I use them?

  List<Int32> userIDs;
  List<User> userList;
  List<String> userNames;
  userIDs = SelectFromUserList(selectUserID, orderByUserID, userList);
  userNames = SelectFromUserList(selectUserName, orderByUserID, userList);
Pretty nice huh?

Friday, July 18, 2008

I'm starting to worry about myself

So the "dynamic" linq query so far isn't just enough to stop. Oh no, now that I can have methods pass back expressions, what about a dictionary of order by expressions to allow an even more dynamic feel? Huh? How's about that kids? I hate myself too.
//Enum created for the dictionary key
public enum OrderByChoice
{
  FirstName,
  LastName,
  UserName
}

//dictionary of expressions
private static Dictionary<OrderByChoice, Expression<Func<User, String>>>  GetOrderByList()
{
  if(orderByList == null)
  {
    orderByList = new Dictionary<OrderByChoice,Expression<Func<User, String>>>();

    orderByList.Add(OrderByChoice.FirstName, currentUser => currentUser.FirstName);
    orderByList.Add(OrderByChoice.LastName, currentUser => currentUser.LastName);
    orderByList.Add(OrderByChoice.UserName, currentUser => currentUser.UserName);
  }
         
  return orderByList;
}   
That's right, I know. Silly, but I think it's cool. Now mind you, I could have methods that return expressions instead of the expressions themselves, but I wanted to show the expressions. And for the method call:
public static IList<User> GetUserList(OrderByChoice orderBy)
{
  return GetUserList(currentUser => true, GetOrderByList()[orderBy]).ToList();
}
And now you have an even more dynamicish call. I FORGOT THE USINGS!!!!
 using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq.Expressions;

Thursday, July 17, 2008

And some days I love programming

This is the newest edition of Linq madness. So I added an OrderBy to my lame dynamic query thing.
private static IQueryable<User> GetUserList(Expression<Func<User, Boolean>> whereClause, Expression<Func<User, String>> orderBy)
{
    var query = (from user in GetDataContext().Users
             select user).Where(whereClause).OrderBy(orderBy);

    return query;
}
Now you will notice there is a new Expression in town and it's name isn't Reggie Hammond. It is my order by Expression which uses a Func<User, String>. It took me a minute to figure out how this works. I thought it was somehow in need of the property name to order by, but in reality it is looking for a list of strings to order by. Simple. I could do something like: (Ignore the WhereLikeFirstNameLastNameUserName for now)
    return GetUserList
           (
            WhereLikeFirstNameLastNameUserName(name),
            currentUser => currentUser.UserName
           ).ToList();
But that's boring. I want to be able to pass a method that would give the correct string to orderby somewhat cleaner. In comes a method that returns an Expression. OooOoOOo.
private static Expression<Func<User, String>> SortOnUserName()
{
    return currentUser => currentUser.UserName;
}
So now it looks like:
public static IList<User> GetUserListByLikeName(String name)
{
   return GetUserList
          (
           WhereLikeFirstNameLastNameUserName(name),
           SortOnUserName()
          ).ToList();
}
Cleaner... Now what is WhereLikeFirstNameLastNameUserName? Simple, that is my where Expression just being returned in this method:
private static Expression<Func<User, Boolean>> WhereLikeFirstNameLastNameUserName(String name)
{
   return currentUser => SqlMethods.Like(currentUser.UserName, "%" + name + "%")
   || SqlMethods.Like(currentUser.FirstName, "%" + name + "%")
   || SqlMethods.Like(currentUser.LastName, "%" + name + "%");
}
By the way, SqlMethods.Like is just a built in method used only with Linq to Sql. Probably shouldn't have used it in this example but oh well. Live with it. OH NO NOT THE USINGS!!
using System;
using System.Collections.Generic;
using System.Data.Linq.SqlClient;
using System.Linq;
using System.Linq.Expressions;

I likz demz linq

So today I had this weird need to try creating a sort of dynamic linq thing in which I could query a User list but not have to write a Linq query for every method. I just wanted to make a method that would return the correct query that I wanted based on a simple "dynamic" where clause. Well part of the reason this should be easy is if you look at the Where extension method on an IEnumerable collection, you will see it needs a Func<T, K> where T is the type of the items in the list. So ding!, create a method that takes in a Func<User, Boolean> and puts it in them thar where clause. Then you could easily use a lambda expression to create the Func. At first you might thing something like this:
private static IQueryable<User> GetUserList(Func<User, Boolean> whereClause)
{
  var query = from user in Users
            where whereClause(user)
            select user;

  return query;
}

public static IList<User> GetUserListByUserName(String userName)
{
  return GetUserList(currentUser => currentUser.UserName == userName).ToList();
}
AND YOU WOULD BE WRONG! I only know this because... eh... I saw a friend of mine try this and it failed. Fact is, the Linq where doesn't take in a Func, but rather it expects and Expression. So no big deal right? Slap on an expression and go... except you can't keep the same syntax. (Trust me, my... friend tried) Being the absolute genius that I am, I offered my friend a solution, and it's slightly ugly:
private static IQueryable<User> GetUserList(Expression<Func<User, Boolean>> whereClause)
{
  var query = (from user in Users
            select user).Where(whereClause);

  return query;
}
Could be used like:
public static IList<User> GetUserList()
{
  return GetUserList(currentUser => true).ToList();
}
Or
public static IList<User> GetUserListByFirstName(String firstName)
{
  return GetUserList(currentUser => currentUser.FirstName == firstName).ToList();
}
Yeah, not the prettiest of things, but it is what it is. I'm not even sure this is useful yet. Still screwing around with it. Does cut down on code for easy queries. AND NOW FOR THE ASSEMBLIES!
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;