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


132

Tôi có lỗi này trong biểu thức linq này:

var naleznosci = (from nalTmp in db.Naleznosci
                              where nalTmp.idDziecko == idDziec
                              select new Payments
                              (
                                  nalTmp.Dziecko.Imie,
                                  nalTmp.Dziecko.Nazwisko,
                                  nalTmp.Miesiace.Nazwa,
                                  nalTmp.Kwota,
                                  nalTmp.RodzajeOplat.NazwaRodzajuOplaty,
                                  nalTmp.RodzajeOplat.TypyOplat.NazwaTypuOplaty,
                                  nalTmp.DataRozliczenia,
                                  nalTmp.TerminPlatnosci
                              )).ToList();

Bất kỳ ý tưởng làm thế nào để giải quyết vấn đề này? Tôi thử với bất kỳ kết hợp biểu thức nào ...: /


1
bạn có thể hiển thị lớp Thanh toán không? hoặc ít nhất là ctor được gọi ở đây, và cụ thể là liệu cuộc gọi ctor 8-param đó có thể được hoán đổi một cách an toàn cho một cuộc gọi ctor 0-param và đặt 8 thuộc tính trên đối tượng không?
James Manning

23
Tôi đã gặp lỗi tương tự khi sử dụng Struct thay vì Class cho đối tượng mà tôi đã "mới".
HuckIt

3
Điều TL; DR là EF-LINQ đang cố gắng gửi câu lệnh chọn đến nhà cung cấp EF, tức là. chuyển đổi nó thành SQL. Để thoát khỏi EF-LINQ, hãy gọi ToList () trước khi tạo đối tượng.

Câu trả lời:


127

không có thêm thông tin về 'Thanh toán', điều này không giúp được gì nhiều, nhưng giả sử bạn muốn tạo một đối tượng Thanh toán và đặt một số thuộc tính của nó dựa trên các giá trị cột:

var naleznosci = (from nalTmp in db.Naleznosci
                              where nalTmp.idDziecko == idDziec
                              select new Payments
                              {
                                  Imie = nalTmp.Dziecko.Imie,
                                  Nazwisko = nalTmp.Dziecko.Nazwisko,
                                  Nazwa= nalTmp.Miesiace.Nazwa,
                                  Kwota = nalTmp.Kwota,
                                  NazwaRodzajuOplaty = nalTmp.RodzajeOplat.NazwaRodzajuOplaty,
                                  NazwaTypuOplaty = nalTmp.RodzajeOplat.TypyOplat.NazwaTypuOplaty,
                                  DataRozliczenia = nalTmp.DataRozliczenia,
                                  TerminPlatnosci = nalTmp.TerminPlatnosci,
                              }).ToList();

10
Điều này hoạt động rất tốt, đừng quên thêm một hàm tạo trống cho lớp.
sống tình yêu

58
Chỉ cần thêm vào câu trả lời này, bạn không thể làm điều này với Structs, chỉ các Lớp học - tôi đã mất một chút để tìm ra điều đó!
naspinski

4
Vâng, tôi nghĩ rằng câu trả lời của Tony tốt hơn câu trả lời này bởi vì nó thực sự giải quyết được vấn đề tức thời, trong khi điều này giải quyết vấn đề bằng cách thay đổi bản chất của lớp Thanh toán và có thể ngăn không cho nó trở nên bất biến.
Stephen Holt

Điều này có vẻ xấu af. Còn cách nào tốt hơn với EF6?
Bộ công cụ

115

Nếu bạn vẫn muốn sử dụng hàm tạo của mình để khởi tạo và không thuộc tính (đôi khi hành vi này được mong muốn cho mục đích khởi tạo), hãy liệt kê truy vấn bằng cách gọi ToList()hoặc ToArray()sau đó sử dụng Select(…). Do đó, nó sẽ sử dụng LINQ để Bộ sưu tập và giới hạn của việc không thể gọi hàm tạo với các tham số Select(…)sẽ biến mất.

Vì vậy, mã của bạn sẽ trông giống như thế này:

