簡體   English   中英

如何針對層次對象列表動態構建和存儲復雜的linq查詢?

[英]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
};

一些注意事項。

  • 根據用戶定義條件的復雜程度,我可能需要也可能不需要從不同的對象列表中拉出:“ froms”是動態的。
  • 請注意,在此示例中,我從task.fields []提取了兩次,因此我對其進行了兩次別名。
  • 示例LINQ結構使我可以具有復雜的AND,OR,括號等,而我認為這對於動態鏈或表達式樹不可行。

在我的代碼中,我設想:

//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

還要注意,“與”和“或”的順序非常重要。 從本質上講,您允許客戶針對您的數據構建S​​QL查詢,而這通常以眼淚結束。 將它們限制在應查詢的適當條件范圍內是您的工作。

基於與Guillaume的討論,我只建議在使用高級動態查詢生成時注意結果查詢的類型。 如果您要更改通過SelectAggregate或其他方法之一返回的內容的形狀,您將期望內部類型相應地更改。 如果只是使用Where進行過濾,則可以繼續添加所需的其他案例,除非您希望使用OR行為,那么PredicateBuilder之類的東西會有所幫助。 如果要通過JoinZip ,...提取更多數據,則可以進行過濾,將其添加到返回的行中,並可能更改數據的形狀。

過去,我已經做過很多事情,並且最成功的工作集中在允許我遇到常見情況的特定輔助方法上,然后依靠linq表達式樹和模式(例如訪問者模式)來允許在運行時構建自定義表達式。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM