I want to change some properties of a LINQ query result object without creating a new object and manually setting every property. Is this possible?
Example:
var list = from something in someList
select x // but change one property
I'm not sure what the query syntax is. But here is the expanded LINQ expression example.
var query = someList.Select(x => { x.SomeProp = "foo"; return x; })
What this does is use an anonymous method vs and expression. This allows you to use several statements in one lambda. So you can combine the two operations of setting the property and returning the object into this somewhat succinct method.
If you just want to update the property on all elements then
someList.All(x => { x.SomeProp = "foo"; return true; })
I prefer this one. It can be combined with other linq commands.
from item in list
let xyz = item.PropertyToChange = calcValue()
select item
There shouldn't be any LINQ magic keeping you from doing this. Don't use projection though that'll return an anonymous type.
User u = UserCollection.FirstOrDefault(u => u.Id == 1);
u.FirstName = "Bob"
That will modify the real object, as well as:
foreach (User u in UserCollection.Where(u => u.Id > 10)
{
u.Property = SomeValue;
}
It is not possible with the standard query operators - it is Language Integrated Query, not Language Integrated Update. But you could hide your update in extension methods.
public static class UpdateExtension
{
public static IEnumerable<Car> ChangeColorTo(
this IEnumerable<Car> cars, Color color)
{
foreach (Car car in cars)
{
car.Color = color;
yield return car;
}
}
}
Now you can use it as follows.
cars.Where(car => car.Color == Color.Blue).ChangeColorTo(Color.Red);
UPDATE
, DELETE
, and INSERT
. So I wouldn't argue that semantics is the thing preventing this function.
If you want to update items with a Where
clause, using a .Where(...) will truncate your results if you do:
list = list.Where(n => n.Id == ID).Select(n => { n.Property = ""; return n; }).ToList();
You can do updates to specific item(s) in the list like so:
list = list.Select(n => { if (n.Id == ID) { n.Property = ""; } return n; }).ToList();
Always return item even if you don't make any changes. This way it will be kept in the list.
We often run into this where we want to include a the index value and first and last indicators in a list without creating a new object. This allows you to know the position of the item in your list, enumeration, etc. without having to modify the existing class and then knowing whether you're at the first item in the list or the last.
foreach (Item item in this.Items
.Select((x, i) => {
x.ListIndex = i;
x.IsFirst = i == 0;
x.IsLast = i == this.Items.Count-1;
return x;
}))
You can simply extend any class by using:
public abstract class IteratorExtender {
public int ListIndex { get; set; }
public bool IsFirst { get; set; }
public bool IsLast { get; set; }
}
public class Item : IteratorExtender {}
In 2020 I use the MoreLinq
Pipe
method. https://morelinq.github.io/2.3/ref/api/html/M_MoreLinq_MoreEnumerable_Pipe__1.htm
Since I didn´t find the answer here which I consider the best solution, here my way:
Using "Select" to modify data is possible, but just with a trick. Anyway, "Select" is not made for that. It just executes the modification when used with "ToList", because Linq doesn´t execute before the data is being needed. Anyway, the best solution is using "foreach". In the following code, you can see:
class Person
{
public int Age;
}
class Program
{
private static void Main(string[] args)
{
var persons = new List<Person>(new[] {new Person {Age = 20}, new Person {Age = 22}});
PrintPersons(persons);
//this doesn't work:
persons.Select(p =>
{
p.Age++;
return p;
});
PrintPersons(persons);
//with "ToList" it works
persons.Select(p =>
{
p.Age++;
return p;
}).ToList();
PrintPersons(persons);
//This is the best solution
persons.ForEach(p =>
{
p.Age++;
});
PrintPersons(persons);
Console.ReadLine();
}
private static void PrintPersons(List<Person> persons)
{
Console.WriteLine("================");
foreach (var person in persons)
{
Console.WriteLine("Age: {0}", person.Age);
;
}
}
}
Before "foreach", you can also make a linq selection...
var item = (from something in someList
select x).firstordefault();
Would get the item
, and then you could do item.prop1=5;
to change the specific property.
Or are you wanting to get a list of items from the db and have it change the property prop1
on each item in that returned list to a specified value? If so you could do this (I'm doing it in VB because I know it better):
dim list = from something in someList select x
for each item in list
item.prop1=5
next
(list
will contain all the items returned with your changes)
for each
loop.
This is the very easiest solution: list.Where(t => t.Id == 1).ToList().ForEach(t => t.Property = "something");
Tried and worked, quoted from here: https://visualstudiomagazine.com/articles/2019/07/01/updating-linq.aspx
I ran into the exact requirement (OP's question) and I don't see that being answered anywhere above using expressions.
Assuming x
is of type MyType
, you can call a method that returns MyType
, which takes in x
. Inside that method you can do all the modifications you want. The code will look something like this.
var list = from something in someList
select GetModifiedObject(x); // but change one property
private MyType GetModifiedObject(MyType x)
{
x.Property1 = "Changed Value";
return x;
}
User u = UserCollection.Single(u => u.Id == 1);
u.FirstName = "Bob"
Success story sharing
ToList()
before that seems to do the trick. Not sure why you get that though. If it is Entity Framework, it does not work with that either.