2016-11-01 2 views
1

Я пытаюсь выполнить динамический поиск EF Core. Я сделал все это в петле следующим образом:Ошибка производительности с отражением

foreach (var i in vm.SearchProperties) 
{ 
    if (result == null) 
    result = db.MyTable.Where(x => x.GetType().GetProperty(i).GetValue(x, null).ToString().ToLower().StartsWith("MySearchString")); 
    else 
    result = result.Where(x => x.GetType().GetProperty(i).GetValue(x,null).ToString().ToLower().StartsWith(i.Suchfeld.ToLower(“mySearchString”))); 
} 

Прежде чем добавить часть отражения, она работает довольно быстро. Как только я добавил Reflection к нему, он замедлился в 1000 раз. Любые идеи о том, как я ускоряюсь, или вокруг части отражения.

+0

вы имели в виду сказать "_перед_ я добавил отражение часть"? – JLRishe

+0

какая часть медленная, если или линия? –

+0

Ваше выражение должно быть сложным для интерпретации конвертером LINQ to SQL, поэтому оно компилируется и выполняется на каждом элементе в вашей таблице. Я бы построил дерево выражений на основе свойств, которые вы хотите искать, а затем передайте их как 'Expression >'. –

ответ

1

Ваше выражение слишком сложно интерпретировать с помощью LINQ to SQL-конвертера, поэтому оно компилируется и выполняется на каждом элементе вашей таблицы, поэтому неудивительно, что он выполняется чрезвычайно медленно.

Вам необходимо построить дерево выражений на основе свойств, которые вы хотите найти, а затем построить Expression<Func<MyType, bool>>, чтобы перейти к вашему методу Where(...). Таким образом, LINQ to SQL converter распознает это.

Попробуйте это:

ParameterExpression param = Expression.Parameter(typeof(MyType)); 
MethodInfo stringStartsWith = typeof(string).GetMethods().First(m => m.Name == "StartsWith" && m.GetParameters().Length == 1); 

PropertyInfo firstProp = typeof(MyType).GetProperty(vm.SearchProperties.First()); 
MemberExpression firstMembAccess = Expression.Property(param, firstProp); 
MethodCallExpression firstStartsWithExpr = Expression.Call(firstMembAccess, stringStartsWith, Expression.Constant(mySearchString)); 
Expression current = firstStartsWithExpr; 

foreach (string s in vm.SearchProperties.Skip(1)) 
{ 
    PropertyInfo prop = typeof(MyType).GetProperty(s); 
    MemberExpression membAccess = Expression.Property(param, prop); 
    MethodCallExpression startsWithExpr = Expression.Call(membAccess, stringStartsWith, Expression.Constant(mySearchString)); 
    current = Expression.OrElse(current, startsWithExpr); 
} 

Expression<Func<MyType, bool>> mySearchExpression = Expression.Lambda<Func<MyType, bool>>(current, param); 

result = db.MyTable.Where(mySearchExpression); 

Примечание: MyType относится к тому, что ваш тип объекта является.

0

Как уже ответили люди, отражение не может использоваться в IQueryable.

Данные загружаются в память и выполняется предложение Where.

Это подобно тому, как если бы вы сделали следующий код (добавление ToList() до пункта где):

foreach (var i in vm.SearchProperties) 
{ 
    if (result == null) 
    result = db.MyTable.ToList().Where(x => x.GetType().GetProperty(i).GetValue(x, null).ToString().ToLower().StartsWith("MySearchString")); 
    else 
    result = result.ToList().Where(x => x.GetType().GetProperty(i).GetValue(x,null).ToString().ToLower().StartsWith(i.Suchfeld.ToLower(“mySearchString”))); 
} 

@Mr Андерсон прав. Вам нужно использовать дерево выражений вместо отражения.

Однако использование дерева выражений иногда может быть сложным для построения.

Отказ от ответственности: Я владелец проекта Eval-Expression.NET

Этот проект позволяет оценить и выполнить динамическое выражение во время выполнения.

Короче говоря, вы можете передать динамическую строку в предложение where.

Wiki: Eval-Expression.NET - LINQ Dynamic

foreach (var i in vm.SearchProperties) 
{ 
    if (result == null) 
    result = db.MyTable.Where(x => "x." + i + ".ToLower().StartsWith('MySearchString')"); 
    else 
    result = result.Where(x => "x." + i + ".ToLower().StartsWith('MySearchString')"); 
} 
Смежные вопросы