LINQ to Entities가 'System.String ToString ()'메소드를 인식하지 못하므로이 메소드를 상점 표현식으로 변환 할 수 없습니다.


126

하나의 MySQL 서버에서 SQL Server로 일부 내용을 마이그레이션하고 있지만이 코드를 작동시키는 방법을 알 수 없습니다.

using (var context = new Context())
{
    ...

    foreach (var item in collection)
    {
        IQueryable<entity> pages = from p in context.pages
                                   where  p.Serial == item.Key.ToString()
                                   select p;
        foreach (var page in pages)
        {
            DataManager.AddPageToDocument(page, item.Value);
        }
    }

    Console.WriteLine("Done!");
    Console.Read();
}

두 번째로 들어가면 다음과 foreach (var page in pages)같은 예외가 발생합니다.

LINQ to Entities는 'System.String ToString ()'메소드를 인식하지 못하므로이 메소드를 상점 표현식으로 변환 할 수 없습니다.

왜 이런 일이 일어나는지 아십니까?


답변:


134

문자열을 임시 변수에 저장 한 다음 표현식에서 사용하십시오.

var strItem = item.Key.ToString();

IQueryable<entity> pages = from p in context.pages
                           where  p.Serial == strItem
                           select p;

ToString()실제로 실행되지 않기 때문에 문제가 발생 하고 MethodGroup으로 변환 된 다음 구문 분석되어 SQL로 변환됩니다. ToString()동등한 것이 없기 때문에 표현식이 실패합니다.

노트 :

나중에 추가 된 도우미 클래스 에 대한 Alex의 답변 도 확인하십시오 SqlFunctions. 대부분의 경우 임시 변수가 필요하지 않습니다.


14
ToString ()이 평등의 왼쪽에 적용되면 어떻게됩니까? egpSerial.ToString () = 아이템.
dotNET

3
@dotNet 모든 것이 Entity Framework가 유효한 SQL로 바뀌려고하는 Expression으로 바뀌기 때문에 여전히 실패합니다. 처리 방법을 알고있는 방법이 있지만 ToString()그중 하나가 아닙니다.
Josh

7
@ 조쉬 : 나는 그것이 실패한다는 것을 이해합니다. 위의 솔루션을 분명히 적용 할 수 없기 때문에 내가 요구 한 것은 해당 시나리오의 솔루션입니다.
dotNET

3
@ 조쉬 : 나는 하나의 그런 시나리오로 고심하고 있습니다. 내 OrderNumber 열이 int이지만 사용자가 입력 할 때 OrderNumber 목록을 필터링 할 수 있기를 원합니다. 검색 상자에 143을 입력 한 경우 OrderNumber LIKE '% 143 %'인 레코드 만 원합니다. . 주문 번호 열에서 ToString ()을 수행하지 않아도됩니까?
dotNET

5
@dotNET 이것은 ORM이 직면하는 시나리오 중 하나입니다. 이러한 상황에서 직접 SQL을 통해 ExecuteQuery또는 다음과 함께 Entity SQL을 사용하여 SQL로 드롭해도 괜찮습니다.ObjectQuery<T>
Josh

69

다른 사람들이 대답했듯이 .ToString이 데이터베이스로가는 도중 관련 SQL로 변환하지 못하기 때문에 중단됩니다.

그러나 Microsoft는 이와 같은 상황에서 사용할 수있는 메서드 모음 인 SqlFunctions 클래스 를 제공합니다 .

이 경우 찾고있는 것은 SqlFunctions.StringConvert입니다 .

from p in context.pages
where  p.Serial == SqlFunctions.StringConvert((double)item.Key.Id)
select p;

어떤 이유로 든 임시 변수가있는 솔루션이 바람직하지 않은 경우에 좋습니다.

SqlFunctions와 유사하게 EntityFunctions ( DbFunctions에서 사용되지 않는 EF6 )도 있으며 데이터 소스 와 무관 한 다른 함수 세트 (예 : SQL에 제한되지 않음)를 제공합니다.


4
그들은 .NET 4에서 SqlFunctions 클래스를 다시 추가했는데 그것에 대해 배우고 있습니까? 훌륭한 발견.
James Skemp

