我處於需要在運行時在EF上下文中指定查詢的情況。我們的顧問根據客戶的特定情況在客戶現場配置這些查詢。如何在運行時使用不同類型的多個子句構建linq查詢
爲了方便我考慮使用linq構建查詢,基於顧問在某種類型的前端(現在,winforms)中指定的條件列表。顧問基本上從一個對象指定一個屬性,指定操作符,然後指定值。例如:給我所有的客戶,其中的[狀態] [等於] [1]。
此刻我有一個表達式構建器,它在運行時創建where子句,到目前爲止,我可以管理一個group by子句。我在這裏遇到困難的時候,顧問是通過不同類型的子句(f.e.一個字符串和一個datetime屬性)來配置多個組。
例如,我必須能夠處理這個查詢:從bsn,dateofbirth(其中bsn = string和dateofbirth = datetime)的status = 1 group的客戶機中選擇bsn as a,dateofbirth as b。
目前,這是「膠水」,查詢起來代碼:
public List<ClientV2> ExportClients(List<CriteriaV2> criteriaList)
{
var whereExpression = BuildWhereExpressionChain(criteriaList.Where(c => c.Operator != CriteriaOperatorV2.GROUPBY).ToList());
var groupByExpression = BuildGroupByExpression(criteriaList.Where(c => c.Operator == CriteriaOperatorV2.GROUPBY).ToList());
var sourceClients = _context.Clients.Where(whereExpression).GroupBy(groupByExpression).ToList();
IEnumerable<Client> resultClients = sourceClients.SelectMany(group => group);
return ClientToClientV2.MapList(resultClients.ToList());
}
這是where子句建設者:
private Expression<Func<Client, bool>> BuildWhereExpressionChain(List<CriteriaV2> criteriaList)
{
var expressionList = new List<Expression<Func<Client, bool>>>();
var paramExp = Expression.Parameter(typeof(Client));
foreach (var crit in criteriaList)
{
var propertyItem = PropertyTranslator.GetPropertyItem(crit.Property);
if (propertyItem == null) throw new InvalidFilterCriteriaException("Property " + crit.Property + " niet toegestaan als filter criterium");
var propInfo = typeof(Client).GetProperty(propertyItem.InternalName);
var left = Expression.Property(paramExp, propInfo);
Expression right;
if (propInfo.PropertyType.IsEnum)
right = Expression.Constant(Enum.ToObject(propInfo.PropertyType, PropertyTranslator.TranslateEnum(propertyItem.Type, crit.Value)));
else if (propInfo.PropertyType == typeof(DateTime) || propInfo.PropertyType == typeof(DateTime?))
right = Expression.Constant(DateTime.Parse(crit.Value), propInfo.PropertyType);
else
right = Expression.Constant(crit.Value, typeof(string));
var exp = BuildExpression(left, right, crit.Operator);
expressionList.Add(Expression.Lambda<Func<Client, bool>>(exp, new ParameterExpression[] { paramExp }));
}
var firstExpression = expressionList.First();
expressionList.Skip(1).ToList().ForEach(ex => { firstExpression = firstExpression.And(ex); });
return firstExpression;
}
而這正是我的一部分)在
private Expression<Func<Client, string>> BuildGroupByExpression(List<CriteriaV2> criteriaList)
{
var expressionList = new List<Expression<Func<Client, string>>>();
var paramExp = Expression.Parameter(typeof(Client));
foreach (var crit in criteriaList)
{
var propertyItem = PropertyTranslator.GetPropertyItem(crit.Property);
if (propertyItem == null) throw new InvalidFilterCriteriaException("Property " + crit.Property + " niet toegestaan als group by criterium");
var propInfo = typeof(Client).GetProperty(propertyItem.InternalName);
var body = Expression.Property(paramExp, propInfo);
var lambda = Expression.Lambda<Func<Client, string>>(body, paramExp);
expressionList.Add(lambda);
}
var firstExpression = expressionList.First();
expressionList.Skip(1).ToList().ForEach(ex => { firstExpression = firstExpression.And(ex); });
return firstExpression;
}
是否有可能使BuildGroupByExpression(:卡(它爲一個字串類型的從句工作)這樣,它會導致一個表達式,其中包含多個可以直接在.GroupBy(表達式)中使用的不同類型的子句;?
我對linq的專家並不多,但我有一種感覺,我想要的是可能的。如果我在這裏做一些愚蠢的事情,請將它們指出來,然後我會繼續努力。
你試過dynamiclinq? –
我試過了,但不知怎的,它從來沒有得到它的工作,因爲它似乎並不支持多個分組的子句,也不需要自己寫。我確實發現缺少tbh的文檔,它只是一堆.cs文件,所以也許我錯過了一些內容。 – Helfdane