var naleznosci = db.Naleznosci
                          .Where(nalTmp => nalTmp.idDziecko == idDziec)
                          .ToList() // Here comes transfer to LINQ to Collections.
                          .Select(nalImp => new Payments
                              (
                                  nalTmp.Dziecko.Imie,
                                  nalTmp.Dziecko.Nazwisko,
                                  nalTmp.Miesiace.Nazwa,
                                  nalTmp.Kwota,
                                  nalTmp.RodzajeOplat.NazwaRodzajuOplaty,
                                  nalTmp.RodzajeOplat.TypyOplat.NazwaTypuOplaty,
                                  nalTmp.DataRozliczenia,
                                  nalTmp.TerminPlatnosci
                              ))
                          .ToList();

21
Chỉ cần làm rõ lý do tại sao điều này hoạt động, vấn đề với mã được nêu ban đầu là Entity Framework cố gắng truyền lệnh gọi của hàm tạo đến SQL cùng với phần còn lại của truy vấn LINQ và dĩ nhiên không có cách nào để SQL tiến hành xây dựng đối tượng phức tạp! Bằng cách chèn lệnh gọi ToList (), bạn di chuyển vô số từ truy vấn SQL chưa được thực thi sang danh sách cụ thể các đối tượng trong bộ nhớ, sau đó bạn có thể thao tác theo bất kỳ cách nào bạn muốn.
Stephen Holt

19
Đừng dùng ToX()cái này, dùng đi AsEnumerable().
Rawling

1
.ToList () // Ở đây chuyển đến LINQ sang Bộ sưu tập. là dòng giải quyết vấn đề cho tôi.
Ram

15
Xin lưu ý rằng điều này sẽ chọn tất cả các cột ở cấp db trong khi thông thường nó sẽ chỉ chọn các cột bắt buộc
Hugh Jeffner

4
Không chỉ vậy mà có lẽ bạn sẽ có nhiều bảng liệt kê. Tôi không thích giải pháp này.
Bluebaron

47

Bản thân tôi đã gặp phải lỗi này, tôi nghĩ rằng tôi sẽ thêm rằng nếu Paymentloại là a struct, bạn cũng sẽ gặp lỗi tương tự vì structcác loại không hỗ trợ các hàm tạo không tham số.

Trong trường hợp đó, chuyển đổi Paymentsang một lớp và sử dụng cú pháp khởi tạo đối tượng sẽ giải quyết vấn đề.


Điều này giải quyết vấn đề hình thành tôi. Trên thực tế, truy vấn này với bộ chọn cấu trúc được hỗ trợ trong LINQ-2-SQL và nó là một vấn đề khi bạn nâng cấp lên EntityFramework.
Tomas Kubes

Tôi ghét cấu trúc. Họ không bao giờ kết thúc việc làm những gì tôi muốn
Simon_Weaver

Đã tạo một DateTime(đó là một cấu trúc) trong Truy vấn của tôi, dẫn đến cùng một Lỗi. trích xuất nó vào một biến cục bộ đã sửa nó cho tôi. Cảm ơn các gợi ý cấu trúc.
LuckyLikey

20

Nếu bạn giống tôi và không muốn phải điền các thuộc tính của mình cho mỗi truy vấn bạn đang xây dựng, có một cách khác để giải quyết vấn đề này.

var query = from orderDetail in context.OrderDetails
            join order in context.Orders on order.OrderId equals orderDetail.orderId
            select new { order, orderDetail };

Tại thời điểm này, bạn có một IQueryable chứa một đối tượng ẩn danh. Nếu bạn muốn điền đối tượng tùy chỉnh của mình với hàm tạo, bạn có thể chỉ cần làm một cái gì đó như thế này:

return query.ToList().Select(r => new OrderDetails(r.order, r.orderDetail));

Bây giờ đối tượng tùy chỉnh của bạn (lấy hai đối tượng làm tham số) có thể điền các thuộc tính của bạn khi cần.


Điều này làm việc cho tôi và trở thành giải pháp sạch nhất. Những người đã đề nghị loại bỏ hàm tạo và sử dụng cú pháp khởi tạo phải không có logic trong hàm tạo. Đó là lần duy nhất tôi dựa vào các nhà xây dựng để điền các thuộc tính cho một đối tượng. Cảm ơn bạn đã chia sẻ.
Bonez024

9

Đầu tiên tôi sẽ tránh giải pháp với

from ....
select new Payments
{
  Imie = nalTmp.Dziecko.Imie,
  ....
}

Điều này yêu cầu một nhà xây dựng trống và bỏ qua việc đóng gói để bạn nói rằng Thanh toán mới () là một khoản thanh toán hợp lệ không có bất kỳ dữ liệu nào, nhưng thay vào đó, đối tượng phải có ít nhất một giá trị và có thể là các trường bắt buộc khác tùy thuộc vào miền của bạn.

