web-dev-qa-db-ja.com

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

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()'メソッドを認識せず、このメソッドはストア式に変換できません。

なぜこれが起こるのか誰でも知っていますか?

125
Erre Efe

文字列を一時変数に保存してから、式で使用します。

var strItem = item.Key.ToString();

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

ToString()は実際には実行されず、 MethodGroup に変換され、解析されてSQLに変換されるため、問題が発生します。同等のToString()がないため、式は失敗します。

注意:

また、後で追加されたSqlFunctionsヘルパークラスに関する Alexの答え も確認してください。多くの場合、一時変数の必要性を排除できます。

133
Josh

他の人が答えたように、.ToStringがデータベースへの途中で関連するSQLに変換できないため、これは壊れます。

ただし、Microsoftは SqlFunctions class を提供しています。これは、このような状況で使用できるメソッドのコレクションです。

この場合、ここで探しているのは SqlFunctions.StringConvert です。

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

一時変数を使用したソリューションが何らかの理由で望ましくない場合に適しています。

SqlFunctionsと同様に、 EntityFunctions (EF6は DbFunctions に置き換えられています)もあり、データソースに依存しないさまざまな関数セットを提供します(たとえば、SQLに限定されません)。

65
Alex

問題は、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;
23
Justin Niessner

同様の問題がありました。エンティティコレクションでToList()を呼び出し、リストを照会することで解決しました。コレクションが小さい場合、これはオプションです。

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

お役に立てれば。

10
cynicaldoctor

このように変更すると、動作するはずです:

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

LINQクエリが宣言されている行で例外がスローされないのはforeachの行である理由は、遅延実行機能です。つまり、結果にアクセスしようとするまでLINQクエリは実行されません。そして、これはforeachで起こり、以前ではありません。

6
Daniel Hilgarth

テーブルをEnumerableにキャストしてから、ToString()メソッドを使用してLINQメソッドを呼び出します。

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

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

3
neustart47

Entity Frameworkバージョン6.2.へのアップグレードはうまくいきました。

以前はバージョン6.0.0でした。

お役に立てれば、

2
93Ramadan

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);
    }         
}
1
shakti

この場合も同じエラーが発生しました:

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()の代わりにCASTCONVERTがあるため、これまでのところ、いくつかの単純なケースでは、エンティティのlinqがそれを使用しています。 DateTimeオブジェクトは、必ずしもそれほど単純であるとは限りません...

0
pekaaw

クエリ内に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);

        }
    }
}
0
Zev Spitz