简体   繁体   English

在C#中创建动态查询

[英]Creating Dynamic Queries in C#

I am creating a custom reporting tool that allows the user to enter a list of criteria. 我正在创建一个自定义报告工具,该工具允许用户输入条件列表。

public partial class CustomReportCriteria
    {
        public int Id { get; set; }
        public int ReportId { get; set; }
        public string Operator { get; set; }
        public string CriteriaValue { get; set; }
        public string CriteriaType { get; set; }
        public string CriteriaField { get; set; }
        public string PropertyType { get; set; }

        public virtual CustomReport CustomReport { get; set; }
    }

I need to utilize these fields to create dynamic queries on my database. 我需要利用这些字段在数据库上创建动态查询。 For example: 例如:

Select BidYear From FWOBid Where BidYear = 2015
// ^ this would look like this instead
Select CriteriaField From PropertyType Where CriteriaField [Operator] CriteriaValue

However, I may not always be querying the same table, and in most cases the query will require joins on other tables. 但是,我可能并不总是在查询同一张表,并且在大多数情况下,查询将需要在其他表上进行联接。

I already have generic methods for the where clauses: 我已经有了where子句的通用方法:

 public static IQueryable<T> Filter<T>(IQueryable<T> query, string propertyName, string propertyValue, string op)
    {
        PropertyInfo propertyInfo = typeof(T).GetProperty(propertyName);
        return Filter<T>(query, propertyInfo, propertyValue, op);
    }

    public static IQueryable<T> Filter<T>(IQueryable<T> query, PropertyInfo propertyInfo, string propertyValue, string op)
    {
        ParameterExpression e = Expression.Parameter(typeof(T), "e");
        MemberExpression m = Expression.MakeMemberAccess(e, propertyInfo);
        UnaryExpression c = GetExpressionByType(propertyValue, propertyInfo.PropertyType);

        BinaryExpression b = null;
        if (op == "=")
        {
            b = Expression.Equal(m, c);
        }
        else if (op == "!=")
        {
            b = Expression.NotEqual(m, c);
        }
        else if (op == ">")
        {
            b = Expression.GreaterThan(m, c);
        }
        else if (op == "<")
        {
            b = Expression.LessThan(m, c);
        }

        Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(b, e);
        return query.Where(lambda);
    }

The approach I was envisioning was something like this: 我设想的方法是这样的:

foreach (var crit in report.CustomReportCriterias)
            {
                var objectName = crit.PropertyType;
                var value = crit.CriteriaValue;
                var type = crit.CriteriaType;
                var field = crit.CriteriaField;
                var op = crit.Operator;

                // how to dynamically get a generic collection ?
                var queryable = _context.Set<objectName>();
                var results = Filter<objectName>(queryable, field, value, op);
                // would then do joins if needed
            }

But, I'm struggling with the initial steps of retrieving a queryable from the db based on a string, and then I'm lost as how to join these results after. 但是,我正在努力从基于字符串的数据库中检索可查询对象的初始步骤中挣扎,然后我迷失了如何加入这些结果。

Have a look at https://dynamiclinq.codeplex.com/releases/view/109593 . 看看https://dynamiclinq.codeplex.com/releases/view/109593 This library extends the Linq library with a System.Linq.Dynamic namespace that provides string-based overloads to common query methods, so you can dynamically build string-based criteria for filtering and grouping. 该库使用System.Linq.Dynamic命名空间扩展了Linq库,该命名空间为常见查询方法提供了基于字符串的重载,因此您可以动态地构建基于字符串的条件以进行过滤和分组。 In addition, the one in my link is one better than the version available through NuGet, because it allows you to define the entire query, including the record type to retrieve, in Linq syntax as one string (see the Documentation tab for one example; it uses a static typeof expression to provide a Type to parse the results into, but you can reflectively create generic types with Type.MakeGenericType() ). 另外,我的链接中的一个优于NuGet提供的版本,因为它允许您使用Linq语法将整个查询(包括要检索的记录类型)定义为一个字符串(请参见“文档”选项卡中的一个示例;它使用静态的typeof表达式来提供将结果解析为的Type,但是您可以使用Type.MakeGenericType()来反射性地创建泛型。

The problem's going to be getting back to a statically-defined type; 问题将回到静态定义的类型。 once you go dynamic, you tend to have to stay dynamic, using GetProperties() to enumerate data fields and GetValue/SetValue to manipulate them. 一旦变为动态状态,就倾向于保持动态状态,使用GetProperties()枚举数据字段,并使用GetValue / SetValue对其进行操作。 There are a few tricks, typically requiring the use of a set of concretely-typed overloads that force the runtime to examine the true type of the object, then once the object's passed into one of those overloads you're back in static world again. 有一些技巧,通常需要使用一组具体类型的重载,这些重载迫使运行时检查对象的真实类型,然后,一旦将对象传递给这些重载之一,您就又回到了静态世界。

Using Entity Framework you can execute a query directly and have the results map to an entity like this. 使用实体框架,您可以直接执行查询并将结果映射到这样的实体。

var results = db.ExecuteStoreQuery<YourEntity>(
    "SELECT * FROM YourEntities WHERE SomeColumn = @param1", 
    param1);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM