web-dev-qa-db-ja.com

Entity Framework 4 / Linq:クエリでDateTimeから文字列に変換する方法は?

私は次のクエリを持っています:

from a in Products
select new ProductVM
    {
         id = a.id,
         modified = a.modified.ToString()
    }

これは私に次のエラーを与えます:

LINQ to Entities does not recognize the method 'System.String ToString()'
method, and this method cannot be translated into a store expression.

ProductsテーブルのmodifiedはDateTimeです。 ProductVMクラスのmodifiedは文字列です。

何か案は?これは些細な問題でなければなりません。

23
Zachary Scott

ToString()はLinqto Entitiesではサポートされていません SqlFunctions の一部として関数ヘルパーのリストがあります=ただし、これは日付から文字列への変換をサポートしていません。

最も簡単なのは、最初にクエリ内で匿名型に投影し、次に AsEnumerable() を使用してIEnumerableにキャストすることです。その後、ToString()クエリ式の残りの部分でLinqto Objectsを使用しているためです(このトピックに関する長い記事があります ここ )。

   var results = Products.Select( p => new { a.id, a.modified })
                         .AsEnumerable()
                         .Select(p => new ProductVM() 
                                { id = p.id, 
                                  modified = p.modified.ToString() 
                                });
22
BrokenGlass

別の方法は次のとおりです。

