Tạo một Tuple trong Linq Select


87

Tôi đang làm việc với C # và .NET Framework 4.5.1 truy xuất dữ liệu từ cơ sở dữ liệu SQL Server với Entity Framework 6.1.3.

Tôi có cái này:

codes = codesRepo.SearchFor(predicate)
      .Select(c => new Tuple<string, byte>(c.Id, c.Flag))
      .ToList();

Và khi tôi chạy nó, tôi nhận được thông báo này:

Chỉ các bộ khởi tạo và bộ khởi tạo không tham số mới được hỗ trợ trong LINQ to Entities.

Tôi không biết mình phải tạo Tuple như thế nào vì tất cả các ví dụ mà tôi đã tìm thấy hầu hết đều giống như ví dụ này.

Tôi đã thử điều này:

codes = codesRepo.SearchFor(predicate)
      .Select(c => Tuple.Create(c.Id, c.Flag))
      .ToList();

Và gặp lỗi này:

LINQ to Entities không nhận dạng được phương thức 'System.Tuple`2 [System.String, System.Byte] Create [String, Byte] (System.String, Byte)' và phương thức này không thể được dịch thành một biểu thức lưu trữ.

Vấn đề ở đâu?


Có vẻ như bạn sẽ cần tạo một đối tượng được gõ mạnh. Nhưng có, câu hỏi hay; ủng hộ.
frenchie

Câu trả lời:


119

Mặc dù câu trả lời của octavioccl hoạt động, nhưng tốt hơn hết bạn nên chiếu kết quả truy vấn thành kiểu ẩn danh, sau đó chuyển sang kiểu có thể liệt kê và chuyển nó thành tuple. Bằng cách này, truy vấn của bạn sẽ chỉ lấy các trường cần thiết từ cơ sở dữ liệu.

codes = codesRepo.SearchFor(predicate)
    .Select(c => new { c.Id, c.Flag })
    .AsEnumerable()
    .Select(c => new Tuple<string, byte>(c.Id, c.Flag))
    .ToList();

Lưu ý: Quy tắc trên áp dụng cho EF6. EF Core tự nhiên hỗ trợ các bộ giá trị (trong phép chiếu hoặc dưới dạng khóa tham gia / nhóm) thông qua phương thức khởi tạo tuple, ví dụ: truy vấn ban đầu chỉ hoạt động

codes = codesRepo.SearchFor(predicate)
  .Select(c => new Tuple<string, byte>(c.Id, c.Flag))
  .ToList();

nhưng không phải là Tuple.Createphương pháp (EF Core 2.x).


Giải pháp rất tốt - Cảm ơn! ... Còn nếu tôi phải mở rộng giải pháp này bằng một giá trị nullable khác thì sao? .Select(c => new { c.Id, c.Flag, c.Foo?.Code })không hoạt động.
skyfrog

2
@skyfrog Toán tử ?.không được hỗ trợ trong cây biểu thức. Nhưng khác hơn thế, bạn có thể mở rộng loại vô danh với càng nhiều giá trị bạn muốn - chỉ cần don; t quên tên họ khi cần thiết :) ví dục => new { c.Id, c.Flag, Code = (int?)c.Foo.Code }
Ivan Stoev

1
Tuyệt quá! Rất cám ơn @Ivan đã trả lời. Tôi đa đên rât gân! ... nhưng nó luôn luôn dễ dàng để nói trong khi nhìn lại ;-)
skyfrog

Great câu trả lời, có thể được sử dụng với EF-Đối tượng .. ví dụ dbCtx.MyEntity.Where () Chọn (.. để anon đối tượng ...) vv .....
joedotnot

45

Chỉ là một câu trả lời được cập nhật cho C # 7, bây giờ bạn có thể sử dụng một cú pháp đơn giản hơn để tạo ValueTuples.

codes = codesRepo.SearchFor(predicate)
.Select(c => new { c.Id, c.Flag })
.AsEnumerable()
.Select(c => (c.Id, c.Flag))
.ToList();

Bạn thậm chí có thể đặt tên cho các thuộc tính của tuple ngay bây giờ:

codes = codesRepo.SearchFor(predicate)
.Select(c => new { c.Id, c.Flag }) // anonymous type
.AsEnumerable()
.Select(c => (Id: c.Id, Flag: c.Flag)) // ValueTuple
.ToList();