24

문제는 LINQ to Entities 쿼리에서 ToString을 호출한다는 것입니다. 이것은 파서가 ToString 호출을 동등한 SQL로 변환하려고 시도한다는 것을 의미합니다 (따라서 예외는 아닙니다).

ToString 호출을 별도의 줄로 옮기기 만하면됩니다.

var keyString = item.Key.ToString();

var pages = from p in context.entities
            where p.Serial == keyString
            select p;

9

비슷한 문제가 있었다. 엔티티 컬렉션에서 ToList ()를 호출하고 목록을 쿼리하여 문제를 해결했습니다. 컬렉션이 작 으면 옵션입니다.

IQueryable<entity> pages = context.pages.ToList().Where(p=>p.serial == item.Key.ToString())

도움이 되었기를 바랍니다.


42
데이터베이스에서 모든 페이지 엔터티를 검색 하고 db 대신 클라이언트 쪽에서 필터링을 수행합니다. 일반적으로 좋지 않습니다.
lambinator

3
이 방법은 하나 이상의 레코드를 포함하는 모든 테이블에 대해 비효율적입니다. 즉, 존재하는 모든 테이블을 의미합니다. 그러나 toString ()을 포함하는 .Select 투영을 수행했기 때문에이 답변이 오늘 도움이되었습니다. 서식 및 내 .Select 문 ...
Nathan Prather

6

다음과 같이 변경하면 작동합니다.

var key = item.Key.ToString();
IQueryable<entity> pages = from p in context.pages
                           where  p.Serial == key
                           select p;

LINQ 쿼리가 선언 된 행에서 예외가 발생하지 않는 이유는 foreach지연된 실행 기능입니다. 즉, 결과에 액세스하려고 할 때까지 LINQ 쿼리가 실행되지 않습니다. 그리고 이것은 foreach이전이 아니라 이전에 발생합니다 .


6

테이블을로 캐스팅 Enumerable한 다음 ToString()내부에서 메소드를 사용하여 LINQ 메소드를 호출하십시오 .

    var example = contex.table_name.AsEnumerable()
