简体   繁体   English

C# 实体框架 - 如何在 Where 子句中动态传递值

[英]C# Entity Framework - How to pass a value in the Where clause dynamically

字段名称组合框显示 G_Name 和 G_Owner,它们是附加列,如 G_Platform 和 G_Type 在此处输入图片说明 I am trying to filter out rows in a table.我正在尝试过滤表中的行。

I have following code:我有以下代码:

if (groupRadio.Checked)
        {
            using (gf = new GFilterEntities())
            {
                dgvFiltered.DataSource = gf.GroupTs.Where(x => x.G_Platform.Equals(platformCombo.Text) &&
                x.G_Type.Equals(typeCombo.Text) && x.fieldNameCombo.Text <= how to do this

            }
        }

I want to dynamically pass a column name from a ComboBox in x.fieldNameCombo.Text but am unable to do so.我想从 x.fieldNameCombo.Text 中的 ComboBox 动态传递列名,但我无法这样做。 How can this be achieved?如何做到这一点?

Example: G_Platform and G_Type are column names in the GroupT table.示例:G_Platform 和 G_Type 是 GroupT 表中的列名。 When I enter x.当我输入 x 时。 I can see these values populate in Visual Studio.我可以看到这些值填充在 Visual Studio 中。

However, I'd like to be able to use a value that user inputs in a combo box for filtering purposes.但是,我希望能够使用用户在组合框中输入的值进行过滤。 So something like x.所以像x这样的东西。 user value in fieldNameComboBox .Equals(textBox1.text). fieldNameComboBox .Equals(textBox1.text) 中的用户值 The fieldNameComboBox value WILL be a column name, just like G_Platform and G_Type. fieldNameComboBox 值将是一个列名,就像 G_Platform 和 G_Type。

Thanks.谢谢。

UPDATE Based on one of the comments below (by sam), my code now reads:更新根据以下评论之一(由 sam),我的代码现在显示为:

 private void btnSearch_Click(object sender, EventArgs e)
    {
        if (groupRadio.Checked)
        {
            using (gf = new GFilterEntities())
            {
                var testClass = new TestClass { ComboBoxSelectedText = fieldNameCombo.Text, UserInputValue = txt1.Text};
                var comboBoxSelectedColumnValues = from object item in gf.GroupTs
                                                   let propertyInfo = item.GetType().GetProperties()
                                                   from info in propertyInfo
                                                   where string.Equals(info.Name, testClass.ComboBoxSelectedText)
                                                   select (string)info.GetValue(item)
                                                   into s
                                                   where !string.IsNullOrWhiteSpace(s)
                                                   select s;


                dgvFiltered.DataSource = gf.GroupTs.Where(x => x.G_Platform.Equals(platformCombo.Text) &&
                x.G_Type.Equals(typeCombo.Text) && comboBoxSelectedColumnValues.Equals(testClass.UserInputValue)
                ).ToList();
            }
        }
    }
    public class TestClass
    {
        public string ComboBoxSelectedText { get; set; }
        public string UserInputValue { get; set; }
    }

I am getting an error that says: System.NotSupportedException: 'Unable to cast the type 'Filtering.GroupT' to type 'System.Object'.我收到一条错误消息:System.NotSupportedException: '无法将类型'Filtering.GroupT' 转换为类型'System.Object'。 LINQ to Entities only supports casting EDM primitive or enumeration types.' LINQ to Entities 仅支持转换 EDM 原语或枚举类型。

When we need something "dynamic" in the query like this that LINQ doesn't really support we use the "Raw SQL Query" feature of EF Core that is explained here: https://docs.microsoft.com/en-us/ef/core/querying/raw-sql当我们在 LINQ 并不真正支持这样的查询中需要“动态”的东西时,我们使用 EF Core 的“原始 SQL 查询”功能,在此处解释: https : //docs.microsoft.com/en-us/ ef/core/querying/raw-sql

So you could write something like:所以你可以这样写:

var whereStatement = $"G_Platform = '{platformCombo.Text}' AND {fieldNameCombo.Text} = '{textBox1.text}'"; // generate this based on your user's input
dgvFiltered.DataSource = gf.GroupTs
                           .FromSqlRaw($"SELECT * FROM GroupTs WHERE {whereStatement}")
                           .ToList();

Couple of notes:几个注意事项:

  • The function name changed in EFCore 3.0 - in previous versions of EFCore it was called FromSql not FromSqlRaw EFCore 3.0 中的函数名称发生了变化 - 在以前版本的 EFCore 中,它被称为 FromSql 而不是 FromSqlRaw
  • In your final version please make sure you respect the warning in the documentation about using parameterization for raw SQL queries:在您的最终版本中,请确保您尊重文档中关于对原始 SQL 查询使用参数化的警告:

When introducing any user-provided values into a raw SQL query, care must be taken to avoid SQL injection attacks.在将任何用户提供的值引入原始 SQL 查询时,必须小心避免 SQL 注入攻击。 In addition to validating that such values don't contain invalid characters, always use parameterization which sends the values separate from the SQL text.除了验证这些值不包含无效字符之外,始终使用参数化来发送与 SQL 文本分开的值。

In particular, never pass a concatenated or interpolated string ($"") with non-validated user-provided values into FromSqlRaw or ExecuteSqlRaw.特别是,切勿将带有未经验证的用户提供值的串联或内插字符串 ($"") 传递到 FromSqlRaw 或 ExecuteSqlRaw。 The FromSqlInterpolated and ExecuteSqlInterpolated methods allow using string interpolation syntax in a way that protects against SQL injection attacks. FromSqlInterpolated 和 ExecuteSqlInterpolated 方法允许以防止 SQL 注入攻击的方式使用字符串插值语法。

Like ajphall mentioned depending on your version of EF you can use FromSqlRaw for EF Core or FromSql for older versions, in your case please use FromSqlInterpolated or sql parameters to prevent sql injection documentation: https://docs.microsoft.com/en-us/ef/core/querying/raw-sql就像提到的 ajphall 一样,根据您的 EF 版本,您可以将 FromSqlRaw 用于 EF Core 或 FromSql 用于旧版本,在您的情况下,请使用 FromSqlInterpolated 或 sql 参数来防止 sql 注入文档: https ://docs.microsoft.com/en-us /ef/core/querying/raw-sql

To solve your query issues you can do something like this:要解决您的查询问题,您可以执行以下操作:

var plataformParameter = platformCombo.Text;
var typeParameter = typeCombo.Text
var fieldParameter = fieldNameCombo.Text;   
dgvFiltered.DataSource = gf.GroupTs
                         .FromSqlInterpolated(
                                              $"SELECT * FROM GroupTs WHERE
                                              G_Platform = {plataformParameter } AND 
                                              G_Type = {typeParameter} AND 
                                              (G_Name = {fieldParameter} 
                                              OR G_Owner = {fieldParameter}")
                                              .ToList()

Depending on your data if you don't want to return nulls you could check for that first.根据您的数据,如果您不想返回空值,您可以先检查一下。

...((G_Name<>null and G_Name={fieldParameter})or (G_Owner <> null and G_Owner={fieldParameter}))

Hope this helps.希望这可以帮助。

UPDATE:更新:

You can use dynamic SQL but be aware that you must validate the inputs or you will endup with sql injection:您可以使用动态 SQL,但请注意,您必须验证输入,否则最终会出现 sql 注入:

  1. Create a stored procedure创建存储过程
  2. Add all the parameters you want to be filtering by.添加您要过滤的所有参数。
  3. Call this stored procedure form your code and pass the parameters you need.从您的代码中调用此存储过程并传递您需要的参数。

     --This is just and example I haven't run this query. DECLARE @platform VARCHAR(20) DECLARE @columnName VARCHAR(20) -- this should be the name from the input DECLARE @columnValue VARCHAR(20)-- DECLARE @typeValue VARCHAR(20) DECLARE @sqlCommand VARCHAR(255) --DO SOME CHECKS TO ENSURE THE COLUMN ACTUALLY EXISTS. --DO NOT JUST RUN WHAT YOU GOT FROM THE INPUT SET @sqlCommand = 'SELECT * FROM GroupTs WHERE G_PLATFORM=' + @platform +'AND G_Type='+@typeValue+'AND'+@columnName+'='+@columnValue EXEC (@sqlCommand)

Here is more info about this https://www.essentialsql.com/build-dynamic-sql-stored-procedure/这是有关此https://www.essentialsql.com/build-dynamic-sql-stored-procedure/ 的更多信息

Thank you so much for your responses.非常感谢您的回复。 I found the following solution to be the most optimal:我发现以下解决方案是最佳的:

I created a few static helper methods that each returned a Lambda Expression.我创建了一些静态辅助方法,每个方法都返回一个 Lambda 表达式。 For example, for ".Contains" I added the following method:例如,对于“.Contains”,我添加了以下方法:

private static Expression<Func<T, bool>> GetContainsExpression<T>(string propertyName, string propertyValue)
    {
        var parameterExp = Expression.Parameter(typeof(T), "type");
        var propertyExp = Expression.Property(parameterExp, propertyName);
        MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
        var someValue = Expression.Constant(propertyValue, typeof(string));
        var containsMethodExp = Expression.Call(propertyExp, method, someValue);

        return Expression.Lambda<Func<T, bool>>(containsMethodExp, parameterExp);
    }

For .StartsWith the following method (similarly):对于 .StartsWith 以下方法(类似):

private static Expression<Func<T, bool>> GetStartsWithExpression<T>(string propertyName, string propertyValue)
    {
        var parameterExp = Expression.Parameter(typeof(T), "type");
        var propertyExp = Expression.Property(parameterExp, propertyName);
        MethodInfo method = typeof(string).GetMethod("StartsWith", new[] { typeof(string) });
        var someValue = Expression.Constant(propertyValue, typeof(string));
        var containsMethodExp = Expression.Call(propertyExp, method, someValue);

        return Expression.Lambda<Func<T, bool>>(containsMethodExp, parameterExp);
    }

And the same goes to ".Equals" and ".EndsWith". “.Equals”和“.EndsWith”也是如此。

Later on, in my click method above (in my question), I added the following lambda expressions: (This one is for .Contains)后来,在我上面的 click 方法中(在我的问题中),我添加了以下 lambda 表达式:(这个用于 .Contains)

if (valCombo.Text.Equals("contains"))
                        {
                            dgvFiltered.DataSource = af.Accounts.Where(GetContainsExpression<Account>(fieldNameCombo.Text, txt1.Text))
                                .Where(x => x.A_Platform.Equals(platformCombo.Text) && x.A_Type.Equals(typeCombo.Text)).ToList();
                        }

This one is for .StartsWith:这是针对 .StartsWith 的:

if (valCombo.Text.Equals("starts with"))
                        {
                            dgvFiltered.DataSource = af.Accounts.Where(GetStartsWithExpression<Account>(fieldNameCombo.Text, txt1.Text))
                                .Where(x => x.A_Platform.Equals(platformCombo.Text) && x.A_Type.Equals(typeCombo.Text)).ToList();
                        }

And so on.等等。 Yes, it does make both the .Wheres in each code sample above an && expression.是的,它确实使每个代码示例中的 .Wheres 都位于 && 表达式之上。 So, to tackle the ||所以,为了解决 || case where one Where is true and the other is not, I added the following if statement above them:在其中一个 Where 为真而另一个不为真的情况下,我在它们上面添加了以下 if 语句:

if (fieldNameCombo.Text == "")
                    {
                        dgvFiltered.DataSource = af.Accounts.Where(x => x.A_Platform.Equals(platformCombo.Text) && x.A_Type.Equals(typeCombo.Text)).ToList();
                    }

Hope this provides clarity on this issue.希望这可以澄清这个问题。 Let me know if you have any questions.如果您有任何问题,请告诉我。

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

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