Vì vậy, thay vì sử dụng nó như Item1 hoặc Item2, bạn có thể truy cập nó dưới dạng Id hoặc Flag.

Các tài liệu khác về chọn-giữa-ẩn-danh-và-tuple


11

Thử đi:

codes = codesRepo.SearchFor(predicate)
  .Select(c => Tuple.Create(c.Id, c.Flag))
  .ToList();

Đã được thông báo rằng điều này không chấp nhận trong LINQ cho các thực thể.

Một tùy chọn khác là kéo kết quả vào bộ nhớ trước khi chọn. Nếu bạn định làm điều này, tôi khuyên bạn nên thực hiện tất cả việc lọc trước .AsEnumerable () vì nó có nghĩa là bạn chỉ lấy lại kết quả mà bạn muốn thay vì kéo lại toàn bộ bảng rồi lọc.

codes = codesRepo.SearchFor(predicate).AsEnumerable()
  .Select(c => Tuple.Create(c.Id, c.Flag))
  .ToList();

cũng như Tuple.Create (c.Id, c.Flag) có thể được thay đổi thành Tuple mới (c.Id, c.Flag) nếu bạn muốn làm cho mã rõ ràng hơn một chút trong các loại tuples


Xin lỗi, nó không hoạt động. Tôi đã cập nhật câu hỏi của mình với nhiều chi tiết hơn.
VansFannel

11

Trong linq tới các thực thể, bạn có thể chiếu lên kiểu ẩn danh hoặc lên DTO. Để tránh vấn đề đó, bạn có thể sử dụng AsEnumerablephương pháp mở rộng:

codes = codesRepo.SearchFor(predicate).AsEnumerable().
      .Select(c => new Tuple<string, byte>(c.Id, c.Flag))
      .ToList();

Phương pháp này cho phép bạn làm việc với LINQ to Object thay vì LINQ to Entities , vì vậy sau khi gọi nó, bạn có thể chiếu kết quả của truy vấn của bạn trong bất cứ điều gì bạn need.The lợi thế của việc sử dụng AsEnumerablethay vì ToListđược rằng AsEnumerablekhông thực hiện truy vấn, nó bảo hoãn thực hiện. Bạn nên lọc dữ liệu của mình trước khi gọi một trong các phương pháp này.


5

Tôi đã tìm ra câu trả lời:

codes = codesRepo.SearchFor(predicate)
      .ToList()
      .Select(c => Tuple.Create(c.Id, c.Flag))
      .ToList();

Không, điều này sẽ tạo ra CHỌN *
Mihai Bratulescu

1

Sử dụng phương pháp này để làm điều này và sử dụng async.

var codes = await codesRepo.SearchFor(predicate)
                    .Select(s => new
                    {
                        Id = s.Id,
                        Flag = s.Flag
                    }).FirstOrDefaultAsync();

                var return_Value = new Tuple<string, byte>(codes.Id, codes.Flag);

0

Chỉ hai xu của tôi: điều này đã làm tôi ngạc nhiên một vài lần với các tên loại:

Một vài ví dụ điển hình:

    private Tuple<string, byte> v1()
    {
        return new Tuple<string, byte>("", 1);
    }

    private (string, int) v2()
    {
        return ("", 1);
    }

    private (string Id, byte Flag) v3()
    {
        return ("", 1);
    }

Trân trọng.


Cú pháp bạn đã đăng không hoạt động. Những gì bạn có thể muốn viết là public (string Id, byte Flag) SearchFor(Expression predicate), nhưng điều này là bên cạnh vấn đề. Hai xu không nên là một câu trả lời, mà là một bình luận.
M.Stramm

2
Tôi đã cập nhật câu trả lời của mình - tôi nên kiểm tra nó trước khi đăng. Tôi không đồng ý; tất cả thông tin đều hữu ích cho tất cả những khách truy cập vào trang này bất kể nó được đăng như thế nào. Nhận xét không truyền đạt ý định cũng như trả lời nhờ câu trả lời.
IbrarMumtaz

Tôi đồng ý rằng nội dung được thêm vào là tốt và các nhận xét không phù hợp với các ví dụ về mã. Cảm ơn bạn đã chỉnh sửa, bây giờ rõ ràng đây không phải là câu trả lời cho câu hỏi của OP (nhưng có thể giúp giải quyết các vấn đề liên quan đến tuple).
M.Stramm
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.