.Select(x => new {Date = x.date.ToString("M/d/yyyy")...)

그러나이 메소드 이전에 모든 엔티티에서 모든 데이터를 요청하므로 호출 AsEnumerable또는 ToList메소드 를 호출 할 때주의하십시오 . 위의 경우에는 한 table_name번의 요청으로 모든 행을 읽습니다 .


5
일반적으로 좋은 선택이 아닙니다. .AsEnumerable () 모든 데이터를 메모리에 넣으면 여기에서 더 자세히 볼 수 있습니다. stackoverflow.com/questions/3311244/…
kavain

5

Entity Framework 버전 6.2.0으로 업그레이드하면 효과적 입니다.

이전에 버전 6.0.0에있었습니다.

도움이 되었기를 바랍니다,


1

MVC에서 요구 사항 또는 정보를 기반으로 레코드를 검색한다고 가정합니다. 제대로 작동하고 있습니다.

[HttpPost]
[ActionName("Index")]
public ActionResult SearchRecord(FormCollection formcollection)
{       
    EmployeeContext employeeContext = new EmployeeContext();

    string searchby=formcollection["SearchBy"];
    string value=formcollection["Value"];

    if (formcollection["SearchBy"] == "Gender")
    {
        List<MvcApplication1.Models.Employee> emplist = employeeContext.Employees.Where(x => x.Gender == value).ToList();
        return View("Index", emplist);
    }
    else
    {
        List<MvcApplication1.Models.Employee> emplist = employeeContext.Employees.Where(x => x.Name == value).ToList();
        return View("Index", emplist);
    }         
}

2
더 나은 방법 또는 프로덕션 유형의 코드를 위해서는 항상 작업이 아닌 서비스 계층 또는 데이터 계층에 데이터베이스 이벤트가 있어야합니다.
TGarrett

0

당신이 정말로 입력합니다 ToString쿼리 내에서, 당신은에 전화를 다시 쓰는 식 트리 방문자 쓸 수 ToStringA를 적절한에 호출 StringConvert기능 :

using System.Linq;
using System.Data.Entity.SqlServer;
using System.Linq.Expressions;
using static System.Linq.Expressions.Expression;
using System;

namespace ToStringRewriting {
    class ToStringRewriter : ExpressionVisitor {
        static MethodInfo stringConvertMethodInfo = typeof(SqlFunctions).GetMethods()
                 .Single(x => x.Name == "StringConvert" && x.GetParameters()[0].ParameterType == typeof(decimal?));

        protected override Expression VisitMethodCall(MethodCallExpression node) {
            var method = node.Method;
            if (method.Name=="ToString") {
                if (node.Object.GetType() == typeof(string)) { return node.Object; }
                node = Call(stringConvertMethodInfo, Convert(node.Object, typeof(decimal?));
            }
            return base.VisitMethodCall(node);
        }
    }
    class Person {
        string Name { get; set; }
        long SocialSecurityNumber { get; set; }
    }
    class Program {
        void Main() {
            Expression<Func<Person, Boolean>> expr = x => x.ToString().Length > 1;
            var rewriter = new ToStringRewriter();
            var finalExpression = rewriter.Visit(expr);
            var dcx = new MyDataContext();
            var query = dcx.Persons.Where(finalExpression);

        }
    }
}

First가 아닌 FirstOrDefault를 사용해야합니다. 기본 키인 경우 Find를 사용하면 성능이 향상됩니다.
TGarrett

@TGarrett 여기서 유일한 사용법은 First결과 GetMethods()를 반환하는 것 MethodInfo[]입니다. AFAIK MethodInfo[]에는 Find메소드가 없으며 그러한 확장 메소드도 없습니다. 그러나이 Single방법은 리플렉션을 통해 발견되기 때문에 실제로 사용해야 하며 적절한 방법을 해결할 수 없으면 컴파일 타임 오류가 발생하지 않습니다.
Zev Spitz

0

이 경우 동일한 오류가 발생했습니다.

var result = Db.SystemLog
.Where(log =>
    eventTypeValues.Contains(log.EventType)
    && (
        search.Contains(log.Id.ToString())
        || log.Message.Contains(search)
        || log.PayLoad.Contains(search)
        || log.Timestamp.ToString(CultureInfo.CurrentUICulture).Contains(search)
    )
)
.OrderByDescending(log => log.Id)
.Select(r => r);

디버깅에 너무 많은 시간을 소비 한 후 논리 표현식에 오류가 나타나는 것을 알았습니다.

첫 번째 줄 search.Contains(log.Id.ToString())은 제대로 작동하지만 DateTime 객체를 처리하는 마지막 줄은 비참하게 실패했습니다.

|| log.Timestamp.ToString(CultureInfo.CurrentUICulture).Contains(search)

문제가있는 줄과 문제를 해결하십시오.

이유를 완전히 이해하지 못하지만 ToString ()은 문자열에 대한 LINQ 표현식이지만 엔티티에는 없습니다. LINQ for Entities는 SQL과 같은 데이터베이스 쿼리를 처리하며 SQL에는 ToString ()이라는 개념이 없습니다. 따라서 ToString ()을 .Where () 절에 넣을 수 없습니다.

그러나 첫 번째 줄은 어떻게 작동합니까? ToString () 대신 SQL에는 CASTand CONVERT가 있으므로 엔터티에 대한 linq는 간단한 경우에 이것을 사용한다는 것이 지금까지 가장 좋은 추측입니다. DateTime 객체가 항상 그렇게 간단하지는 않습니다 ...


-8

LINQ 쿼리에서 메서드 호출을 사용해야 할 때마다 LINQ to Entity 쿼리를 LINQ to Objects 쿼리로 변환하십시오 (예 : ToArray 호출).


3
"언제든지 메소드 호출을 사용해야합니다"는 좋지 않은 조언입니다. 많은 레코드가 있으면 큰 문제가 될 수 있습니다. 이 시나리오에서는 허용되는 답변이 훨씬 좋습니다.
PeteGO
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.