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 / ...
Revious

Відповіді:


380

Я не впевнений, що таке синтаксис запитів. Але ось приклад розширеного виразу LINQ.

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

Для цього використовується анонімний метод vs та вираження. Це дозволяє використовувати кілька тверджень в одній лямбда. Таким чином, ви можете поєднати дві операції встановлення властивості та повернення об'єкта в цей дещо лаконічний метод.


4
@Rob, Не легко. Синтаксис для роботи - у кращому випадку нечитабельний. var query = з нього у списку select ((Func <Foo>) (() => {it.x = 42; повернути його;})) ();
JaredPar

35
Я отримую помилку "лямбда-вираз із тілом оператора не може бути перетворений на дерево виразів". Це не для LINQ для SQL, будь-яка порада?
surya

2
@spiderdevil +1 для вас - ви просто не ненавидите розбіжність? Це не перший раз, коли я зіткнувся з ситуацією, коли код працює лише для Linq для Object, а не для Linq для SQL / EF. Це може бути рішення тут , але я не пробував використовувати його ще й тому у мене немає часу.
dislexicanaboko

11
@surya Це саме те повідомлення, яке я отримав при спробі використання Linq в SQL. Додавання ToList()до цього, здається, робить трюк. Не впевнений, чому ви все це отримаєте. Якщо це Entity Framework, він також не працює з цим.
Разіель

5
@surya, коли ви телефонуєте в ToList (), ви фактично повертаєте дані в пам'ять. Тож його вже насправді немає Linq до 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 (). Виберіть (x => {x.SomeProp = "foo"; return 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 я назвав u до +1 +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 {}

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

Перед тим, як "передбачити", ви також можете зробити підбірку linq ...


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

Ви отримаєте item, і тоді ви можете зробити, item.prop1=5;щоб змінити конкретну властивість.

Або ви хочете отримати список елементів з db і чи змінює він властивість prop1кожного елемента у цьому поверненому списку на вказане значення? Якщо так, ви можете зробити це (я це роблю в VB, тому що я це краще знаю):

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

( listміститиме всі елементи, повернені із змінами)


Хоча це буде працювати, я думаю, що ОП запитували, як написати заяву LINQ, не вимагаючи написання for eachциклу.
fujiiface


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.