LINQ: Pilih objek dan ubah beberapa properti tanpa membuat objek baru

217

Saya ingin mengubah beberapa properti dari objek hasil pencarian LINQ tanpa membuat objek baru dan secara manual mengatur setiap properti. Apakah ini mungkin?

Contoh:

var list = from something in someList
           select x // but change one property
Rob Volk
sumber
7
Saya menulis metode ekstensi berdasarkan jawaban JaredPar di bawah ini yang memungkinkan Anda untuk menyelesaikan ini menggunakan sintaksis deklaratif seperti contoh saya. Lihat: blog.robvolk.com/2009/05/...
Rob Volk
3
@RobVolk, tautan nampak mati - ada yang baru? Berikut ini adalah arsip - LINQ: Pilih objek, tetapi ubah beberapa properti tanpa membuat objek baru
KyleMit
blog.robvolk.com/2009/05/... tidak ditemukan Kesalahan DNS
Kiquenet
1
maaf soal itu! di sini adalah alamat yang benar: robvolk.com/...
Rob Volk

Jawaban:

379

Saya tidak yakin apa sintaks kueri itu. Tapi di sini adalah contoh ekspresi LINQ yang diperluas.

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

Apa yang dilakukan adalah menggunakan metode anonim vs dan ekspresi. Ini memungkinkan Anda untuk menggunakan beberapa pernyataan dalam satu lambda. Jadi, Anda dapat menggabungkan dua operasi pengaturan properti dan mengembalikan objek ke metode yang ringkas ini.

JaredPar
sumber
4
@Rob, Tidak mudah. Sintaks untuk menjalankannya adalah ... paling tidak dapat dibaca. var query = dari dalam daftar pilih ((Fungsi <Foo>) (() => {it.x = 42; kembalikan;})) ();
JaredPar
35
Saya mendapatkan "Ekspresi lambda dengan badan pernyataan tidak dapat dikonversi ke pohon ekspresi" kesalahan. Ini bukan untuk LINQ to SQL, saran?
surya
2
@spiderdevil +1 untuk Anda - bukankah Anda membenci perbedaan itu saja? Ini bukan pertama kalinya saya mengalami situasi di mana kode hanya berfungsi untuk Linq ke Object dan bukan Linq ke SQL / EF. Ini mungkin solusi di sini , tetapi saya belum mencoba menggunakannya karena saya tidak punya waktu.
dyslexicanaboko
11
@surya Itulah pesan yang saya dapat saat mencobanya dengan Linq to SQL. Menambahkan ToList()sebelum sepertinya berhasil. Tidak yakin mengapa Anda mendapatkannya. Jika itu adalah Entity Framework, itu tidak berfungsi dengan baik.
Raziel
5
@ surya ketika Anda memanggil ToList () Anda sebenarnya membawa data kembali ke memori. Jadi sebenarnya bukan Linq to SQL lagi.
Oak
60

Jika Anda hanya ingin memperbarui properti pada semua elemen, maka

someList.All(x => { x.SomeProp = "foo"; return true; })
Jon Spokes
sumber
3
Di EF (Entity Framework): untuk mengganti properti di semua objek dari IEnumerable, jawaban yang diterima bekerja untuk saya. Kode kerja untuk saya: var myList = _db.MyObjects.Where (o => o.MyProp == "bar"). AsEnumerable (). Pilih (x => {x.SomeProp = "foo"; return x;}) ;
firepol
32

Saya lebih suka yang ini. Itu dapat dikombinasikan dengan perintah LINQ lainnya.

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

Seharusnya tidak ada sihir LINQ yang mencegah Anda melakukan ini. Jangan gunakan proyeksi meskipun itu akan menghasilkan tipe anonim.

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

Itu akan mengubah objek nyata, serta:

foreach (User u in UserCollection.Where(u => u.Id > 10)
{
    u.Property = SomeValue;
}
Joshua Belden
sumber
Saya suka ini, pertanyaan saya adalah dalam contoh pertama bagaimana jika beberapa u> 10 tidak ditemukan dalam daftar? Saya menambahkan cek nol dan sepertinya berfungsi. Juga LHS saya beri nama u ke v. +1. Sangat singkat.
desaivv
11

Ini tidak mungkin dengan operator kueri standar - itu adalah Bahasa Integrated Query, bukan Language Integrated Update. Tetapi Anda dapat menyembunyikan pembaruan Anda dalam metode ekstensi.

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

Sekarang Anda dapat menggunakannya sebagai berikut.

cars.Where(car => car.Color == Color.Blue).ChangeColorTo(Color.Red);
Daniel Brückner
sumber
1
Bukannya Anda ingin memperbarui koleksi dasar. Kami ingin properti pengumpulan hasil diperbarui.
Michael Brennt
4
Nah LINQ menggantikan SQL yang merupakan singkatan dari Structured Query Language, tetapi masih memperlihatkan kemampuan kemampuan untuk UPDATE, DELETE, dan INSERT. Jadi saya tidak akan berpendapat bahwa semantik adalah hal yang mencegah fungsi ini.
KyleMit
5

Jika Anda ingin memperbarui item dengan Whereklausa, menggunakan .Where (...) akan memotong hasil Anda jika Anda melakukannya:

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

Anda dapat melakukan pembaruan untuk item tertentu dalam daftar seperti:

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

Selalu kembalikan item meskipun Anda tidak melakukan perubahan. Dengan cara ini akan disimpan dalam daftar.

Pierre
sumber
1

Kami sering mengalami hal ini di mana kami ingin memasukkan nilai indeks dan indikator pertama dan terakhir dalam daftar tanpa membuat objek baru. Ini memungkinkan Anda mengetahui posisi item dalam daftar Anda, enumerasi, dll. Tanpa harus memodifikasi kelas yang ada dan kemudian mengetahui apakah Anda berada di item pertama dalam daftar atau yang terakhir.

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

Anda bisa memperpanjang kelas apa saja dengan menggunakan:

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

public class Item : IteratorExtender {}
Informatika
sumber
0

Karena saya tidak menemukan jawaban di sini yang saya anggap solusi terbaik, inilah cara saya:

Menggunakan "Pilih" untuk memodifikasi data dimungkinkan, tetapi hanya dengan trik. Lagi pula, "Pilih" tidak dibuat untuk itu. Itu hanya menjalankan modifikasi ketika digunakan dengan "Daftar", karena Linq tidak mengeksekusi sebelum data sedang diperlukan. Bagaimanapun, solusi terbaik adalah menggunakan "foreach". Dalam kode berikut, Anda dapat melihat:

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

Sebelum "foreach", Anda juga dapat membuat pilihan LINQ ...

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

Akan mendapatkan item, dan kemudian Anda bisa lakukan item.prop1=5;untuk mengubah properti tertentu.

Atau Anda ingin mendapatkan daftar item dari db dan mengubahnya mengubah properti prop1pada setiap item dalam daftar yang dikembalikan ke nilai yang ditentukan? Jika demikian Anda bisa melakukan ini (saya melakukannya di VB karena saya tahu lebih baik):

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

( listakan berisi semua item yang dikembalikan bersama perubahan Anda)

Solmead
sumber
Sementara ini akan berhasil, saya pikir OP bertanya bagaimana menulis pernyataan LINQ tanpa harus menulis satu for eachlingkaran.
fujiiface
-3
User u = UserCollection.Single(u => u.Id == 1);
u.FirstName = "Bob"
Kazem
sumber
1
Tidak perlu menduplikasi jawaban yang ada di utas ini . Jika Anda memiliki komentar tentang jawaban itu, Anda dapat meletakkannya di sana.
KyleMit