Tốt hơn là có một hàm tạo cho các trường bắt buộc nhưng chỉ mang lại dữ liệu cần thiết:

from ....
select new
{
  Imie = nalTmp.Dziecko.Imie,
  Nazwisko = nalTmp.Dziecko.Nazwisko
  ....
}
.ToList() // Here comes transfer to LINQ to Collections.
.Select(nalImp => new Payments
 (
  nalTmp.Imie,//assume this is a required field
  ...........
  )
  {
     Nazwisko = nalTmp.Nazwisko //optional field
  })
.ToList();

Đây là cái ác ít hơn.
Phấn

Tôi cũng thích một cái gì đó như thế này. Tôi đã thử buộc sử dụng Tuple nhưng Tuple không có tham số ít hàm tạo. Tôi đã điền một đối tượng ẩn danh và sau đó chọn Tuple.
Tchaps

một trong những để nắm lấy đóng gói và tên miền
inrandomwetrust

2

Bạn có thể thử làm tương tự, nhưng sử dụng các phương thức mở rộng. Các nhà cung cấp của cơ sở dữ liệu sử dụng là gì?

var naleznosci = db.Naleznosci
                          .Where<TSource>(nalTmp => nalTmp.idDziecko == idDziec)
                          .Select<TSource, TResult>(
                             delegate(TSource nalTmp) { return new Payments
                             (
                                 nalTmp.Dziecko.Imie,
                                 nalTmp.Dziecko.Nazwisko,
                                 nalTmp.Miesiace.Nazwa,
                                 nalTmp.Kwota,
                                 nalTmp.RodzajeOplat.NazwaRodzajuOplaty,
                                 nalTmp.RodzajeOplat.TypyOplat.NazwaTypuOplaty,
                                 nalTmp.DataRozliczenia,
                                 nalTmp.TerminPlatnosci
                             ); })
                          .ToList();

2

Chỉ cần ToList()các DbSettrước khi Selecttuyên bố .. thực tế DbSetđược lưu như một truy vấn, nó không hoàn thành được nêu ra. Sau khi gọi ToList()bạn đang chơi với các đối tượng và sau đó bạn có thể sử dụng hàm tạo không mặc định trong truy vấn.

Không phải là cách hiệu quả nhất về thời gian sử dụng, nhưng đó là một lựa chọn trên các bộ nhỏ.


1

yeh, hãy thử nó như thế này ....

var naleznosci = (from nalTmp in db.Naleznosci
                              where nalTmp.idDziecko == idDziec
                              select new Payments()
                              {
                                  Dziecko.Imie,
                                  Dziecko.Nazwisko,
                                  Miesiace.Nazwa,
                                  Kwota,
                                  RodzajeOplat.NazwaRodzajuOplaty,
                                  RodzajeOplat.TypyOplat.NazwaTypuOplaty,
                                  DataRozliczenia,
                                  TerminPlatnosci
                              }).ToList();

điều này sẽ làm mới đối tượng Thanh toán của bạn bằng cách sử dụng hàm tạo không tham số, sau đó khởi tạo các thuộc tính được liệt kê bên trong dấu ngoặc nhọn { }


