Sto cercando di eseguire una query su un elenco per fornire risultati immediati utilizzando un'espressione impostata altrove nel codice, mentre un secondo thread si spegne e lo utilizza per ottenere un set completo di risultati da un database in una query Linq.
So che l'espressione stessa è OK come quando la invio via cavo al lato server e la applico a un IQueryble, quindi funzionerà. Tuttavia, se applicato sul lato client, viene generato il seguente errore:
System.InvalidOperationException: 'variabile' elemento 'di tipo' MissionControlSuite.Shared.Interface.Model.IBox 'referenziato dall'ambito' ', ma non è definito'
Questo codice che esegue il filtraggio:
public abstract class GridWithPaging<T> where T : class
{
List<T> _items
public Expression<Func<T, bool>> Filter { get; set; }
public ObservableCollection<IGridFilter<T>> Filters { get; set; }
// more code skipped for breveity
public void FiltersChanged(object sender, EventArgs e)
{
Expression<Func<T, bool>> combined = item => true;
foreach (var filter in Filters)
combined = Expression.Lambda<Func<T, bool>>(
Expression.AndAlso(filter.Expression.Body, combined.Body),
combined.Parameters.Single());
Filter = combined;
NotifyPropertyChanged("Filter");
}
public void AddFilter(IGridFilter<T> filter)
{
Filters.Add(filter);
NotifyPropertyChanged("Filters");
}
private void DoFiltering(int start)
{
var newView = _items.Skip(start);
if(Filter != null)
newView = newView.AsQueryable().Where(Filter);
//some more code that acts on the filtered list
}
}
L'espressione è impostata altrove come questa:
Expression<Func<IBox, bool>> expression = item => item.BoxId.Contains(v);
var filter = new GridFilter<IBox>()
{
Name = string.Format("{0} contains {1}", Property, Value),
Expression = expression
};
Grid.AddFilter(filter);
Questa risposta ti aiuterà: Combinare due espressioni (Espressione <Func <T, bool >>)
Riassumendo: il problema è che i parametri, sebbene con lo stesso nome, non sono la stessa istanza di ParameterExpression
, che è inclusa nel commento per quella risposta .
La soluzione è utilizzare un visitatore (codice copiato dal post collegato):
public static Expression<Func<T, bool>> AndAlso<T>(
this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var parameter = Expression.Parameter(typeof (T));
var leftVisitor = new ReplaceExpressionVisitor(expr1.Parameters[0], parameter);
var left = leftVisitor.Visit(expr1.Body);
var rightVisitor = new ReplaceExpressionVisitor(expr2.Parameters[0], parameter);
var right = rightVisitor.Visit(expr2.Body);
return Expression.Lambda<Func<T, bool>>(
Expression.AndAlso(left, right), parameter);
}
private class ReplaceExpressionVisitor
: ExpressionVisitor
{
private readonly Expression _oldValue;
private readonly Expression _newValue;
public ReplaceExpressionVisitor(Expression oldValue, Expression newValue)
{
_oldValue = oldValue;
_newValue = newValue;
}
public override Expression Visit(Expression node)
{
if (node == _oldValue)
return _newValue;
return base.Visit(node);
}
}
Nota anche: il provider ORM (o un altro IQueryable che usi) probabilmente funziona perché lo traduce direttamente in testo, mentre l'esecuzione ( Invoke
ing) di questa LambdaExpression
causa l'errore in quanto è più rigoroso, in un certo senso. Alcuni IQueryable
possono anche accettare semplici parametri di string
e analizzarli autonomamente, il che dovrebbe indicare che hanno una gamma più ampia di input accettabili.