LINQ: Chọn một đối tượng và thay đổi một số thuộc tính mà không tạo đối tượng mới


217

Tôi muốn thay đổi một số thuộc tính của đối tượng kết quả truy vấn LINQ mà không tạo đối tượng mới và đặt thủ công mọi thuộc tính. Điều này có thể không?

Thí dụ:

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

7
Tôi đã viết một phương thức mở rộng dựa trên câu trả lời của JaredPar bên dưới cho phép bạn thực hiện điều này bằng cú pháp khai báo như ví dụ của tôi. Hãy xem thử: blog.robvolk.com/2009/05/ Mạnh
Rob

3
@RobVolk, liên kết xuất hiện chết - có một cái mới? Đây là kho lưu trữ - LINQ: Chọn một đối tượng, nhưng thay đổi một số thuộc tính mà không tạo đối tượng mới
KyleMit

blog.robvolk.com/2009/05/ Lỗi không tìm thấy Lỗi DNS
Kiquenet

1
xin lỗi vì điều đó! đây là địa chỉ chính xác: robvolk.com/ Hãy
Rob

Câu trả lời:


379

Tôi không chắc cú pháp truy vấn là gì. Nhưng đây là ví dụ biểu thức LINQ mở rộng.

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

Những gì nó làm là sử dụng một phương thức nặc danh vs và biểu thức. Điều này cho phép bạn sử dụng một số câu lệnh trong một lambda. Vì vậy, bạn có thể kết hợp hai thao tác thiết lập thuộc tính và trả lại đối tượng vào phương thức hơi súc tích này.


4
@Rob, Không dễ. Cú pháp để làm việc đó là ... không thể đọc được. var query = từ nó trong danh sách select ((Func <Foo>) (() => {it.x = 42; return it;})) ();
JaredPar

35
Tôi nhận được lỗi "Một biểu thức lambda với phần thân câu lệnh không thể được chuyển đổi thành một cây biểu thức". Nó không cho LINQ to SQL, lời khuyên nào?
surya

2
@spiderdevil +1 cho bạn - bạn không ghét sự chênh lệch sao? Đây không phải là lần đầu tiên tôi gặp phải tình huống mã chỉ hoạt động cho Linq to Object chứ không phải Linq to SQL / EF. Đây có thể là một giải pháp ở đây , nhưng tôi chưa thử sử dụng nó vì tôi không có thời gian.
chứng khó đọc

11
@surya Đó chính xác là thông điệp tôi nhận được khi dùng thử với Linq to SQL. Thêm một ToList()trước đó dường như để làm các mẹo. Không chắc chắn tại sao bạn có được điều đó mặc dù. Nếu đó là Entity Framework, nó cũng không hoạt động với điều đó.
Raziel

5
@surya khi bạn gọi ToList () bạn thực sự sẽ đưa dữ liệu trở lại bộ nhớ. Vì vậy, nó không thực sự Linq để SQL nữa.
Sồi

60

Nếu bạn chỉ muốn cập nhật thuộc tính trên tất cả các yếu tố thì

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

3
Trong EF (Entity Framework): để thay thế một thuộc tính trên tất cả các đối tượng của IEnumerable, câu trả lời được chấp nhận làm việc cho tôi. Mã làm việc cho tôi: var myList = _db.MyObjects.Where (o => o.MyProp == "bar"). AsEnumerable (). Chọn (x => {x.SomeProp = "foo"; return x;}) ;
firepol

32

Tôi thích cái này Nó có thể được kết hợp với các lệnh linq khác.

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

24

Không nên có bất kỳ phép thuật LINQ nào ngăn bạn làm điều này. Đừng sử dụng phép chiếu mặc dù điều đó sẽ trả về một loại ẩn danh.

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

Điều đó sẽ sửa đổi đối tượng thực, cũng như:

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

Tôi thích điều này, câu hỏi của tôi là trong ví dụ đầu tiên nếu không tìm thấy một số u> 10 trong danh sách thì sao? Tôi đã thêm một kiểm tra null và dường như làm việc. Ngoài ra LHS tôi đặt tên u cho v. +1 mặc dù. Rất súc tích.
desaivv

11

Không thể với các toán tử truy vấn tiêu chuẩn - đó là Truy vấn tích hợp ngôn ngữ, không phải là Cập nhật tích hợp ngôn ngữ. Nhưng bạn có thể ẩn cập nhật của mình trong các phương thức mở rộng.

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

Bây giờ bạn có thể sử dụng nó như sau.

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

1
Không phải là bạn muốn cập nhật bộ sưu tập cơ sở. Chúng tôi muốn tài sản bộ sưu tập kết quả được cập nhật.
Michael Brennt

4
Vâng LINQ thay thế SQL mà là viết tắt của Structured Query Language, nhưng nó vẫn cho thấy nhiều khả năng khả năng UPDATE, DELETEINSERT. Vì vậy, tôi sẽ không tranh luận rằng ngữ nghĩa là thứ ngăn chặn chức năng này.
KyleMit

5

Nếu bạn muốn cập nhật các mục bằng một Wheremệnh đề, sử dụng .Where (...) sẽ cắt bớt kết quả của bạn nếu bạn thực hiện:

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

Bạn có thể thực hiện cập nhật cho (các) mục cụ thể trong danh sách như sau:

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

Luôn trả lại hàng ngay cả khi bạn không thực hiện bất kỳ thay đổi nào. Bằng cách này, nó sẽ được giữ trong danh sách.


1

Chúng tôi thường chạy vào đây, nơi chúng tôi muốn bao gồm một giá trị chỉ mục và các chỉ số đầu tiên và cuối cùng trong một danh sách mà không tạo ra một đối tượng mới. Điều này cho phép bạn biết vị trí của mục trong danh sách, bảng liệt kê, v.v. mà không phải sửa đổi lớp hiện có và sau đó biết bạn đang ở mục đầu tiên trong danh sách hay cuối cùng.

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

Bạn chỉ có thể mở rộng bất kỳ lớp nào bằng cách sử dụng:

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

public class Item : IteratorExtender {}

0

Vì tôi đã không tìm thấy câu trả lời ở đây mà tôi cho là giải pháp tốt nhất, theo cách của tôi:

Có thể sử dụng "Chọn" để sửa đổi dữ liệu, nhưng chỉ với một mẹo. Dù sao, "Chọn" không được thực hiện cho điều đó. Nó chỉ thực hiện sửa đổi khi được sử dụng với "ToList", bởi vì Linq không thực hiện trước khi dữ liệu cần thiết. Dù sao, giải pháp tốt nhất là sử dụng "foreach". Trong đoạn mã sau, bạn có thể thấy:

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

Trước khi "foreach", bạn cũng có thể thực hiện lựa chọn linq ...


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

Sẽ nhận được item, và sau đó bạn có thể làmitem.prop1=5; để thay đổi tài sản cụ thể.

Hoặc bạn có muốn nhận danh sách các mục từ db và để nó thay đổi thuộc tính prop1trên mỗi mục trong danh sách được trả về thành giá trị đã chỉ định không? Nếu vậy bạn có thể làm điều này (Tôi đang làm nó trong VB vì tôi biết nó tốt hơn):

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

( listsẽ chứa tất cả các mục được trả lại cùng với các thay đổi của bạn)


Trong khi điều này sẽ hoạt động, tôi nghĩ OP đã hỏi làm thế nào để viết câu lệnh LINQ mà không phải viết một for eachvòng lặp.
fujiiface


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.