LINQ: seleccione un objeto y cambie algunas propiedades sin crear un nuevo objeto

217

Quiero cambiar algunas propiedades de un objeto de resultado de consulta LINQ sin crear un nuevo objeto y configurar manualmente cada propiedad. es posible?

Ejemplo:

var list = from something in someList
           select x // but change one property
Rob Volk
fuente
77
Escribí un método de extensión basado en la respuesta de JaredPar a continuación que le permite lograr esto usando una sintaxis declarativa como mi ejemplo. Compruébalo: blog.robvolk.com/2009/05/…
Rob Volk
3
@RobVolk, el enlace parece muerto: ¿tienes uno nuevo? Aquí hay un archivo - LINQ: seleccione un objeto, pero cambie algunas propiedades sin crear un nuevo objeto
KyleMit
blog.robvolk.com/2009/05/… no encontrado Error de DNS
Kiquenet
1
¡Lo siento por eso! esta es la dirección correcta: robvolk.com/…
Rob Volk
1
Ver también: stackoverflow.com/questions/47836019/…
Anterior

Respuestas:

379

No estoy seguro de cuál es la sintaxis de la consulta. Pero aquí está el ejemplo de expresión LINQ expandida.

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

Lo que esto hace es usar un método anónimo vs y expresión. Esto le permite usar varias declaraciones en una lambda. Por lo tanto, puede combinar las dos operaciones de establecer la propiedad y devolver el objeto a este método algo sucinto.

JaredPar
fuente
44
@Rob, no es fácil. La sintaxis para que funcione es ... ilegible en el mejor de los casos. var query = de ella en la lista select ((Func <Foo>) (() => {it.x = 42; return it;})) ();
JaredPar
35
Aparece el error "No se puede convertir una expresión lambda con un cuerpo de declaración en un árbol de expresión". No es para LINQ to SQL, ¿algún consejo?
surya
2
@spiderdevil +1 para ti, ¿no odias la disparidad? Esta no es la primera vez que me encuentro con una situación en la que el código solo funciona para Linq to Object y no Linq to SQL / EF. Esto podría ser una solución aquí , pero aún no he intentado usarlo porque no tengo tiempo.
dyslexicanaboko
11
@surya Ese es exactamente el mensaje que recibí cuando lo intenté con Linq to SQL. Agregar un ToList()antes parece ser el truco. Aunque no estoy seguro de por qué lo entiendes. Si es Entity Framework, tampoco funciona con eso.
Raziel
55
@surya cuando llamas a ToList () en realidad estás trayendo los datos nuevamente a la memoria. Entonces, en realidad, ya no es Linq to SQL.
Roble
60

Si solo desea actualizar la propiedad en todos los elementos, entonces

someList.All(x => { x.SomeProp = "foo"; return true; })
Jon Spokes
fuente
3
En EF (Entity Framework): para reemplazar una propiedad en todos los objetos de un IEnumerable, la respuesta aceptada funcionó para mí. Código de trabajo para mí: var myList = _db.MyObjects.Where (o => o.MyProp == "bar"). AsEnumerable (). Seleccione (x => {x.SomeProp = "foo"; return x;}) ;
firepol
32

Prefiero éste. Se puede combinar con otros comandos linq.

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

No debería haber ninguna magia LINQ que te impida hacer esto. Sin embargo, no use la proyección que devolverá un tipo anónimo.

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

Eso modificará el objeto real, así como:

foreach (User u in UserCollection.Where(u => u.Id > 10)
{
    u.Property = SomeValue;
}
Joshua Belden
fuente
Me gusta esto, mi pregunta es en el primer ejemplo, ¿qué pasa si algún u> 10 no se encuentra en la lista? Agregué un cheque nulo y parece funcionar. También LHS te llamé a v. +1 sin embargo. Muy conciso.
desaivv
11

No es posible con los operadores de consulta estándar: es Language Integrated Query, no Language Integrated Update. Pero podría ocultar su actualización en métodos de extensión.

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

Ahora puede usarlo de la siguiente manera.

cars.Where(car => car.Color == Color.Blue).ChangeColorTo(Color.Red);
Daniel Brückner
fuente
1
No es que quieras actualizar la colección base. Queremos que se actualice la propiedad de recopilación de resultados.
Michael Brennt
44
Así LINQ reemplaza SQL que significa estructurado de consultas Idioma, pero todavía expone la capacidad de capacidad de UPDATE, DELETE, y INSERT. Así que no diría que la semántica es lo que impide esta función.
KyleMit
5

Si desea actualizar elementos con una Wherecláusula, el uso de .Where (...) truncará sus resultados si lo hace:

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

Puede hacer actualizaciones a elementos específicos en la lista de la siguiente manera:

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

Siempre devuelva el artículo, incluso si no realiza ningún cambio. De esta manera se mantendrá en la lista.

Pierre
fuente
1

A menudo nos encontramos con esto donde queremos incluir un valor de índice y el primer y último indicador en una lista sin crear un nuevo objeto. Esto le permite conocer la posición del elemento en su lista, enumeración, etc. sin tener que modificar la clase existente y luego saber si está en el primer elemento de la lista o en el último.

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

Simplemente puede extender cualquier clase usando:

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

public class Item : IteratorExtender {}
Informitics
fuente
0

Como no encontré la respuesta aquí, que considero la mejor solución, a mi manera:

Usar "Seleccionar" para modificar datos es posible, pero solo con un truco. De todos modos, "Seleccionar" no está hecho para eso. Simplemente ejecuta la modificación cuando se usa con "ToList", porque Linq no se ejecuta antes de que se necesiten los datos. De todos modos, la mejor solución es usar "foreach". En el siguiente código, puedes ver:

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

Antes de "foreach", también puede hacer una selección de linq ...

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

Obtendría el item, y luego podría hacer item.prop1=5;para cambiar la propiedad específica.

¿O desea obtener una lista de elementos de la base de datos y hacer que cambie la propiedad prop1de cada elemento de esa lista devuelta a un valor especificado? Si es así, podrías hacer esto (lo estoy haciendo en VB porque lo sé mejor):

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

( listcontendrá todos los artículos devueltos con sus cambios)

Solmead
fuente
Si bien esto funcionará, creo que el OP preguntaba cómo escribir la declaración LINQ sin tener que escribir un for eachbucle.
fujiiface
-3
User u = UserCollection.Single(u => u.Id == 1);
u.FirstName = "Bob"
kazem
fuente
1
No es necesario duplicar las respuestas existentes en este hilo . Si tiene un comentario sobre esa respuesta, puede colocarla allí.
KyleMit