LINQ: Wybierz obiekt i zmień niektóre właściwości bez tworzenia nowego obiektu


218

Chcę zmienić niektóre właściwości obiektu wynikowego zapytania LINQ bez tworzenia nowego obiektu i ręcznego ustawiania każdej właściwości. czy to możliwe?

Przykład:

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

7
Napisałem metodę rozszerzenia opartą na odpowiedzi JaredPara poniżej, która pozwala to osiągnąć za pomocą składni deklaratywnej, tak jak w moim przykładzie. Sprawdź to: blog.robvolk.com/2009/05/…
— Rob Volk

3
@RobVolk, link wydaje się martwy - masz nowy? Oto archiwum - LINQ: Wybierz obiekt, ale zmień niektóre właściwości bez tworzenia nowego obiektu
— KyleMit

blog.robvolk.com/2009/05/… nie znaleziono Błąd DNS
— Kiquenet

1
przepraszam za to! prawidłowy adres: robvolk.com/…
— Rob Volk,

Odpowiedzi:


380

Nie jestem pewien, jaka jest składnia zapytania. Ale oto przykład rozszerzonego wyrażenia LINQ.

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

To robi to użyć anonimowej metody vs i wyrażenia. Pozwala to na użycie kilku instrukcji w jednej lambda. Możesz więc połączyć dwie operacje ustawiania właściwości i zwracania obiektu do tej nieco zwięzłej metody.


4
@Rob, Nie łatwo. Składnia umożliwiająca to działanie jest ... w najlepszym razie nieczytelna. var query = z listy w liście select ((Func <Foo>) (() => {it.x = 42; return it;})) ();
— JaredPar

35
Otrzymuję błąd „Nie można przekonwertować wyrażenia lambda z treścią instrukcji na drzewo wyrażeń”. To nie jest LINQ na SQL, jakaś rada?
— surya

2
@spiderdevil +1 dla Ciebie - czy nie nienawidzisz różnic? To nie pierwszy raz, kiedy napotkałem sytuację, w której kod działa tylko dla Linq na Object, a nie Linq na SQL / EF. To może być rozwiązanie tutaj , ale nie próbowałem go jeszcze użyć, ponieważ nie mam czasu.
— dyslexicanaboko

11
@surya To jest dokładnie wiadomość, którą otrzymałem, gdy próbowałem z Linq na SQL. Dodanie ToList()przed tym wydaje się załatwić sprawę. Nie jestem pewien, dlaczego to dostajesz. Jeśli jest to Entity Framework, to też z tym nie działa.
— Raziel

5
@surya, kiedy wywołujesz ToList (), faktycznie przywracasz dane do pamięci. Więc to właściwie nie jest już Linq na SQL.
— Oak

60

Jeśli chcesz po prostu zaktualizować właściwość we wszystkich elementach, to

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

3
W EF (Entity Framework): aby zastąpić właściwość na wszystkich obiektach IEnumerable, zaakceptowana odpowiedź działała dla mnie. Działający kod dla mnie: var myList = _db.MyObjects.Where (o => o.MyProp == "bar"). AsEnumerable (). Wybierz (x => {x.SomeProp = "foo"; return x;}) ;
— firepol

32

Wolę tą. Można go łączyć z innymi poleceniami linq.

from item in list
let xyz = item.PropertyToChange = calcValue()
select item

24

Nie powinna istnieć żadna magia LINQ powstrzymująca cię przed zrobieniem tego. Nie używaj jednak projekcji, która zwróci anonimowy typ.

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

To zmodyfikuje prawdziwy obiekt, a także:

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

Podoba mi się, moje pytanie jest w pierwszym przykładzie, co jeśli jakieś u> 10 nie znajduje się na liście? Dodałem czek zerowy i wydaje się, że działa. Również LHS nazwałem u do v. +1. Bardzo zwięzłe.
— desaivv

11

Nie jest to możliwe w przypadku standardowych operatorów zapytań - jest to Zapytanie zintegrowane z językiem, a nie Aktualizacja zintegrowana z językiem. Ale możesz ukryć swoją aktualizację w metodach rozszerzenia.

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

Teraz możesz go używać w następujący sposób.

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

1
To nie tak, że chcesz zaktualizować kolekcję podstawową. Chcemy zaktualizować właściwość kolekcji wyników.
— Michael Brennt,

4
Dobrze LINQ zastępuje SQL, który stoi na Structured Query Language, ale wciąż naraża zdolność umiejętność UPDATE, DELETEi INSERT. Nie argumentowałbym więc, że semantyka jest tym, co zapobiega tej funkcji.
— KyleMit,

5

Jeśli chcesz zaktualizować elementy za pomocą Whereklauzuli, użycie .Where (...) obetnie wyniki, jeśli:

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

Możesz dokonywać aktualizacji określonych pozycji na liście w następujący sposób:

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

Zawsze zwracaj przedmiot, nawet jeśli nie dokonasz żadnych zmian. W ten sposób będzie on przechowywany na liście.


1

Często spotykamy się z tym, że chcemy dołączyć wartość indeksu oraz pierwszy i ostatni wskaźnik do listy bez tworzenia nowego obiektu. To pozwala poznać pozycję elementu na liście, wyliczenie itp. Bez konieczności modyfikowania istniejącej klasy, a następnie wiedzieć, czy jesteś na pierwszym elemencie na liście, czy na ostatnim.

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

Możesz po prostu rozszerzyć dowolną klasę, używając:

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

public class Item : IteratorExtender {}

0

Ponieważ nie znalazłem tutaj odpowiedzi, którą uważam za najlepsze rozwiązanie, oto moja droga:

Używanie „Wybierz” do modyfikowania danych jest możliwe, ale z pewną sztuczką. W każdym razie „Select” nie jest do tego stworzony. Po prostu wykonuje modyfikację, gdy jest używana z „ToList”, ponieważ Linq nie wykonuje się przed potrzebą danych. W każdym razie najlepszym rozwiązaniem jest użycie „foreach”. W poniższym kodzie widać:

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

Przed „foreach” możesz również dokonać wyboru linq ...


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

Otrzymałbym item, a następnie możesz zrobić, item.prop1=5;aby zmienić konkretną właściwość.

A może chcesz uzyskać listę elementów z bazy danych i zmienić tę właściwość prop1dla każdego elementu na tej zwróconej liście na określoną wartość? Jeśli tak, możesz to zrobić (robię to w VB, ponieważ wiem to lepiej):

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

( listbędzie zawierać wszystkie elementy zwrócone z Twoimi zmianami)


Chociaż to zadziała, myślę, że OP pytał, jak napisać instrukcję LINQ bez konieczności zapisywania for eachpętli.
— fujiiface


Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.