LINQ: sélectionnez un objet et modifiez certaines propriétés sans créer de nouvel objet

217

Je souhaite modifier certaines propriétés d'un objet de résultat de requête LINQ sans créer un nouvel objet et définir manuellement chaque propriété. Est-ce possible?

Exemple:

var list = from something in someList
           select x // but change one property
Rob Volk
la source
7
J'ai écrit une méthode d'extension basée sur la réponse de JaredPar ci-dessous qui vous permet d'accomplir cela en utilisant une syntaxe déclarative comme mon exemple. Découvrez-le: blog.robvolk.com/2009/05/…
Rob Volk
3
@RobVolk, le lien semble mort - en avez-vous un nouveau? Voici une archive - LINQ: sélectionnez un objet, mais modifiez certaines propriétés sans créer un nouvel objet
KyleMit
blog.robvolk.com/2009/05/… introuvable Erreur DNS
Kiquenet
1
Désolé pour ça! ihere est la bonne adresse: robvolk.com/…
Rob Volk

Réponses:

379

Je ne suis pas sûr de la syntaxe de la requête. Mais voici l'exemple d'expression LINQ développé.

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

Cela utilise une méthode anonyme vs et une expression. Cela vous permet d'utiliser plusieurs instructions dans un lambda. Vous pouvez donc combiner les deux opérations de définition de la propriété et de retour de l'objet dans cette méthode quelque peu succincte.

JaredPar
la source
4
@Rob, pas facilement. La syntaxe pour que cela fonctionne est ... illisible au mieux. var query = à partir de celui-ci dans la liste select ((Func <Foo>) (() => {it.x = 42; le renvoyer;})) ();
JaredPar
35
J'obtiens l'erreur "Une expression lambda avec un corps d'instruction ne peut pas être convertie en arborescence d'expression". Ce n'est pas pour LINQ to SQL, un conseil?
surya
2
@spiderdevil +1 pour vous - ne détestez-vous pas simplement la disparité? Ce n'est pas la première fois que je rencontre une situation où le code ne fonctionne que pour Linq to Object et non Linq to SQL / EF. Cela pourrait être une solution ici , mais je n'ai pas encore essayé de l'utiliser parce que je n'ai pas le temps.
dyslexicanaboko
11
@surya C'est exactement le message que j'ai reçu en l'essayant avec Linq to SQL. Ajouter un ToList()avant qui semble faire l'affaire. Je ne sais pas pourquoi vous obtenez cela. Si c'est Entity Framework, il ne fonctionne pas non plus avec cela.
Raziel
5
@surya lorsque vous appelez ToList (), vous ramenez en fait les données en mémoire. Donc, ce n'est plus réellement Linq to SQL.
Oak
60

Si vous souhaitez simplement mettre à jour la propriété sur tous les éléments,

someList.All(x => { x.SomeProp = "foo"; return true; })
Jon Spokes
la source
3
Dans EF (Entity Framework): pour remplacer une propriété sur tous les objets d'un IEnumerable, la réponse acceptée a fonctionné pour moi. Code de travail pour moi: var myList = _db.MyObjects.Where (o => o.MyProp == "bar"). AsEnumerable (). Sélectionnez (x => {x.SomeProp = "foo"; return x;}) ;
firepol
32

Je préfère celui-ci. Il peut être combiné avec d'autres commandes linq.

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

Aucune magie LINQ ne devrait vous empêcher de le faire. N'utilisez cependant pas de projection qui renverra un type anonyme.

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

Cela modifiera l'objet réel, ainsi que:

foreach (User u in UserCollection.Where(u => u.Id > 10)
{
    u.Property = SomeValue;
}
Joshua Belden
la source
J'aime ça, ma question est dans le premier exemple et si certains u> 10 ne sont pas trouvés dans la liste? J'ai ajouté un chèque nul et semble fonctionner. Aussi LHS, j'ai nommé u à v. +1 cependant. Très concis.
desaivv
11

Ce n'est pas possible avec les opérateurs de requête standard - c'est Language Integrated Query, pas Language Integrated Update. Mais vous pouvez masquer votre mise à jour dans les méthodes d'extension.

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

Vous pouvez maintenant l'utiliser comme suit.

cars.Where(car => car.Color == Color.Blue).ChangeColorTo(Color.Red);
Daniel Brückner
la source
1
Ce n'est pas que vous souhaitez mettre à jour la collection de base. Nous voulons que la propriété de la collection de résultats soit mise à jour.
Michael Brennt le
4
Eh bien LINQ remplace SQL qui signifie Structured Query Language, mais il expose encore la capacité de capacité à UPDATE, DELETEet INSERT. Je ne dirais donc pas que la sémantique est la chose qui empêche cette fonction.
KyleMit
5

Si vous souhaitez mettre à jour des éléments avec une Whereclause, l'utilisation d'un .Where (...) tronquera vos résultats si vous le faites:

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

Vous pouvez effectuer des mises à jour sur des éléments spécifiques de la liste comme suit:

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

Renvoyez toujours l'article même si vous n'apportez aucune modification. De cette façon, il sera conservé dans la liste.

Pierre
la source
1

Nous rencontrons souvent ceci où nous voulons inclure la valeur d'index et les premier et dernier indicateurs dans une liste sans créer un nouvel objet. Cela vous permet de connaître la position de l'élément dans votre liste, l'énumération, etc. sans avoir à modifier la classe existante et à savoir ensuite si vous êtes au premier élément de la liste ou au dernier.

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

Vous pouvez simplement étendre n'importe quelle classe en utilisant:

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

public class Item : IteratorExtender {}
Informatique
la source
0

Comme je n'ai pas trouvé ici la réponse que je considère comme la meilleure solution, voici mon chemin:

Utiliser "Select" pour modifier les données est possible, mais juste avec une astuce. Quoi qu'il en soit, "Select" n'est pas fait pour ça. Il exécute simplement la modification lorsqu'il est utilisé avec "ToList", car Linq ne s'exécute pas avant que les données ne soient nécessaires. Quoi qu'il en soit, la meilleure solution consiste à utiliser "foreach". Dans le code suivant, vous pouvez voir:

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

Avant "foreach", vous pouvez également faire une sélection linq ...

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

Obtiendrait le item, puis vous pourriez faire item.prop1=5;pour modifier la propriété spécifique.

Ou souhaitez-vous obtenir une liste d'éléments de la base de données et lui faire changer la propriété prop1de chaque élément de cette liste renvoyée en une valeur spécifiée? Si oui, vous pouvez le faire (je le fais en VB parce que je le sais mieux):

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

( listcontiendra tous les articles retournés avec vos modifications)

Solmead
la source
Bien que cela fonctionne, je pense que l'OP demandait comment écrire l'instruction LINQ sans avoir à écrire une for eachboucle.
fujiiface
-3
User u = UserCollection.Single(u => u.Id == 1);
u.FirstName = "Bob"
kazem
la source
1
Pas besoin de dupliquer les réponses existantes sur ce fil . Si vous avez un commentaire sur cette réponse, vous pouvez le placer ici.
KyleMit