我已經創建了一個基於條件集合構建謂詞的通用表達式構建器。我將謂詞傳遞給存儲庫中的通用方法。我認爲表達式生成器工作正常,並創建所需的謂詞,儘管實體框架生成的SQL腳本並不如我預期的那樣。我已經閱讀了很多關於動態查詢或LinqKit和表達式生成器的問題和文章,並且最相關的是this comment。我真的很感謝你能否看看我做了什麼,並讓我知道我是否犯了錯誤?爲實體框架創建動態表達式
下面是的ExpressionBuilder類的代碼:
public static class ExpressionBuilder
{
private static MethodInfo containsMethod = typeof(string).GetMethod("Contains");
private static MethodInfo startsWithMethod = typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) });
private static MethodInfo endsWithMethod = typeof(string).GetMethod("EndsWith", new Type[] { typeof(string) });
public static Expression<Func<T, bool>> GetExpression<T>(IList<ExpressionModel> filters)
{
if (filters == null)
return null;
IList<ExpressionModel> nullFreeCollection = filters.OfType<ExpressionModel>().ToList();
if (nullFreeCollection.Count == 0)
return null;
ParameterExpression param = Expression.Parameter(typeof(T), "item");
Expression exp = null;
if (nullFreeCollection.Count == 1)
exp = GetExpression<T>(param, nullFreeCollection[0]);
else if (nullFreeCollection.Count == 2)
exp = GetExpression<T>(param, nullFreeCollection[0], nullFreeCollection[1]);
else
{
while (nullFreeCollection.Count > 0)
{
var f1 = nullFreeCollection[0];
var f2 = nullFreeCollection[1];
if (exp == null)
exp = GetExpression<T>(param, nullFreeCollection[0], nullFreeCollection[1]);
else
exp = Expression.AndAlso(exp, GetExpression<T>(param, nullFreeCollection[0], nullFreeCollection[1]));
nullFreeCollection.Remove(f1);
nullFreeCollection.Remove(f2);
if (nullFreeCollection.Count == 1)
{
exp = Expression.AndAlso(exp, GetExpression<T>(param, nullFreeCollection[0]));
nullFreeCollection.RemoveAt(0);
}
}
}
return Expression.Lambda<Func<T, bool>>(exp, param);
}
private static Expression GetExpression<T>(ParameterExpression param, ExpressionModel filter)
{
MemberExpression member = Expression.Property(param, filter.PropertyName);
ConstantExpression constant = Expression.Constant(filter.Value);
switch (filter.Operator)
{
case ExpressionOperators.Equals:
return Expression.Equal(member, constant);
case ExpressionOperators.GreaterThan:
return Expression.GreaterThan(member, constant);
case ExpressionOperators.LessThan:
return Expression.LessThan(member, constant);
case ExpressionOperators.GreaterThanOrEqual:
return Expression.GreaterThanOrEqual(member, constant);
case ExpressionOperators.LessThanOrEqual:
return Expression.LessThanOrEqual(member, constant);
case ExpressionOperators.Contains:
return Expression.Call(member, containsMethod, constant);
case ExpressionOperators.StartsWith:
return Expression.Call(member, startsWithMethod, constant);
case ExpressionOperators.EndsWith:
return Expression.Call(member, endsWithMethod, constant);
}
return null;
}
private static BinaryExpression GetExpression<T>(ParameterExpression param, ExpressionModel filter1, ExpressionModel filter2)
{
Expression bin1 = GetExpression<T>(param, filter1);
Expression bin2 = GetExpression<T>(param, filter2);
return Expression.AndAlso(bin1, bin2);
}
public enum ExpressionOperators
{
Equals,
GreaterThan,
LessThan,
GreaterThanOrEqual,
LessThanOrEqual,
Contains,
StartsWith,
EndsWith
}
}
這裏是通用的存儲庫方法:
public IEnumerable<TEntity> RetrieveCollectionAsync(Expression<Func<TEntity, bool>> predicate)
{
try
{
return DataContext.Set<TEntity>().Where(predicate);
}
catch (Exception ex)
{
Logger.Error(ex);
throw ex;
}
}
而生成的腳本由實體框架的SQL(我期望有一個選擇查詢幾條where子句):
SELECT
CAST(NULL AS uniqueidentifier) AS [C1],
CAST(NULL AS uniqueidentifier) AS [C2],
CAST(NULL AS varchar(1)) AS [C3],
CAST(NULL AS uniqueidentifier) AS [C4],
CAST(NULL AS uniqueidentifier) AS [C5],
CAST(NULL AS uniqueidentifier) AS [C6],
CAST(NULL AS datetime2) AS [C7],
CAST(NULL AS datetime2) AS [C8],
CAST(NULL AS varchar(1)) AS [C9],
CAST(NULL AS uniqueidentifier) AS [C10],
CAST(NULL AS varchar(1)) AS [C11],
CAST(NULL AS uniqueidentifier) AS [C12],
CAST(NULL AS uniqueidentifier) AS [C13],
CAST(NULL AS uniqueidentifier) AS [C14],
CAST(NULL AS uniqueidentifier) AS [C15],
CAST(NULL AS datetime2) AS [C16],
CAST(NULL AS varchar(1)) AS [C17],
CAST(NULL AS datetime2) AS [C18],
CAST(NULL AS varchar(1)) AS [C19],
CAST(NULL AS tinyint) AS [C20]
FROM (SELECT 1 AS X) AS [SingleRowTable1]
WHERE 1 = 0
我正在使用
- 的EntityFramework 6.0
- .Net框架4.6
- ASP.NET MVC 5
更新型號爲表達式:
public class ExpressionModel
{
public string PropertyName { get; set; }
public ExpressionOperators Operator { get; set; }
public object Value { get; set; }
}
另一個缺失部分是一個通用的映射器它將給定的搜索條件映射到一個新的ExpressionModel,我相信它與這個問題無關。
看起來過於複雜。你能否包含缺少的部分 - ExpressionModel類和其他'GetExpression'函數,你從發佈的方法中調用。僅僅看代碼就不清楚爲什麼你有一個帶有2個模型參數的函數。 –
@IvanStoev我已根據要求更新了該問題。 – DeveloperX
@IvanStoev [this](https://msdn.microsoft.com/en-us/library/bb882637.aspx)文章也提出了類似的方法。 – DeveloperX