LINQ: Wählen Sie ein Objekt aus und ändern Sie einige Eigenschaften, ohne ein neues Objekt zu erstellen

217

Ich möchte einige Eigenschaften eines LINQ-Abfrageergebnisobjekts ändern, ohne ein neues Objekt zu erstellen und jede Eigenschaft manuell festzulegen. Ist das möglich?

Beispiel:

var list = from something in someList
           select x // but change one property
Rob Volk
quelle
7
Ich habe eine Erweiterungsmethode geschrieben, die auf der folgenden Antwort von JaredPar basiert und es Ihnen ermöglicht, dies mit deklarativer Syntax wie in meinem Beispiel zu erreichen. Probieren
Rob Volk
3
@RobVolk, Link erscheint tot - hast du einen neuen? Hier ist ein Archiv - LINQ: Wählen Sie ein Objekt aus, aber ändern Sie einige Eigenschaften, ohne ein neues Objekt zu
erstellen
blog.robvolk.com/2009/05/… nicht gefunden DNS-Fehler
Kiquenet
1
Das tut mir leid! Hier ist die richtige Adresse: robvolk.com/…
Rob Volk

Antworten:

379

Ich bin mir nicht sicher, wie die Abfragesyntax lautet. Aber hier ist das erweiterte LINQ-Ausdrucksbeispiel.

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

Dies verwendet eine anonyme Methode vs und Ausdruck. Auf diese Weise können Sie mehrere Anweisungen in einem Lambda verwenden. Sie können also die beiden Operationen zum Festlegen der Eigenschaft und zum Zurückgeben des Objekts in diese etwas prägnante Methode kombinieren.

JaredPar
quelle
4
@ Rob, nicht einfach. Die Syntax, um das zum Laufen zu bringen, ist ... bestenfalls unlesbar. var query = daraus in der Liste select ((Func <Foo>) (() => {it.x = 42; return it;})) ();
JaredPar
35
Ich erhalte die Fehlermeldung "Ein Lambda-Ausdruck mit einem Anweisungskörper kann nicht in einen Ausdrucksbaum konvertiert werden". Es ist nicht für LINQ to SQL, ein Rat?
Surya
2
@spiderdevil +1 für dich - hasst du nicht einfach die Ungleichheit? Dies ist nicht das erste Mal, dass ich auf eine Situation stoße, in der Code nur für Linq to Object und nicht für Linq to SQL / EF funktioniert. Dies könnte eine Lösung sein , hier , aber ich habe nicht versucht , noch zu verwenden , weil ich die Zeit nicht haben.
Legasthenikeraboko
11
@surya Das ist genau die Nachricht, die ich beim Versuch mit Linq to SQL erhalten habe. Das Hinzufügen eines ToList()Vorher scheint den Trick zu tun. Ich bin mir nicht sicher, warum du das bekommst. Wenn es sich um Entity Framework handelt, funktioniert es auch damit nicht.
Raziel
5
@surya Wenn Sie ToList () aufrufen, bringen Sie die Daten tatsächlich wieder in den Speicher. Es ist also nicht mehr Linq to SQL.
Eiche
60

Wenn Sie nur die Eigenschaft für alle Elemente aktualisieren möchten, dann

someList.All(x => { x.SomeProp = "foo"; return true; })
Jon spricht
quelle
3
In EF (Entity Framework): Um eine Eigenschaft für alle Objekte einer IEnumerable zu ersetzen, hat die akzeptierte Antwort für mich funktioniert. Arbeitscode für mich: var myList = _db.MyObjects.Where (o => o.MyProp == "bar"). AsEnumerable (). Wählen Sie (x => {x.SomeProp = "foo"; return x;}) ;;
Firepol
32

Ich bevorzuge dieses hier. Es kann mit anderen linq-Befehlen kombiniert werden.

from item in list
let xyz = item.PropertyToChange = calcValue()
select item
Jan Zahradník
quelle
24

Es sollte keine LINQ-Magie geben, die Sie davon abhält. Verwenden Sie jedoch keine Projektion, da dies einen anonymen Typ zurückgibt.

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

Dadurch wird das reale Objekt geändert und:

