views:

60

answers:

2

Everyone projects once in a while:

 var c = dc.Products.Select( p => new {p.id, p.name});
 // results in c having properties of .id and .name

What if I want to refactor that in to a method. How would I pass the Select parameter?

var c = myFunction( p => new {p.id, p.name});

Product myFunction( ??? myLambda)
{
    var c = dc.Products.Select( myLambda);

    var result = new Product();
    foreach( var myproperty in //property in myLambda)
    {
        result.XX // property that matches, set it equal to myproperty
    }
}

Obviously, I need to figure out reflection a lot more. :)

+3  A: 

You could at least look at any exists LINQ definitions, like http://msdn.microsoft.com/en-us/library/bb548891.aspx

So you need Func<TSource, TResult> myLambda

To be precise:

TResult myFunction<TResult>(Expression<Func<Product, TResult>> myLambda)

or

IEnumerable<TResult> myFunction<TResult>(Expression<Func<Product, TResult>> myLambda)

UPD: according to the @tvanfosson comment - the code has been changed a little.

zerkms
Nice. Thank you.
Dr. Zim
If you are using LINQ to SQL/EF then using Func<T,TResult> will have the effect of forcing the select to occur after the results have been retrieved from the DB rather than translating to a SQL query. I blogged about something similar at http://farm-fresh-code.blogspot.com/2010/02/expressions-functions-and-case.html
tvanfosson
@tvanfosson: thank you, that is what I actually thought ;-)
zerkms
+1  A: 

The type of the parameter should be something like Expression<Func<Product,object>>. Also, I think you need your function to return an IEnumerable<Product> with the properties set.

IEnumerable<Product> myFunction( Expression<Func<Product,object>> selector )
{
    var products = new List<Product>();
    foreach (var c in dc.Products.Select( selector ))
    {
        var product = new Product();
        foreach (var property in c.GetType().GetProperties())
        {
            var productProperty = typeof(Product).GetProperty( property.Name );
            productProperty.SetValue( product, property.GetValue( c, null ) );
        }
        products.Add( product );
    }
    return products;
}

Having said that I think I would probably have a separate model for each combination of properties being returned and use a generic method to return one of those.

IEnumerable<T> ProductPartial<T>( Func<Product,T> selector ) where T : new
{
      return dc.Products.ToList().Select( selector );
}

used as

var pricing = repository.ProductPartial( p => new PricingModel( p ) );

Note: After thinking about it a little, if you are going to return a separate model, I think you may need to realize the query before you can do the conversion to the model anyway. In that case a Func<Product,T> is probably most appropriate -- I've thrown an explicit ToList() in to make sure that the query is realized first so the select doesn't fail. If it were a Where clause, that would be different. Even though this is slightly less efficient from a data transfer perspective (you're retrieving all the properties), I still prefer using using the specific model if you feel like you need to put the selection in the repository. Since you're only selecting part of the properties it feels wrong to pretend that you have an actual Product instance when you really don't.

tvanfosson
@tvanfosson: What is the reason to wrap `Func` with `Expression`? I've seen this for several times but never read about why it is necessary. (I think it is for delay linq2sql evaluation for example, but I'm not sure)
zerkms
@tvanfosson: Btw, since he returns anonymous object - `IEnumerable<Product>` is not true, imo.
zerkms
@zerkms -- if you look at the original, it returns an enumeration of anonymous objects. The first attempt at the method erroneously returns an instance of Product -- the code looks like it's filling each product instance with properties from the anonymous object. This is what I tried to replicate. The reason for this is that if you can't specify a return type of an IEnumerable of an anonymous type, only object. If you do that, you lose the ability to reference the properties without reflection in the consuming class. That's why I recommended using specific models and a generic method.
tvanfosson
Cool. This is an attempt for a method in a Repository that returns IList<DomainModel>, but allow it to only populate select fields. No need to create custom POCOs, just know we only use certain fields.
Dr. Zim