2009-08-12 39 views
9

Tôi bắt đầu với this question, mà tôi sắp xếp trả lời there, và bây giờ tôi hỏi câu hỏi cơ bản hơn ở đây. Tôi đã đơn giản hóa các truy vấn xuống này:LINQ "Không thể dịch biểu thức ... thành SQL và không thể xử lý nó dưới dạng biểu thức cục bộ."

var q = from ent in LinqUtils.GetTable<Entity>() 
     from tel in ent.Telephones.DefaultIfEmpty() 
     select new { 
      Name = ent.FormattedName, 
      Tel = tel != null ? tel.FormattedNumber : "" // this is what causes the error 
     }; 

tel.FormattedNumber là một tài sản kết hợp các lĩnh vực NumberExtension thành một chuỗi định dạng gọn gàng. Và đây là những lỗi mà kết quả:

System.InvalidOperationException: Could not translate expression 'Table(Entity).SelectMany(ent => ent.Telephones.DefaultIfEmpty(), (ent, tel) => new <>f__AnonymousType0`2(Name = ent.FormattedName, Tel = IIF((tel != null), tel.FormattedNumber, "")))' into SQL and could not treat it as a local expression. 

Nếu tôi thay đổi tài liệu tham khảo trên từ FormattedNumber để chỉ đơn giản Number, mọi thứ đều hoạt động tốt.

Nhưng tôi muốn số được định dạng hiển thị độc đáo trong danh sách của mình. Bạn đề nghị cách nào gọn gàng nhất, sạch sẽ nhất?

Trả lời

11

Bạn có thể sử dụng AsEnumerable trên thực thể, nhưng điều đó sẽ buộc nó phải mang lại tất cả các cột (ngay cả khi không được sử dụng); có lẽ thay vì một cái gì đó như:

var q1 = from ent in LinqUtils.GetTable<Entity>() 
     from tel in ent.Telephones.DefaultIfEmpty() 
     select new { 
      Name = ent.FormattedName, 
      Number = (tel == null ? null : ent.Number), 
      Extension = (tel == null ? null : ent.Extension) 
     }; 

var q2 = from row in q1.AsEnumerable() 
     select new { 
      row.Name, 
      FormattedNumber = FormatNumber(row.Number, row.Extension) 
     }; 

nơi FormatNumber là một số phương pháp mà phải mất hai và kết hợp chúng, có lẽ tái sử dụng từ khác (tài sản) mã của bạn.

Với LINQ-to-SQL, một tùy chọn khác là hiển thị UDF trên ngữ cảnh dữ liệu có định dạng bên trong cơ sở dữ liệu; một ví dụ hơi khác nhau:

var qry = from cust in ctx.Customers // and tel 
      select new { 
       cust.Name, 
       FormattedNumber = ctx.FormatNumber(tel.Number, tel.Extension) 
      }; 

(mà sẽ làm việc tại các cơ sở dữ liệu; hay không đó là một ý tưởng tốt ;-p)

1

@Marc Gravell đánh tôi đến câu trả lời, tín dụng cũng để những người trả lời khác nhau cho this question, người đã đưa tôi đi đúng hướng.

tôi đã làm nó giống như gợi ý đầu tiên của Marc, như vậy:

var q1 = from ent in LinqUtils.GetTable<Entity>() 
     from tel in ent.Telephones.DefaultIfEmpty() 
     select new { ent, tel }; 
var q2 = from q in q1.AsEnumerable() 
     select new { 
      Name = q.ent.FormattedName, 
      Tel = q.tel != null ? q.tel.FormattedNumber : "" 
     }; 

Và đó đã làm nó! Cảm ơn, tất cả!

3

Cách rõ ràng là nêu rõ các trường bạn thực sự muốn biểu thức, đặt chúng vào đối tượng ở giữa và sau đó sử dụng bất kỳ hàm phụ trợ nào để sửa đổi chúng sau này.

Tôi không chắc chắn nếu bạn nhận ra rằng một lớp đại diện cho bảng SQL cho LINQ là một lớp DTO - nó định nghĩa ngữ pháp được sử dụng bởi trình biên dịch LINQ-SQL. Việc tiêm một thuộc tính vào một DTO không được ánh xạ tới bảng SQL thậm chí không được hỗ trợ - nghĩa là người dịch có thể tự động kích hoạt. Các thuộc tính định nghĩa ngữ pháp và bất cứ điều gì không được định nghĩa bởi chúng không tồn tại cho trình dịch.

Thực thể có tên trong mệnh đề từ không phải là đối tượng - chúng chỉ là biểu tượng được sử dụng để giúp đánh vần các trường bảng thực tế sẽ được tìm nạp. Một trường không được đặt tên rõ ràng trong lựa chọn là một trường không được tìm nạp - ít nhất đó là mục tiêu của người phiên dịch, nó có thể phải để một vài thông qua. Ví dụ, nếu ent.FormattedName đó không được khai báo, đó là một phiếu và có thể phát nổ sau này.

Vì vậy, thuộc tính FormattedNumber được đưa vào lớp DTO thậm chí không tồn tại trong ngữ pháp. Nó không phải là một "trường được tính toán" - thuật ngữ đó là đúng đối với các định nghĩa bảng SQL và nếu bạn có một thuật ngữ trong ngữ pháp của DTO.Lưu ý rằng lỗi nói rất chính xác "biểu thức cục bộ" - phạm vi rất hạn chế.

Bạn có thể cố gắng gian lận nó bằng biểu thức lambda lồng nhau gọi hàm tĩnh trên toàn bộ "tel" có thể tìm nạp toàn bộ bản ghi - hoặc ném một ngoại lệ khác.

LINQ-s khác, không phải là người dịch, có thể có các quy tắc thoải mái. LINQ-SQL phải rất nghiêm ngặt hoặc rất chậm và nó đã đủ chậm :-)

Các vấn đề liên quan