LINQ: selezionare un oggetto e modificare alcune proprietà senza creare un nuovo oggetto


217

Voglio modificare alcune proprietà di un oggetto risultato della query LINQ senza creare un nuovo oggetto e impostare manualmente ogni proprietà. È possibile?

Esempio:

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

7
Ho scritto un metodo di estensione basato sulla risposta di JaredPar di seguito che consente di ottenere ciò utilizzando la sintassi dichiarativa come il mio esempio. Dai
Rob Volk,

3
@RobVolk, il link sembra morto - ne hai uno nuovo? Ecco un archivio - LINQ: seleziona un oggetto, ma modifica alcune proprietà senza creare un nuovo oggetto
KyleMit

blog.robvolk.com/2009/05/… non trovato Errore DNS
Kiquenet

1
scusa per quello! ecco l'indirizzo corretto: robvolk.com/…
Rob Volk,

Risposte:


379

Non sono sicuro di quale sia la sintassi della query. Ma ecco l'esempio di espressione LINQ espansa.

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

Quello che fa è usare un metodo anonimo vs ed espressione. Ciò consente di utilizzare diverse istruzioni in una lambda. Quindi puoi combinare le due operazioni per impostare la proprietà e restituire l'oggetto in questo metodo un po 'succinto.


4
@ Rob, non facilmente. La sintassi per farlo funzionare è ... nella migliore delle ipotesi illeggibile. var query = da esso nell'elenco selezionare ((Func <Foo>) (() => {it.x = 42; restituirlo;})) ();
JaredPar,

35
Sto ottenendo l'errore "Un'espressione lambda con un corpo di istruzione non può essere convertito in un albero di espressioni". Non è per LINQ to SQL, qualche consiglio?
surya,

2
@spiderdevil +1 per te - non odi semplicemente la disparità? Questa non è la prima volta che mi imbatto in una situazione in cui il codice funziona solo per Linq to Object e non Linq per SQL / EF. Questa potrebbe essere una soluzione qui , ma non ho ancora provato a usarla perché non ho tempo.
dyslexicanaboko,

11
@surya Questo è esattamente il messaggio che ho ricevuto provandolo con Linq to SQL. L'aggiunta di un ToList()prima sembra fare il trucco. Non sono sicuro del perché lo ottieni. Se si tratta di Entity Framework, non funziona neanche con quello.
Raziel,

5
@surya quando chiami ToList () stai effettivamente riportando i dati in memoria. Quindi in realtà non è più Linq to SQL.
Oak

60

Se vuoi solo aggiornare la proprietà su tutti gli elementi, allora

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

3
In EF (Entity Framework): per sostituire una proprietà su tutti gli oggetti di un IEnumerable, la risposta accettata ha funzionato per me. Codice di lavoro per me: var myList = _db.MyObjects.Where (o => o.MyProp == "bar"). AsEnumerable (). Seleziona (x => {x.SomeProp = "foo"; return x;}) ;
firepol,

32

Preferisco questo. Può essere combinato con altri comandi linq.

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

24

Non dovrebbe esserci alcuna magia LINQ che ti impedisce di farlo. Non usare la proiezione sebbene restituisca un tipo anonimo.

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

Ciò modificherà l'oggetto reale, nonché:

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

Mi piace questo, la mia domanda è nel primo esempio cosa succede se qualche u> 10 non viene trovato nell'elenco? Ho aggiunto un controllo null e sembra funzionare. Anche LHS ti ho nominato a v. +1 però. Molto conciso
desaivv,

11

Non è possibile con gli operatori di query standard: è Language Integrated Query, non Language Integrated Update. Ma potresti nascondere l'aggiornamento nei metodi di estensione.

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

Ora puoi usarlo come segue.

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

1
Non è che desideri aggiornare la raccolta di base. Vogliamo che la proprietà della raccolta dei risultati sia aggiornata.
Michael Brennt,

4
Bene LINQ sostituisce SQL che sta per Structured Query Language, ma espone ancora la capacità capacità di UPDATE, DELETEe INSERT. Quindi non direi che la semantica è la cosa che impedisce questa funzione.
KyleMit,

5

Se vuoi aggiornare gli articoli con una Whereclausola, usa un .Where (...) troncerà i tuoi risultati se lo fai:

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

Puoi fare aggiornamenti su specifici articoli nell'elenco in questo modo:

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

Restituisci sempre l'articolo anche se non apporti alcuna modifica. In questo modo verrà mantenuto nell'elenco.


1

Ci imbattiamo spesso in questo in cui vogliamo includere un valore di indice e il primo e l'ultimo indicatore in un elenco senza creare un nuovo oggetto. Ciò consente di conoscere la posizione dell'elemento nell'elenco, l'enumerazione, ecc. Senza dover modificare la classe esistente e quindi sapere se si è al primo elemento dell'elenco o all'ultimo.

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

Puoi semplicemente estendere qualsiasi classe usando:

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

public class Item : IteratorExtender {}

0

Dato che qui non ho trovato la risposta che considero la soluzione migliore, ecco la mia strada:

L'uso di "Seleziona" per modificare i dati è possibile, ma solo con un trucco. Comunque, "Seleziona" non è fatto per quello. Esegue semplicemente la modifica quando viene utilizzato con "ToList", poiché Linq non viene eseguito prima che i dati siano necessari. Comunque, la soluzione migliore sta usando "foreach". Nel seguente codice, puoi vedere:

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

Prima di "foreach", puoi anche effettuare una selezione linq ...


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

Otterresti il item, e quindi potresti fare item.prop1=5;per cambiare la proprietà specifica.

Oppure vuoi ottenere un elenco di elementi dal db e fare in modo che cambi la proprietà prop1su ciascun elemento dell'elenco restituito in un valore specificato? Se è così potresti farlo (lo sto facendo in VB perché lo so meglio):

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

( listconterrà tutti gli articoli restituiti con le modifiche)


Mentre funzionerà, penso che l'OP chiedesse come scrivere l'istruzione LINQ senza dover scrivere un for eachciclo.
fujiiface,


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.