LINQ: выберите объект и измените некоторые свойства, не создавая новый объект

218

Я хочу изменить некоторые свойства объекта результата запроса LINQ без создания нового объекта и установки каждого свойства вручную. Это возможно?

Пример:

var list = from something in someList
           select x // but change one property
Роб Волк
источник
7
Я написал метод расширения, основанный на ответе JaredPar ниже, который позволяет вам сделать это, используя декларативный синтаксис, как в моем примере. Проверьте это: blog.robvolk.com/2009/05/…
Роб Волк
3
@RobVolk, ссылка кажется мертвой - есть новая? Вот архив - LINQ: выберите объект, но измените некоторые свойства, не создавая новый объект
KyleMit
blog.robvolk.com/2009/05/… не найдена ошибка DNS
Kiquenet
1
Извини за это! Вот правильный адрес: robvolk.com/…
Роб Волк
1
См. Также: stackoverflow.com/questions/47836019/…
Ревив

Ответы:

380

Я не уверен, что синтаксис запроса. Но вот расширенный пример выражения LINQ.

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

Что это делает, это использовать анонимный метод против и выражение. Это позволяет использовать несколько операторов в одной лямбде. Таким образом, вы можете объединить две операции установки свойства и возврата объекта в этот несколько лаконичный метод.

JaredPar
источник
4
@Rob, не легко. Синтаксис, чтобы заставить это работать ... не читается в лучшем случае. var query = из него в списке select ((Func <Foo>) (() => {it.x = 42; вернуть его;})) ();
JaredPar
35
Я получаю сообщение об ошибке «Лямбда-выражение с телом оператора не может быть преобразовано в дерево выражений». Это не для LINQ to SQL, любой совет?
Сурья
2
@spiderdevil +1 для вас - вы просто ненавидите неравенство? Это не первый раз, когда я сталкиваюсь с ситуацией, когда код работает только для Linq to Object, а не для Linq to SQL / EF. Это может быть решением здесь , но я еще не пытался его использовать, потому что у меня нет времени.
dyslexicanaboko
11
@surya Это именно то сообщение, которое я получил при попытке использовать Linq для SQL. Добавление ToList()до этого, кажется, делает свое дело. Не уверен, почему вы это получаете, хотя. Если это Entity Framework, он также не работает с этим.
Разиэль
5
@surya, когда вы вызываете ToList (), вы фактически возвращаете данные в память. Так что на самом деле это уже не Linq to SQL.
Дуб
60

Если вы просто хотите обновить свойство для всех элементов, то

someList.All(x => { x.SomeProp = "foo"; return true; })
Джон Спокс
источник
3
В EF (Entity Framework): чтобы заменить свойство на всех объектах IEnumerable, принятый ответ сработал для меня. Рабочий код для меня: var myList = _db.MyObjects.Where (o => o.MyProp == "bar"). AsEnumerable (). Select (x => {x.SomeProp = "foo"; вернуть x;}) ;
firepol
32

Я предпочитаю этот. Это может быть объединено с другими командами linq.

from item in list
let xyz = item.PropertyToChange = calcValue()
select item
Ян Захрадник
источник
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
11

Это невозможно со стандартными операторами запросов - это встроенный запрос языка, а не встроенное обновление языка. Но вы можете скрыть свое обновление в методах расширения.

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
Дело не в том, что вы хотите обновить базовую коллекцию. Мы хотим, чтобы свойство коллекции результатов обновлялось.
Майкл Брэннт
4
Ну LINQ заменяет SQL , который стоит на Structured Query Language, но она по- прежнему предоставляет возможность способность UPDATE, DELETEи INSERT. Поэтому я бы не стал утверждать, что семантика препятствует этой функции.
KyleMit
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 {}
Informitics
источник
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будет содержать все элементы, возвращенные с вашими изменениями)

Solmead
источник
Хотя это будет работать, я думаю, что OP спрашивал, как написать оператор LINQ без необходимости писать for eachцикл.
fujiiface