foreach (User u in UserCollection.Where(u => u.Id > 10)
{
    u.Property = SomeValue;
}
Joshua Belden
quelle
Ich mag das, meine Frage ist im ersten Beispiel, was ist, wenn einige u> 10 nicht in der Liste gefunden werden? Ich habe einen Null-Check hinzugefügt und scheint zu funktionieren. Auch LHS habe ich dich zu v. +1 benannt. Sehr prägnant.
Desaivv
11

Mit den Standard-Abfrageoperatoren ist dies nicht möglich. Es handelt sich um eine sprachintegrierte Abfrage, nicht um eine sprachintegrierte Aktualisierung. Sie können Ihr Update jedoch in Erweiterungsmethoden ausblenden.

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

Jetzt können Sie es wie folgt verwenden.

cars.Where(car => car.Color == Color.Blue).ChangeColorTo(Color.Red);
Daniel Brückner
quelle
1
Es ist nicht so, dass Sie die Basissammlung aktualisieren möchten. Wir möchten, dass die Ergebnisauflistungseigenschaft aktualisiert wird.
Michael Brennt
4
Nun LINQ ersetzt SQL , die für Strukturierte steht Abfragesprache, aber es macht immer noch die Fähigkeit Fähigkeit zu UPDATE, DELETEund INSERT. Ich würde also nicht argumentieren, dass Semantik diese Funktion verhindert.
KyleMit
5

Wenn Sie Elemente mit einer WhereKlausel aktualisieren möchten, werden Ihre Ergebnisse durch die Verwendung von .Where (...) abgeschnitten, wenn Sie Folgendes tun:

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

Sie können bestimmte Elemente in der Liste folgendermaßen aktualisieren:

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

Geben Sie den Artikel immer zurück, auch wenn Sie keine Änderungen vornehmen. Auf diese Weise wird es in der Liste beibehalten.

Pierre
quelle
1

Wir stoßen häufig darauf, wenn wir den Indexwert sowie den ersten und den letzten Indikator in eine Liste aufnehmen möchten, ohne ein neues Objekt zu erstellen. Auf diese Weise können Sie die Position des Elements in Ihrer Liste, Aufzählung usw. ermitteln, ohne die vorhandene Klasse ändern zu müssen und dann zu wissen, ob Sie sich am ersten oder am letzten Element in der Liste befinden.

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

Sie können jede Klasse einfach erweitern, indem Sie Folgendes verwenden:

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

public class Item : IteratorExtender {}
Informatik
quelle
0

Da ich hier keine Antwort gefunden habe, die ich für die beste Lösung halte, hier mein Weg:

Die Verwendung von "Auswählen" zum Ändern von Daten ist möglich, jedoch nur mit einem Trick. "Select" ist dafür sowieso nicht gemacht. Bei Verwendung mit "ToList" wird die Änderung nur ausgeführt, da Linq nicht ausgeführt wird, bevor die Daten benötigt werden. Wie auch immer, die beste Lösung ist die Verwendung von "foreach". Im folgenden Code sehen Sie:

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

Vor "foreach" können Sie auch eine linq-Auswahl treffen ...

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

Würde das bekommen item, und dann könntest du es tunitem.prop1=5; , um die spezifische Eigenschaft zu ändern.

Oder möchten Sie eine Liste von Elementen aus der Datenbank abrufen und die Eigenschaft prop1für jedes Element in dieser zurückgegebenen Liste auf einen bestimmten Wert ändern lassen ? Wenn ja, könnten Sie dies tun (ich mache es in VB, weil ich es besser weiß):

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

( listenthält alle mit Ihren Änderungen zurückgegebenen Artikel)

Solmead
quelle
Während dies funktionieren wird, hat das OP gefragt, wie die LINQ-Anweisung geschrieben werden soll, ohne dass eine for eachSchleife geschrieben werden muss.
Fujiiface
-3
User u = UserCollection.Single(u => u.Id == 1);
u.FirstName = "Bob"
Kazem
quelle
1
Die vorhandenen Antworten in diesem Thread müssen nicht dupliziert werden . Wenn Sie einen Kommentar zu dieser Antwort haben, können Sie ihn dort platzieren.
KyleMit