[英]How to dynamically build and store complex linq queries against a list of hierarchical objects?
我有一个分层结构的对象列表。 我想根据客户公司设置的“条件”针对该对象列表构建复杂的LINQ查询,并将其存储在数据库中。 因此,我需要在运行时构建它们,但是由于每当客户端的用户更新或刷新其数据时它们就会重复运行,所以我希望将LINQ查询存储在对象中,而不是每次都重新构建它们。
我看过ScottGu的有关Dynamic LINQ的博客。
这篇文章也是关于使用表达式树的 。
这些似乎都不能提供适当的解决方案,但是我可能没有充分理解它们。 恐怕我在考虑其他选项时会尝试使用LINQ。
我的对象层次结构:
WorkOrder[]
Field[]
Task[]
Field[]
这是我要存储和执行的LINQ查询的示例。 我可以根据定义条件的数据库记录合理地构建此格式。
var query =
from wo in WorkOrders
from woF in wo.Fields
from task in wo.Tasks
from taskF in task.Fields
from taskF2 in task.Fields
where woF.Name == "System Status"
&& woF.Value.Contains("SETC")
&& taskF.Name == "Material"
&& taskF.Value == "Y"
&& taskF2.Name == "Planner"
&& taskF2.Value == "GR5259"
select new
{
wo_id = wo.ID,
task_id = task.ID
};
一些注意事项。
在我的代码中,我设想:
//1) Retrieve business rules from DB. I can do this.
//2) Iterate through the business rules to build the linq queries.
foreach (BusinessRule br in BusinessRules) {
//Grab the criteria for the rule from the DB.
//Create a linq to object query based on the criteria just built.
//Add this query to a list for later use.
}
...Elsewhere in application.
//Iterate through and execute the linq queries in order to apply business rules to data cached in the application.
foreach (LinqQuery q in LinqQueries) {
//Execute the query
//Apply business rule to the results.
}
非常感谢您的想法,努力和想法。
从技术上讲,仅使用LINQ即可实现所需的功能,但PredicateBuilder是一个不错的实用程序类:
public enum AndOr
{
And,
Or
}
public enum QueryableObjects
{
WorkOrderField,
TaskField
}
public class ClientCondition
{
public AndOr AndOr;
public QueryableObjects QueryableObject;
public string PropertyName;
public string PropertyValue;
}
public void PredicateBuilderExample()
{
var conditions = new List<ClientCondition> {
{
new ClientCondition { AndOr = LINQ.AndOr.And,
QueryableObject = QueryableObjects.WorkOrderField,
PropertyName = "System Status",
PropertyValue = "SETC"
}
},
{
new ClientCondition{AndOr = AndOr.And,
QueryableObject = QueryableObjects.TaskField,
PropertyName = "Material",
PropertyValue = "Y"
}
},
{
new ClientCondition{AndOr = AndOr.Or,
QueryableObject = QueryableObjects.TaskField,
PropertyName = "Planner",
PropertyValue = "GR5259"
}
}
};
//Obviously this WorkOrder object is empty so it will always return empty lists when queried.
//Populate this yourself.
var WorkOrders = new List<WorkOrder>();
var wofPredicateBuilder = PredicateBuilder.True<WorkOrderField>();
var tfPredicateBuilder = PredicateBuilder.True<TaskField>();
foreach (var condition in conditions)
{
if (condition.AndOr == AndOr.And)
{
if (condition.QueryableObject == QueryableObjects.WorkOrderField)
{
wofPredicateBuilder = wofPredicateBuilder.And(
wof => wof.Name == condition.PropertyName &&
wof.Value.Contains(condition.PropertyValue));
}
}
if (condition.AndOr == AndOr.Or)
{
if (condition.QueryableObject == QueryableObjects.TaskField)
{
tfPredicateBuilder = tfPredicateBuilder.Or(
tf => tf.Name = condition.PropertyName &&
tf.Value.Contains(condition.PropertyValue));
}
}
//And so on for each condition type.
}
var query = from wo in WorkOrders
from woF in wo.Fields.AsQueryable().Where(wofPredicateBuilder)
from task in wo.Tasks
from taskF in task.Fields.AsQueryable().Where(tfPredicateBuilder)
select new
{
wo_id = wo.ID,
task_id = task.ID
};
}
请注意,我使用枚举来限制客户可以向您发送的条件。 要拥有真正的动态可查询引擎,您将需要使用反射来确保收到的对象名称有效。 这似乎是一个相当大的范围,在那一点上,我建议您研究一种不同的方法,例如ElasticSearch 。
还要注意,“与”和“或”的顺序非常重要。 从本质上讲,您允许客户针对您的数据构建SQL查询,而这通常以眼泪结束。 将它们限制在应查询的适当条件范围内是您的工作。
基于与Guillaume的讨论,我只建议在使用高级动态查询生成时注意结果查询的类型。 如果您要更改通过Select
, Aggregate
或其他方法之一返回的内容的形状,您将期望内部类型相应地更改。 如果只是使用Where进行过滤,则可以继续添加所需的其他案例,除非您希望使用OR行为,那么PredicateBuilder之类的东西会有所帮助。 如果要通过Join
, Zip
,...提取更多数据,则可以进行过滤,将其添加到返回的行中,并可能更改数据的形状。
过去,我已经做过很多事情,并且最成功的工作集中在允许我遇到常见情况的特定辅助方法上,然后依靠linq表达式树和模式(例如访问者模式)来允许在运行时构建自定义表达式。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.