Attualmente sto usando la libreria Dynamic LINQ ( link codeplex originale ) per la funzionalità di ricerca dinamica. Ho un requisito che ho bisogno di OrderBy
un DateTime?
campo solo tenendo conto della data e non l'ora. Sto interrogando un database SQL Azure usando EntityFramework
.
Ecco l'entità campione.
public class SampleEntity
{
public DateTime? DateCreated { get; set; }
public bool Flag { get; set; }
}
Ecco il codice per interrogare il database.
var orderByString = "DateCreated.Value.Date asc, Flag"; //This is a dynamic string
var query = _context.Set<SampleEntity>().OrderBy(orderByString);
Questa espressione ("DateCreated.Value.Date") viene analizzata senza problemi da System.Linq.Dynamic
ma genera il seguente errore (che è comprensibile) a causa del fatto che non è supportato da LINQ alle entità. Tieni presente che questo deve funzionare su un IQueryable ( non posso usare la risposta di questo post ) poiché ho bisogno che l'ordinamento venga eseguito dal lato server.
Il membro del tipo specificato 'Data' non è supportato in LINQ alle entità. Sono supportati solo gli inizializzatori, i membri dell'entità e le proprietà di navigazione delle entità.
La soluzione è utilizzare DbFunctions.TruncateTime()
come espresso in questa risposta e in altri. Tuttavia, non funzionerà con System.Linq.Dynamic
Qualche idea su come affrontare questo problema. La soluzione possibile è aggiungere un'altra colonna al DB con solo la parte Date di DateCreated
e interrogare quella colonna. Tuttavia, preferirei non farlo e stavo cercando qualche altra soluzione a questo problema. Un altro approccio sarebbe quello di generare dinamicamente l'espressione lambda e restituire DbFunctions.TruncateTime e quindi eseguirlo contro il DB. Qualsiasi input è apprezzato.
Grazie,
È possibile utilizzare l'approccio dalla mia risposta a Dynamic Linq + Entity Framework: modifiche datetime per la selezione dinamica , ovvero post-elaborare l'espressione della query con ExpressionVisitor
personalizzato e sostituire i metodi non supportati con i loro equivalenti DbFunctions
. In questo caso particolare, sostituire la proprietà DateTime.Date
con la chiamata al metodo DbFunctions.TruncateTime
:
public static class QueryableExtensions
{
public static IQueryable<T> BindDbFunctions<T>(this IQueryable<T> source)
{
var expression = new DbFunctionsBinder().Visit(source.Expression);
if (expression == source.Expression) return source;
return source.Provider.CreateQuery<T>(expression);
}
public static IQueryable BindDbFunctions(this IQueryable source)
{
var expression = new DbFunctionsBinder().Visit(source.Expression);
if (expression == source.Expression) return source;
return source.Provider.CreateQuery(expression);
}
class DbFunctionsBinder : ExpressionVisitor
{
protected override Expression VisitMember(MemberExpression node)
{
if (node.Expression != null && node.Expression.Type == typeof(DateTime) && node.Member.Name == "Date")
{
var dateValue = Expression.Convert(Visit(node.Expression), typeof(DateTime?));
var methodCall = Expression.Call(typeof(DbFunctions), "TruncateTime", Type.EmptyTypes, dateValue);
return Expression.Convert(methodCall, typeof(DateTime));
}
return base.VisitMember(node);
}
}
}
Esempio di utilizzo:
var orderByString = "DateCreated.Value.Date asc, Flag"; //This is a dynamic string
var query = _context.Set<SampleEntity>().OrderBy(orderByString).BindDbFunctions();