【问题标题】:Clean a column value from an Entity Framework table从实体框架表中清除列值
【发布时间】:2019-01-17 21:08:52
【问题描述】:

我需要使用 linq 在 C# 中清理列的值。

这是一个例子:

var p_name = "michaelscott";
var result = db.tablename.where(x => x.name
        .Replace(" ",'')
        .Replace("$","") 
        == p_name).FirstOrDefault();

此示例仅使用 2 个项目(空格和美元符号)。如果我想添加更多的东西来清理,linq 查询会很大,所以我尝试编写一个方法,但它会抛出一个错误,这是不允许的。然后我尝试编写一个扩展类,它在其中完成了所有替换,但它的错误与使用方法的错误相同。

有没有办法做到这一点,而不必为我要删除的每个字符使用.Replace

【问题讨论】:

  • 在处理 EF 时,您仅限于使用 EF 知道如何转换为 SQL 的方法
  • 比较前是清理数据库列还是处理值?

标签: c# .net linq validation linq-to-entities


【解决方案1】:

在 EF 中运行临时查询以轻松完成您想要做的事情。

var cmd = "UPDATE dbo.TableName SET Name = REPLACE(REPLACE(t.Column, ' ', ''), '$', '')";
    cmd += "FROM Table t";
    cmd += "WHERE t.Name = value;";

context.Database.ExecuteSqlCommand(cmd);

【讨论】:

  • 我也想过这个问题,但问题是我必须为链式替换输入这个巨大的查询,并希望避免这种情况:(
  • Entity Framework 确实不是为使用 SQL 函数的复杂查询而设计的,就像您正在尝试做的那样。这将是最简单的方法。
【解决方案2】:

替换为(我认为)更好的答案。

您可以编写一个方法来生成正确的Expression,以使用ExpressionVisitor 来表示清理后的值。使用它的最简单方法是编写一个新版本的Where,以便类型推断对您有用。

public static class IQueryableExt {
    // body only for LINQ to Objects use
    public static string RemoveAll(this string src, string removeChars) => removeChars.Aggregate(src, (ans,ch) => ans.Replace(ch.ToString(), ""));

    private static Expression CleanUp(this Expression dbFn, string charsToRemove) {
        var toCharE = Expression.Constant(String.Empty);
        var replaceMI = typeof(string).GetMethod("Replace", new[] { typeof(string), typeof(string) });

        var methodBody = dbFn;
        foreach (var ch in charsToRemove)
            methodBody = Expression.Call(methodBody, replaceMI, Expression.Constant(ch.ToString()), toCharE);

        return methodBody;
    }

    /// <summary>
    /// ExpressionVisitor to expand x.RemoveAll("x..z") to x.Replace("x","")...Replace("z","") in
    /// an Expression.
    /// </summary>
    private class RemoveAllVisitor : ExpressionVisitor {
        public override Expression Visit(Expression node) {
            if (node?.NodeType == ExpressionType.Call) {
                var callnode = (MethodCallExpression)node;
                if (callnode.Method.Name == "RemoveAll" && callnode.Method.DeclaringType == typeof(IQueryableExt))
                    if (callnode.Arguments[1] is ConstantExpression ce && ce.Type == typeof(string))
                        return callnode.Arguments[0].CleanUp(ce.Value.ToString());
            }

            return base.Visit(node);
        }
    }

    private static T ExpandRemoveAll<T>(this T orig) where T : Expression => (T)new RemoveAllVisitor().Visit(orig);

    public static IQueryable<T> WhereRemoveAll<T>(this IQueryable<T> src, Expression<Func<T, bool>> predFn) => src.Where(predFn.ExpandRemoveAll());
}

有了扩展代码,使用起来还是比较容易的:

var p_name = "michaelscott";
var result = db.tablename
                 .WhereRemoveAll(x => x.name.RemoveAll("$ ") == p_name)
                 .FirstOrDefault();

更通用的方法可能会使用类似于 LINQKit 的方法来修改查询并自动扩展所有具有某些属性的嵌入方法 - 我在 CodeReview here 上使用这种方法添加了一些代码。

【讨论】:

  • 对此工作感到非常兴奋,但也给了我同样的错误“System.NotSupportedException:'LINQ to Entities 无法识别方法'System.String RemoveAll(System.String, System.String) ' 方法,并且该方法不能翻译成商店表达式。 "
  • 这里是我如何使用它 var easypeasy = db.tableName.WhereRemoveAll(x => (x.FirstName.RemoveAll(charstoremove) == firstname.RemoveAll(charstoremove) && x.LastName.RemoveAll( charstoremove) == lastname.RemoveAll(charstoremove)) ).Select(x => new Models.Record { Firstname = x.FirstName, Lastname = x.LastName, PhoneNumber = x.PhoneNumber, EmailAddress = x.EmailAddress }).ToList ();
【解决方案3】:

您可以使用正则表达式来捕获您想要替换的所有字符

 var result = db.tablename(x => 
                Regex.Replace(x, @"[^\w]", "")
          == p_name).FirstOrDefault();

上面的表达式将删除所有不是字母或数字的字符

【讨论】:

  • 试过了(在表名之后添加 Where ),它给出了同样的错误:( linq 无法将其转换为 sql。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多