3
FYI ()không cần thiết trong Payemnts, vì vậy có thể là `chọn Thanh toán mới {// giá trị init}
Postman

bây giờ tôi có lỗi: Không thể khởi tạo loại 'Thanh toán' bằng trình khởi tạo bộ sưu tập vì nó không triển khai 'System.Collections.IEnumerable'
netmajor

đúng - nếu bạn đang tạo một loại anon (thay vì một thể hiện của lớp Thanh toán), mã của Muad sẽ ổn vì các thuộc tính cần đặt sẽ là tên của các thuộc tính được đọc từ đó. Tuy nhiên, vì đây là lớp 'thực', bạn sẽ cần chỉ định các thuộc tính nào được đặt thành các giá trị khác nhau.
James Manning

1

Ngoài các phương pháp đã nói ở trên, bạn cũng có thể phân tích nó như một bộ sưu tập Vô số, như vậy:

(from x in table
....
).AsEnumerable()
.Select(x => ...)

Điều này cũng có thêm lợi ích là làm cho cuộc sống dễ dàng hơn khi xây dựng một đối tượng ẩn danh, như thế này:

 (from x in tableName
select x.obj)
.Where(x => x.id != null)
.AsEnumerable()
.Select(x => new {
   objectOne = new ObjectName(x.property1, x.property2),
   parentObj = x
})
.ToList();

Tuy nhiên, việc ghi nhớ rằng phân tích cú pháp một bộ sưu tập là Vô số kéo nó vào bộ nhớ, vì vậy nó có thể tốn nhiều tài nguyên! Cần thận trọng khi sử dụng ở đây.


1

Ngoài ra, nếu bạn muốn sử dụng một hàm tạo có nhiều đối tượng để khởi tạo, bạn có thể gặp lỗi nếu Linq không trả về giá trị nào.

Vì vậy, bạn có thể muốn làm một cái gì đó như thế này:

(from x in table_1
   join y in table_2
   on x.id equals y.id
   select new {
   val1 = x,
   val2 = y
})
.DefaultIfEmpty()
.ToList()
.Select(a => new Val_Constructor(a.val1 != null ? a.val1 : new Val_1_Constructor(),
                            a.val2 != null ? a.val2 : new Val_2_Constructor()))
.ToList();

1

Xin lỗi vì đến bữa tiệc muộn, nhưng tôi sau khi tìm thấy điều này , tôi nghĩ rằng điều này nên được chia sẻ vì đây là cách thực hiện tiết kiệm bộ nhớ, nhanh nhất và tiết kiệm bộ nhớ nhất mà tôi có thể tìm thấy.

Thích nghi với ví dụ của bạn, bạn sẽ viết:

public static IQueryable<Payments> ToPayments(this IQueryable<Naleznosci> source)
{
  Expression<Func<Naleznosci, Payments>> createPayments = naleznosci => new Payments
  {
    Imie = source.Dziecko.Imie,
    Nazwisko = source.Dziecko.Nazwisko,
    Nazwa= source.Miesiace.Nazwa,
    Kwota = source.Kwota,
    NazwaRodzajuOplaty = source.RodzajeOplat.NazwaRodzajuOplaty,
    NazwaTypuOplaty = source.RodzajeOplat.TypyOplat.NazwaTypuOplaty,
    DataRozliczenia = source.DataRozliczenia,
    TerminPlatnosci = source.TerminPlatnosci,
  };

  return source.Select(createPayments);
}

Những lợi thế lớn ở đây (như Damien Guard đã chỉ ra trong các bình luận tại đường dẫn) là:

  • An toàn cho bạn khỏi việc sử dụng mô hình khởi tạo trên mỗi lần xuất hiện.
  • Có thể sử dụng thông qua var foo = createPayments(bar);cũng như sử dụng thông qua myIQueryable.ToPayments ().

1

Tôi đã có cùng một vấn đề ngày hôm nay và giải pháp của tôi tương tự như những gì Yoda liệt kê, tuy nhiên nó chỉ hoạt động với cú pháp trôi chảy.

Điều chỉnh giải pháp của tôi theo mã của bạn: Tôi đã thêm phương thức tĩnh sau vào lớp đối tượng

    /// <summary>
    /// use this instead of a parameritized constructor when you need support
    /// for LINQ to entities (fluent syntax only)
    /// </summary>
    /// <returns></returns>
    public static Func<Naleznosci, Payments> Initializer()
    {
        return n => new Payments
        {
             Imie = n.Dziecko.Imie,
             Nazwisko = n.Dziecko.Nazwisko,
             Nazwa = n.Miesiace.Nazwa,
             Kwota = n.Kwota,
             NazwaRodzajuOplaty = n.RodzajeOplat.NazwaRodzajuOplaty,
             NazwaTypuOplaty = n.RodzajeOplat.TypyOplat.NazwaTypuOplaty,
             DataRozliczenia = n.DataRozliczenia,
             TerminPlatnosc = n.TerminPlatnosci
        };
    }

và sau đó cập nhật truy vấn cơ bản thành như sau:

var naleznosci = (from nalTmp in db.Naleznosci
    where nalTmp.idDziecko == idDziec
    select new Payments.Initializer());

Điều này tương đương về mặt logic với giải pháp của James Manning với lợi thế là đẩy sự khởi đầu của thành viên vào Đối tượng truyền dữ liệu / lớp

Lưu ý: Ban đầu tôi đang sử dụng các tên mô tả nhiều hơn là "Công cụ khởi tạo" nhưng sau khi xem lại cách tôi đang sử dụng, tôi thấy rằng "Công cụ khởi tạo" là đủ (ít nhất là cho mục đích của tôi).

Lưu ý cuối cùng:
Sau khi đưa ra giải pháp này, ban đầu tôi nghĩ sẽ rất đơn giản để chia sẻ cùng một mã và điều chỉnh nó để làm việc với cú pháp Truy vấn. Tôi không còn tin rằng đó là trường hợp. Tôi nghĩ rằng nếu bạn muốn có thể sử dụng kiểu xây dựng tốc ký này, bạn sẽ cần một phương thức cho mỗi (truy vấn, lưu loát) trôi chảy như được mô tả ở trên có thể tồn tại trong chính lớp đối tượng.

Đối với cú pháp truy vấn, một phương thức mở rộng (hoặc một số phương thức bên ngoài lớp cơ sở đang được sử dụng) sẽ được yêu cầu. (vì cú pháp truy vấn muốn vận hành IQueryable chứ không phải T)

Đây là một ví dụ về những gì tôi đã sử dụng để cuối cùng làm việc này cho cú pháp truy vấn. (Yoda đã đóng đinh cái này nhưng tôi nghĩ việc sử dụng có thể rõ ràng hơn vì lúc đầu tôi không hiểu

/// <summary>
/// use this instead of a parameritized constructor when you need support
/// for LINQ to entities (query syntax only)
/// </summary>
/// <returns></returns>
public static IQueryable<Payments> Initializer(this IQueryable<Naleznosci> source)
{
    return source.Select(
        n => new Payments
        {
            Imie = n.Dziecko.Imie,
            Nazwisko = n.Dziecko.Nazwisko,
            Nazwa = n.Miesiace.Nazwa,
            Kwota = n.Kwota,
            NazwaRodzajuOplaty = n.RodzajeOplat.NazwaRodzajuOplaty,
            NazwaTypuOplaty = n.RodzajeOplat.TypyOplat.NazwaTypuOplaty,
            DataRozliczenia = n.DataRozliczenia,
            TerminPlatnosc = n.TerminPlatnosci
    };
}

và cách sử dụng

var naleznosci = (from nalTmp in db.Naleznosci
    where nalTmp.idDziecko == idDziec
    select nalTmp).Initializer().ToList();

đã thêm một phần liên quan đến cú pháp truy vấn cho đầy đủ khi tôi nhận ra câu trả lời ban đầu của mình không mở rộng tốt. Anwer của @ yoda có lẽ tốt hơn về cú pháp truy vấn.
wode

0

Mặc dù đã muộn để trả lời, nó vẫn có thể giúp ai đó gặp nạn. Vì LINQ cho các thực thể không hỗ trợ các cấu trúc đối tượng không tham số. Tuy nhiên, các phương thức chiếu cho IEnumerable .

Vì vậy, trước khi lựa chọn, chỉ cần chuyển đổi IQueryable của bạn sang IEnumerable bằng cách sử dụng mã này:

var result = myContext.SomeModelClass.AsEnumerable().Select(m => m.ToString());

Nó sẽ hoạt động tốt. Tuy nhiên, tất nhiên, nó sẽ mất đi lợi ích của các truy vấn gốc.


0
IQueryable<SqlResult> naleznosci = (from nalTmp in db.Naleznosci
                              where nalTmp.idDziecko == idDziec
                              select new Payments
                              {
                                  Imie = nalTmp.Dziecko.Imie,
                                  Nazwisko = nalTmp.Dziecko.Nazwisko,
                                  Nazwa= nalTmp.Miesiace.Nazwa,
                                  Kwota = nalTmp.Kwota,
                                  NazwaRodzajuOplaty =                          nalTmp.RodzajeOplat.NazwaRodzajuOplaty,
                              NazwaTypuOplaty = nalTmp.RodzajeOplat.TypyOplat.NazwaTypuOplaty,
                              DataRozliczenia = nalTmp.DataRozliczenia,
                              TerminPlatnosci = nalTmp.TerminPlatnosci,
                          });
Repeater1.DataSource  = naleznosci.ToList(); 
Repeater1.DataBind();


public class SqlResult
{
        public string Imie { get; set; }
        public string Nazwisko { get; set; }
        ...
}
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.