Q: Using LINQ, if I wanted to perform some query and return the object from the query, but change only some of the properties in that object, how would I do this without creating a new object and manually set every property? Is this possible?
Example:
var list = from something in someList select x; // but change one property
This post is based off of my Stack Overflow post on the same topic.
You can do it via the LINQ extension methods pretty easily, as JaredPar answered:
var query = someList.Select(x => { x.SomeProp = "foo"; return x; })
But what if you wanted to use declarative syntax similar to my above example? You could write a simple extension method to do that. Example usage:
// select some monkeys and modify a property in the select statement // instead of creating a new monkey and manually setting all properties var list = from monkey in monkeys select monkey.Set(monkey1 => { monkey1.FavoriteFood += " and banannas"; });
This is much quicker and more concise than creating a new monkey object and manually setting all properties, like so:
// instead of var list = from monkey in monkeys select new Monkey { Name = monkey.Name, FavoriteFood = monkey.FavoriteFood += " and banannas", FavoriteBeer = monkey.FavoriteBeer, FavoriteActivity = monkey.FavoriteActivity, EyeColor = monkey.EyeColor, HairColor = monkey.HairColor, LikesToFlingPoo = monkey.LikesToFlingPoo // etc... };
Answer: Write an extension method. I wrote an extension method called Set that to facilitate this. All it does is allow you to write a lambda expression inside your select statement that returns the same object that you pass it. You can set any number of properties on that object without creating a new instance of it. Here’s the source code:
namespace System.Linq { public static class LinqExtensions { /// <summary> /// Used to modify properties of an object returned from a LINQ query /// </summary> public static TSource Set<TSource>(this TSource input,
Action<TSource> updater) { updater(input); return input; } } }
Cool.
ReplyDelete-David L. Goldberg
Thanks
ReplyDeletenice technique
ReplyDeletebrilliant!
ReplyDeleteThis comment has been removed by the author.
ReplyDeletethis method doesn't work with LINQ to SQL rite?
ReplyDeleteonly work with an existing object?
i got this error...
"A lambda expression with a statement body cannot be converted to an expression tree"
I haven't tested it with LINQ to SQL, sorry. I designed it for use with LINQ to Objects primarily but I'm sure with some tweaks it will work with other flavors of LINQ. I'm pretty sure it works with LINQ to XML. Post a comment if you find a solution.
ReplyDeleteyou could replace the delegate with the Action() generic delegate class.
ReplyDeleteyour method then becomes:
public static TSource Set(this TSource input, Action updater)
{
updater(input);
return input;
}
Good call, Thiago. I wrote this before I completely understood Action<> & Func<> and haven't updated the code.
ReplyDeleteor use foreach method:
ReplyDeletemonkeys.ForEach(x => { monkey1.FavoriteFood += " and banannas" } )
Very helpful! Thx, KK
ReplyDeleteAny solution found for LINQ to SQL? Pls provide the select statement for the same in linq
ReplyDeleteCongratulations! Very nice and clean technique!
ReplyDeleteAll the best,
Cosmin
I have same problem as @y2kstephen.
ReplyDeleteI also got error
"A lambda expression with a statement body cannot be converted to an expression tree"
while trying to use it in Linq to SQL.
El Moist!
ReplyDeleteNice extension. I've been too lazy to do this myself, so I'm glad you did it for me. :)
ReplyDeleteOn the first parameter of the Set method, I'm gettting the 'A lambda expression with a statement body cannot be converted to a an expression tree.'
ReplyDeleteAny clue on how to resolve this?
Are you trying this using LINQ to SQL? This isn't supported by that because there's no logic to convert the method into SQL. Try enumerating your result set first like so:
ReplyDeletevar result = db.Table.ToArray();
then call my method.