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()'メソッドを認識せず、このメソッドはストア式に変換できません。
なぜこれが起こるのか誰でも知っていますか?
文字列を一時変数に保存してから、式で使用します。
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の答え も確認してください。多くの場合、一時変数の必要性を排除できます。
他の人が答えたように、.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に限定されません)。
問題は、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;
同様の問題がありました。エンティティコレクションでToList()を呼び出し、リストを照会することで解決しました。コレクションが小さい場合、これはオプションです。
IQueryable<entity> pages = context.pages.ToList().Where(p=>p.serial == item.Key.ToString())
お役に立てれば。
このように変更すると、動作するはずです:
var key = item.Key.ToString();
IQueryable<entity> pages = from p in context.pages
where p.Serial == key
select p;
LINQクエリが宣言されている行で例外がスローされないのはforeach
の行である理由は、遅延実行機能です。つまり、結果にアクセスしようとするまでLINQクエリは実行されません。そして、これはforeach
で起こり、以前ではありません。
テーブルをEnumerable
にキャストしてから、ToString()
メソッドを使用してLINQメソッドを呼び出します。
var example = contex.table_name.AsEnumerable()
.Select(x => new {Date = x.date.ToString("M/d/yyyy")...)
ただし、AsEnumerable
またはToList
メソッドを呼び出すときは、このメソッドの前にすべてのエンティティからすべてのデータを要求するため、注意してください。上記の私の場合、1回の要求でtable_name
行をすべて読み取りました。
Entity Frameworkバージョン6.2.へのアップグレードはうまくいきました。
以前はバージョン6.0.0でした。
お役に立てれば、
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);
}
}
この場合も同じエラーが発生しました:
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()の代わりにCAST
とCONVERT
があるため、これまでのところ、いくつかの単純なケースでは、エンティティのlinqがそれを使用しています。 DateTimeオブジェクトは、必ずしもそれほど単純であるとは限りません...
クエリ内に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);
}
}
}