C# Entity Framework Core Sql Server – 在运行时动态查询字段

huangapple go评论62阅读模式
英文:

C# Entity Framework Core Sql Server - Query fields dynamically

问题

我将为您翻译代码的部分:

我将我的问题保持简单,而不是解释我的完整情况。
是否有可能通过提供表的字段名称的查询来构建Entity Framework Core中的查询,这些字段名称存储在变量中?

让我们假设有这个表

 "product"
- id bigint
- name string
- status int

用户可以通过选择和组合不同的筛选器进行个别筛选。因此,我有一个筛选器列表,应该应用于查询。

因此,如果用户只想按`id`进行筛选,我需要一个查询,看起来像这样,但是`product.id`应该由我的筛选器列表中的变量(`filter.fieldName`)填充:

使用(Database.DBContext context = new Database.DBContext())
{
    var query = context.ProductsTable;
    foreach(Models.Filter filter in filterList)
    {
        query.Where((product) => product.id == filter.value); // <-- product.id to be replaced by variable (filter.fieldName)?
    }
    var results = query.ToList();
}

我已为您翻译好代码的部分。

英文:

I keep my question simple instead of explaining my complete situation.
Is there a possibility to build a query in Entity Framework Core by providing the query field names of the table from a variable?

Let's assume this table

Table "product"

  • id bigint
  • name string
  • status int

The users can filter individually by choosing and combining different filters. So I have a list of filters which should be applied to the query.

So if a user wants to filter only by id, I need a query which looks like this, but with the product.id filled by a variable from my filter list (filter.fieldName)

using(Database.DBContext context = new Database.DBContext())
{
    var query = context.ProductsTable;
    foreach(Models.Filter filter in filterList)
    {
        query.Where((product) =&gt; product.id == filter.value); // &lt;-- product.id to be replaced by variable (filter.fieldName)?
    }
    var results = query.ToList();
}

Or is there another way to achieve this without using Where. I know I could also use LINQ, but I think there is the same issue.

答案1

得分: 3

To preserve the mapping layer, you can work with Entities + Expressions.

using (Database.DBContext context = new Database.DBContext())
{
    var query = context.ProductsTable;
    foreach (Models.Filter filter in filterList)
    {
        var lambdaParameter = Expression.Parameter(typeof(ProductsTable));
        var lambdaPropertyAccessor = Expression.Property(lambdaParameter, filter.FieldName);

        var comparisonBody = Expression.Equal(lambdaPropertyAccessor, Expression.Constant(filter.Value));
        var comparisonLambda = Expression.Lambda<Func<ProductsTable, bool>>(comparisonBody, lambdaParameter);

        query = query.Where(comparisonLambda);
    }
    var results = query.ToList();
}
英文:

To preserve the mapping layer, you can work with Entities + Expressions.

using(Database.DBContext context = new Database.DBContext())
{
    var query = context.ProductsTable;
    foreach(Models.Filter filter in filterList)
    {
        var lambdaParameter = Expression.Parameter(typeof(ProductsTable));
        var lambdaPropertyAccessor = Expression.Property(lambdaParameter, filter.FieldName);

        var comparisonBody = Expression.Equal(lambdaPropertyAccessor, Expression.Constant(filter.Value));
        var comparisonLambda = Expression.Lambda&lt;Func&lt;ProductsTable, bool&gt;&gt;(comparisonBody, lambdaParameter);

        query = query.Where(comparisonLambda);
    }
    var results = query.ToList();
}

答案2

得分: 0

你可以在EFCore中使用FromSqlRaw来使用动态SQL查询但在使用时要非常小心,需要保护自己免受可能的SQL注入攻击。

FromSqlRaw允许你使用字符串内插来构建SQL查询 - 所以在你的情况下,它可能是这样的:

using(Database.DBContext context = new Database.DBContext())
{
    var query = context.ProductsTable;
    foreach(Models.Filter filter in filterList)
    {
        var filterValue = new SqlParameter("filterValue", filter.value);
        query.FromSqlRaw(
            $"SELECT * FROM Products WHERE {filter.fieldName}=@filterValue",
            filterValue); 
    }
    var results = query.ToList();
}

重要提示:筛选值通过SQL参数提供给数据库,因此它是防止SQL注入的,但列名不安全 - 你应该通过这种方法保护自己免受SQL注入。

如果你的 fieldName 属性值来自你自己代码中的一组封闭选项,并且用户只能从中选择,那么是安全的。

但是,如果用户可以自由输入 fieldName 的值,你必须在尝试运行查询之前验证它是否是有效的名称。

你可以通过白名单值来实现这一点 - 这意味着将输入的值与数据库中的实际列名进行比较(注意:EF不要求模型中的属性名与数据库中列名相同)。

英文:

You can use dynamic SQL queries in EFCore using FromSqlRaw - but be very careful when using it - you'll have to protect yourself from possible SQL Injection attacks.
FromSqlRaw allows you to use string interpolation to build the SQL query - so in your case it would be something like this:

using(Database.DBContext context = new Database.DBContext())
{
    var query = context.ProductsTable;
    foreach(Models.Filter filter in filterList)
    {
        var filterValue = new SqlParameter(&quot;filterValue &quot;, filter.value);
        query.FromSqlRaw(
            $&quot;SELECT * FROM Products WHERE {filter.fieldName}=@filterValue&quot;,
            filterValue); 
    }
    var results = query.ToList();
}

Important: the filter value is provided to the database via an sql parameter so it's SQL injection safe, but the column name is not safe - and you should protect yourself from SQL injection using this method.

If your fieldName property value comes from a closed list of options in your own code, and all the user can do is choose from it, then you're safe.
However, if the user can enter the value of fieldName as free text, you will have to verify that it's a valid name before attempting to run the query.
You do that by white-listing the value - meaning compare the entered value to the actual column name in the database (Note: The name of the property in the model isn't required by EF to be the same as the name of the column in the database).

huangapple
  • 本文由 发表于 2023年4月11日 16:24:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/75983826.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定