_.Select( p -> SqlFunctions.StringConvert((double)
                  SqlFunctions.DatePart("m", p.modified)).Trim() + "/" +
              // SqlFunctions.DateName("mm", p.modified) + "/" + MS ERROR?
              SqlFunctions.DateName("dd", p.modified) + "/" +
              SqlFunctions.DateName("yyyy", p.modified)
_

どうやらDateName("MM", ..)は月の名前を綴っていて、DatePart("mm", ..)は数値、つまりStringConvert( )を提供しますが、これは結果をスペースで埋めるので、.Trim()

46
Zachary Scott

この構造で新しいPOCOを作成します(データ型はDateTimeであると想定しています)。

public class UserProductVM {
    ...
    private DateTime _modified;

    public DateTime SetModified { set { _dateEvent = value; } }
    public string Modified { get { return _modified.ToString("dd MMM yyyy @ HH:mm:ss"); } }
    ...
}

次に、値をSetModifiedに割り当て、次のようにコードを変更します。

from a in Products
select new UserProductVM
{
     ...
     SetModified = a.modified
}

私が使用していることに注意してくださいserProductVM代わりにProductVMそしてSetModified代わりにmodified

次に、プロパティModifiedを取得すると、新しいPOCOはそれをフォーマットした文字列として取得します。

0
William Ardila

これはそれほど多くを追加しないかもしれませんが、誰かが私と同じくらい夢中になっている場合に備えて、時間部分も含めてDatePart/DateNameを使用してDr.Zimの回答の式ツリーを構築する必要がある場合の完全なコードを次に示します。明らかに、他の目的のために、Product-> YourInitialType、ProductVM-> YourResultType、およびmodified-> YourPropertyを変更できます。

編集(1/23/08):これから生成されたSQLは6.0.2と6.1.3の間で変更されました。最初に、値がnullの場合、生成されたSQLはnullの結果を作成します。この場合、これは望ましいと考えましたが、他の場合には望ましくなく(null + "文字列値" = null)、出力が希望と等しくない可能性がある理由がわかります。以下で列の出力がどのように変化したかを詳しく説明しますが、これはnull値に対して「// ::」を出力するようになりました。呼び出しコードでこの出力を特殊なケースとして処理し、手動でnullに戻しただけですが、nullがnullとして出力されるように、より堅牢な結果を追加することに取り組む必要がある場合もあります。新しいバージョンでは、SQLステートメントが非常に長いことにも注意してください。

ParameterExpression paramExp = Expression.Parameter(typeof(Product));
string propertyName = "modified";            
Expression propertyOrField = Expression.PropertyOrField(paramExp, propertyName);

MethodInfo datePartMethod = typeof(System.Data.Entity.SqlServer.SqlFunctions).GetMethods().Where(x => x.Name == "DatePart" && x.GetParameters().Length == 2 && x.GetParameters()[1].ParameterType == typeof(DateTime?)).First();
MethodInfo dateNameMethod = typeof(System.Data.Entity.SqlServer.SqlFunctions).GetMethods().Where(x => x.Name == "DateName" && x.GetParameters().Length == 2 && x.GetParameters()[1].ParameterType == typeof(DateTime?)).First();
MethodInfo stringConvertMethod = typeof(System.Data.Entity.SqlServer.SqlFunctions).GetMethods().Where(x => x.Name == "StringConvert" && x.GetParameters().Length == 1 && x.GetParameters()[0].ParameterType == typeof(decimal?)).First();
MethodInfo stringConcatMethod = typeof(string).GetMethods().Where(x => x.Name == "Concat" && x.GetParameters().Length == 2 && x.GetParameters()[0].ParameterType == typeof(string) && x.GetParameters()[1].ParameterType == typeof(string)).First();
MethodInfo stringTrimMethod = typeof(string).GetMethods().Where(x => x.Name == "Trim" && x.GetParameters().Length == 0).First();
Type projectedType = typeof(ProductVM);
NewExpression newHolder = Expression.New(projectedType);  
MemberInfo member = anonType.GetMember("modified")[0];
var monthPartExpression = Expression.Call(null, datePartMethod, Expression.Constant("mm", typeof(string)), propertyOrField);
var convertedMonthPartExpression = Expression.Call(null, stringConvertMethod, Expression.Convert(monthPartExpression, typeof(decimal?)));
var convertedDayPartExpression = Expression.Call(null, dateNameMethod, Expression.Constant("dd", typeof(string)), propertyOrField);
var convertedYearPartExpression = Expression.Call(null, dateNameMethod, Expression.Constant("yyyy", typeof(string)), propertyOrField);
var convertedHourPartExpression = Expression.Call(null, dateNameMethod, Expression.Constant("hh", typeof(string)), propertyOrField);
var convertedMinutePartExpression = Expression.Call(null, dateNameMethod, Expression.Constant("n", typeof(string)), propertyOrField);
var convertedSecondPartExpression = Expression.Call(null, dateNameMethod, Expression.Constant("ss", typeof(string)), propertyOrField);

var allAddedExpression = Expression.Call(null, stringConcatMethod, 
            convertedMonthPartExpression, 
            Expression.Call(null, stringConcatMethod,
                Expression.Constant("/", typeof(string)), 
                Expression.Call(null, stringConcatMethod, 
                    convertedDayPartExpression, 
                    Expression.Call(null, stringConcatMethod, 
                        Expression.Constant("/", typeof(string)), 
                        Expression.Call(null, stringConcatMethod,
                            convertedYearPartExpression,
                            Expression.Call(null, stringConcatMethod,
                                Expression.Constant(" ", typeof(string)),
                                Expression.Call(null, stringConcatMethod,
                                    convertedHourPartExpression,
                                    Expression.Call(null, stringConcatMethod,
                                        Expression.Constant(":", typeof(string)),
                                        Expression.Call(null, stringConcatMethod,
                                            convertedMinutePartExpression,
                                            Expression.Call(null, stringConcatMethod,
                                                Expression.Constant(":", typeof(string)),
                                                convertedSecondPartExpression

))))))))));
var trimmedExpression = Expression.Call(allAddedExpression, stringTrimMethod, new Expression[] { });    
var month = Expression.Bind(member, trimmedExpression);

MemberInitExpression memberInitExpression =
    Expression.MemberInit(
        newHolder,
        new MemberBinding[] { month });
var lambda = Expression.Lambda<Func<Product, ProductVM>>(memberInitExpression, paramExp);
0
Brandon Barkley