LINQ to Entitiesはメソッド 'System.String ToString()'メソッドを認識せず、このメソッドはストア式に変換できません


126

1つのmysqlサーバーからsqlサーバーにいくつかのものを移行していますが、このコードを機能させる方法がわかりません。

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();
}

2番目に入ると、次の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それでも失敗します。EntityFrameworkが有効なSQLに変換しようとするすべてが式に変換されるためです。処理方法を知っているいくつかのメソッドがありますがToString()、それらの1つではありません。
ジョシュ

7
@ジョシュ:私は失敗することを理解しています。私が求めていたのはそのシナリオの解決策です。なぜなら、上記の解決策は明らかにそこでは適用できないからです。
dotNET 2013年

3
@ジョシュ:私はそのような状況に苦しんでいます。OrderNumber列はintですが、ユーザーは入力時にOrderNumbersのリストをフィルタリングできるようにしたいと考えています。検索ボックスに143と入力した場合、OrderNumber LIKE '%143%'を持つレコードのみが必要です。 。それを達成するためにOrderNumber列でToString()を実行する必要はありませんか?
dotNET 2013年

5
@dotNETこれは、ORMが直面するシナリオの1つです。これらの状況では、ExecuteQueryEntity SQLを介して、または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と同様に、EtityFunctions(EF6はDbFunctionsによって廃止されました)も提供します。これにより、データソースにとらわれないさまざまな関数セットが提供されます(SQLなどに限定されません)。


4
彼らはSqlFunctionsクラスを.NET 4に追加して戻しましたが、私はそれについて学んでいるだけですか?素晴らしい発見。
James Skemp、2015年

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
これはデータベースからすべての Pageエンティティを取得、dbの代わりにクライアント側でフィルタリングを実行することに注意してください。通常は良いことではありません。
ランビネーター2013年

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にキャストしてから、次のメソッドを使用してLINQメソッドを呼び出しますToString()

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

ただし、AsEnumerableまたはToListメソッドを呼び出すときは、このメソッドの前にすべてのエンティティからすべてのデータを要求するので注意してください。上記の私の場合table_name、1つのリクエストですべての行を読み取りました。


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 2016年

0

あなたが本当に入力したい場合はToString、クエリ内で、あなたはへの呼び出しを書き換え式ツリービジター書くことができるToStringとの適切なへの呼び出し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 2016年

@TGarrettここでの唯一の使用法はFirstGetMethods()返される結果です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()句にスローすることはできません。

しかし、最初の行はどのように機能しますか?SQLにはToString()の代わりにCASTand CONVERTがあるので、これまでのところ、エンティティのlinqがいくつかの単純なケースでlinqを使用していると推測します。DateTimeオブジェクトはそれほど単純であるとは限らない...


-8

LINQ to EntityクエリをLINQ to Objectsクエリに変換するだけです(たとえば、ToArrayを呼び出す)、LINQクエリでメソッド呼び出しを使用する必要があるときはいつでも。


3
「メソッド呼び出しを使用する必要があるときはいつでも」というアドバイスは不十分です。多くのレコードがある場合、これは大きな問題になる可能性があります。このシナリオでは、受け入れられた回答の方がはるかに優れています。
PeteGO 2013年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.