LINQ to Entities는 'System.String Format (System.String, System.Object, System.Object)'메서드를 인식하지 않습니다.


88

이 linq 쿼리가 있습니다.

private void GetReceivedInvoiceTasks(User user, List<Task> tasks)
{
    var areaIds = user.Areas.Select(x => x.AreaId).ToArray();

    var taskList = from i in _db.Invoices
                   join a in _db.Areas on i.AreaId equals a.AreaId
                   where i.Status == InvoiceStatuses.Received && areaIds.Contains(a.AreaId)
                   select new Task {
                       LinkText = string.Format(Invoice {0} has been received from {1}, i.InvoiceNumber, i.Organisation.Name),
                       Link = Views.Edit
                   };
}

그래도 문제가 있습니다. 작업을 만들려고합니다. 링크 텍스트를 "Hello"와 같은 상수 문자열로 설정할 때 각각의 새 작업에 대해 괜찮습니다. 그러나 위의 송장 속성을 사용하여 속성 링크 텍스트를 작성하려고합니다.

이 오류가 발생합니다.

base {System.SystemException} = { "LINQ to Entities는 'System.String Format (System.String, System.Object, System.Object)'메서드를 인식하지 못합니다.이 메서드는 저장소 식으로 변환 할 수 없습니다." }

왜 그럴까요? 누구든지이 작업을 수행하는 다른 방법을 알고 있습니까?


예, 원래 그것을 놓쳤습니다
AnonyMouse 2011

답변:


148

Entity Framework는 SQL 측에서 프로젝션을 실행하려고합니다 string.Format. AsEnumerable()Linq to Objects로 해당 부품을 강제로 평가하는 데 사용 합니다.

이전 답변을 바탕 으로 다음 과 같이 쿼리를 재구성합니다.

int statusReceived = (int)InvoiceStatuses.Received;
var areaIds = user.Areas.Select(x=> x.AreaId).ToArray();

var taskList = (from i in _db.Invoices
               where i.Status == statusReceived && areaIds.Contains(i.AreaId)
               select i)
               .AsEnumerable()
               .Select( x => new Task()
               {
                  LinkText = string.Format("Invoice {0} has been received from {1}", x.InvoiceNumber, x.Organisation.Name),
                  Link = Views.Edit
                });

또한 쿼리 ( Organisation.Name) 에서 관련 항목을 사용 하고 있는지 확인 하고 쿼리 에 적절한 항목 Include을 추가했는지 확인 하거나 나중에 사용하기 위해 해당 속성을 구체적으로 구체화합니다.

var taskList = (from i in _db.Invoices
               where i.Status == statusReceived && areaIds.Contains(i.AreaId)
               select new { i.InvoiceNumber, OrganisationName = i.Organisation.Name})
               .AsEnumerable()
               .Select( x => new Task()
               {
                  LinkText = string.Format("Invoice {0} has been received from {1}", x.InvoiceNumber, x.OrganisationName),
                  Link = Views.Edit
                });

select-new-task 부분은 표현식 트리 변환으로 인해 서버 측에서 발생할 수 없다는 사실 외에도 그렇게하는 것이 바람직하지 않다는 점에 유의해야합니다. 아마도 작업이 클라이언트 측에서 생성되기를 원할 것입니다. 따라서 쿼리의 분리와 작업 생성이 훨씬 더 명확해질 수 있습니다.
Tormod 2011

3
또한 필요한 InvoiceNumber 및 Organisation.Name 만있는 익명 유형을 선택하는 것이 좋습니다. 송장 엔터티가 크면 후속 AsEnumerable이있는 select i는 두 개만 사용하더라도 모든 열을 다시 가져옵니다.
Devin

1
@Devin : 예, 동의합니다. 실제로 두 번째 쿼리 예제가 수행하는 작업입니다.
BrokenGlass 2011

15

IQueryable에서 유래 IEnumerable당신은 C #을 말해, 얇은 순간이 주요 닮은 당신이 당신의 쿼리를 할 때 그것은 그것의 언어로 데이터베이스 엔진에 게시되어 있다는 것입니다, 서버 (안 클라이언트 측)의 데이터를 처리하거나 핸들에 SQL에게 데이터.

따라서 기본적으로이라고 말하면 IEnumerable.ToString()C #은 데이터 수집을 가져오고 ToString()개체를 호출 합니다. 그러나 IQueryable.ToString()C #은 SQL에 ToString()개체 를 호출하도록 지시 하지만 SQL에는 이러한 메서드가 없습니다.

단점은 C #에서 데이터를 처리 할 때 C #에서 필터를 적용하기 전에 살펴보고있는 전체 컬렉션을 메모리에 구축해야한다는 것입니다.

이를 수행하는 가장 효율적인 방법 IQueryable은 적용 할 수있는 모든 필터와 마찬가지로 쿼리를 만드는 것입니다 .

그런 다음 메모리에 빌드하고 C #으로 데이터 형식을 지정합니다.

IQueryable<Customer> dataQuery = Customers.Where(c => c.ID < 100 && c.ZIP == 12345 && c.Name == "John Doe");

 var inMemCollection = dataQuery.AsEnumerable().Select(c => new
                                                  {
                                                     c.ID
                                                     c.Name,
                                                     c.ZIP,
                                                     c.DateRegisterred.ToString("dd,MMM,yyyy")
                                                   });

3

SQL은 무엇을해야할지 모르지만 string.Format문자열 연결을 수행 할 수 있습니다.

다음 코드를 실행하면 원하는 데이터를 가져와야합니다.

var taskList = from i in _db.Invoices
               join a in _db.Areas on i.AreaId equals a.AreaId
               where i.Status == InvoiceStatuses.Received && areaIds.Contains(a.AreaId)
               select new Task {
                   LinkText = "Invoice " + i.InvoiceNumber + "has been received from " + i.Organisation.Name),
                   Link = Views.Edit
               };

실제로 쿼리를 수행하면 사용하는 것보다 약간 더 빠를 것입니다 AsEnumerable(적어도 동일한 원래 오류가 발생한 후 내 코드에서 찾은 것입니다). C #으로 더 복잡한 작업을 수행하는 경우에도 여전히 사용해야합니다 AsEnumerable.


2
Linq가 FORMATMESSAGE 함수를 사용하도록 조정될 수없는 이유를 알 수 없습니다. docs.microsoft.com/en-us/sql/t-sql/functions/… 그때까지 당신의 것이 해결책입니다 (구체화를 강요하지 않음)
MemeDeveloper

2
데이터베이스 구조 및 관련 열 수에 따라 대신이 방법을 사용하는 AsEnumerable()것이 훨씬 더 효율적일 수 있습니다. 피 AsEnumerable()ToList()당신이 정말로 메모리에 모든 결과를 가지고 싶어 할 때까지.
Chris Schaller
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.