LINQ:选择一个对象并更改一些属性,而无需创建新对象


217

我想更改LINQ查询结果对象的某些属性,而无需创建新对象并手动设置每个属性。这可能吗?

例:

var list = from something in someList
           select x // but change one property

7
我根据下面的JaredPar的答案写了一个扩展方法,使您可以像我的示例一样使用声明性语法来完成此任务。看看:blog.robvolk.com/2009/05/…–
罗布·

3
@RobVolk,链接似乎已死-有新链接了吗?这是一个存档
-LINQ


1
对于那个很抱歉!ihere的正确地址:robvolk.com/...
罗布沃尔克

Answers:


379

我不确定查询语法是什么。但是,这是扩展的LINQ表达式示例。

var query = someList.Select(x => { x.SomeProp = "foo"; return x; })

这样做是使用匿名方法vs和表达式。这使您可以在一个lambda中使用多个语句。因此,您可以将设置属性和将对象返回到此简洁方法中的两种操作结合​​在一起。


4
@Rob,不容易。使该工作正常运行的语法是最好的。var query =从列表中的它中选择((Func <Foo>)(()=> {it.x = 42; return it;}))();
JaredPar

35
我收到“带有语句主体的lambda表达式无法转换为表达式树”错误。它不适合LINQ to SQL,有什么建议吗?
surya 2012年

2
@spiderdevil +1为您-您是否不讨厌这种差异?这不是我第一次遇到这样的情况,即代码仅适用于Linq to Object而不适用于Linq to SQL / EF。这可能是一个解决方案在这里,但我还没有尝试过使用它只因我没有时间。
dyslexicanaboko

11
@surya正是我在使用Linq to SQL尝试时得到的消息。在此ToList()之前添加一个似乎可以解决问题。不知道为什么你得到了。如果它是实体框架,那么它也不起作用。
拉齐尔

5
@surya调用ToList()时,实际上是在将数据带回到内存中。因此,它实际上不再是Linq to SQL了。
橡树

60

如果只想更新所有元素上的属性,则

someList.All(x => { x.SomeProp = "foo"; return true; })

3
在EF(实体框架)中:要替换IEnumerable的所有对象上的属性,可接受的答案对我有用。我的工作代码:var myList = _db.MyObjects.Where(o => o.MyProp ==“ bar”)。AsEnumerable()。Select(x => {x.SomeProp =“ foo”; return x;}) ;
firepol 2013年


24

不应有任何LINQ魔术使您无法这样做。尽管会返回一个匿名类型,但不要使用投影。

User u = UserCollection.FirstOrDefault(u => u.Id == 1);
u.FirstName = "Bob"

这将修改实际对象,以及:

foreach (User u in UserCollection.Where(u => u.Id > 10)
{
    u.Property = SomeValue;
}

我喜欢这样,我的问题是在第一个示例中,如果列表中未找到u> 10怎么办?我添加了一个空检查,似乎可以工作。我也将LHS命名为v。+1。非常简洁。
desaivv 2012年

11

使用标准查询运算符是不可能的-它是Language Integrated Query,不是Language Integrated Update。但是您可以在扩展方法中隐藏更新。

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;
       }
    }
}

现在,您可以按以下方式使用它。

cars.Where(car => car.Color == Color.Blue).ChangeColorTo(Color.Red);

1
不是您要更新基本集合。我们要更新结果集合属性。
Michael Brennt 2014年

4
那么LINQ取代SQL它代表结构化查询语言,但它仍然暴露出的能力能力UPDATEDELETEINSERT。因此,我不会认为语义是阻止此功能的原因。
KyleMit 2015年

5

如果要Where使用子句更新项目,请使用.Where(...)将截断结果,如果您这样做:

mylist = mylist.Where(n => n.Id == ID).Select(n => { n.Property = ""; return n; }).ToList();

您可以像这样对列表中的特定项目进行更新:

mylist = mylist.Select(n => { if (n.Id == ID) { n.Property = ""; } return n; }).ToList();

即使不做任何更改,也务必退回商品。这样,它将被保留在列表中。


1

我们经常遇到这种情况,我们希望在列表中包括索引值以及第一个和最后一个指示符,而无需创建新对象。这使您无需修改​​现有的类即可知道项目在列表中的位置,枚举等,然后知道您是列表中的第一项还是最后一项。

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;
}))

您可以使用以下方法简单地扩展任何类:

public abstract class IteratorExtender {
    public int ListIndex { get; set; }
    public bool IsFirst { get; set; } 
    public bool IsLast { get; set; } 
}

public class Item : IteratorExtender {}

0

由于我在这里找不到最佳解决方案的答案,因此按以下方式操作:

使用“选择”修改数据是可能的,但这只是一个技巧。无论如何,并非为此选择“选择”。与“ ToList”一起使用时,它仅执行修改,因为Linq不在需要数据之前执行。无论如何,最好的解决方案是使用“ foreach”。在以下代码中,您可以看到:

    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);
            ;
            }
        }
    }

在“ foreach”之前,您还可以进行linq选择...


0
var item = (from something in someList
       select x).firstordefault();

将获得item,然后您可以item.prop1=5;更改特定的属性。

还是要从数据库中获取项目列表,并让其将prop1返回列表中每个项目的属性更改为指定值?如果是这样,您可以这样做(我在VB中这样做是因为我更了解它):

dim list = from something in someList select x
for each item in list
    item.prop1=5
next

list将包含您所做更改返回的所有项目)


尽管这行得通,但我认为OP正在询问如何编写LINQ语句而不必编写for each循环。
fujiiface 2015年


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.