C# 将表达式放在括号中

26 浏览
0 Comments

C# 将表达式放在括号中

我试图将属性转换为对象,然后转换为字符串(请不要问为什么:D),然后调用一个方法,但由于一个非常简单的原因它无法工作。

让我们来看一下代码:

var containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) });
Expression> parameter = s => s.BirthDate;
var convert = Expression.Convert(Expression.Convert(parameter.Body, typeof(object)), typeof(string));
var condition = Expression.Call(
    convert,
    containsMethod!,
    Expression.Constant("bla bla", typeof(string))
);

我省略了一些不必要的代码。基本上,我试图将任何类型的属性转换为字符串,然后调用Contains方法。但表达式生成了以下表达式:

(string)(object) s.BirthDate.Contains("bla bla")

这明显是错误的语法。我想将s.BirthDate放在括号中。像这样:

((string)(object)s.BirthDate).Contains("bla bla")

0
0 Comments

问题出现的原因是在使用表达式参数时,需要以不同的方式使用表达式参数。解决方法是使用Expression类的Convert方法将属性转换为object类型,然后再将其转换为string类型。接下来,使用Expression类的Call方法创建一个包含Contains方法调用的表达式,并传递给它所需的参数。最后,使用Expression类的Lambda方法创建一个Lambda表达式,并将其传递给Entity Framework的Where方法。

对于那些认为DateTime类型无法转换为string类型的人来说,在这种情况下并不重要。这个表达式不会被编译和执行,而是将被传递给Entity Framework进行分析和转换为SQL语句。重要的是Entity Framework能否将这样的表达式转换为SQL语句,至少在EF版本6中是可以的。

一个示例是,假设有一个名为"Picture"的表,其中有一个名为"CreationTime"的datetime类型的列。下面的查询语句可以正常工作,并生成以下形式的SQL查询语句:

select * from Picture where CAST(CreationTime as nvarchar(max)) LIKE '%12%'

并且可以成功返回匹配的实体(至于在效率上进行这样的查询是否明智是另外一个问题)。

总之,通过使用上述的解决方法,可以在C#中将表达式放在括号中,并将其传递给Entity Framework的Where子句。这样可以实现特定需求的查询,并且能够将查询转换为SQL语句以在数据库中执行。

0
0 Comments

问题的原因是C#中的Expression.Convert方法用于类型转换操作,但是DateTime和string之间没有定义的转换。所以对于标准类型如System.DateTime,无法直接进行类型转换。解决方法是使用ToString方法来获取对象的字符串表示,该方法对于任意对象都是可靠的。

对于使用字符串参数的部分查找,可以使用以下代码:

var toString = typeof(object).GetMethod("ToString");
var contains = typeof(string).GetMethod("Contains", new[] { typeof(string) });
var selector = (Expression>)(e => e.BirthDate);
var testStringParam = Expression.Parameter(typeof(string), "s");
var condition = Expression.Lambda>(
    Expression.Call(
        Expression.Call(
            selector.Body,
            toString        
        ),
        contains,
        new[] { testStringParam }
    ),
    selector.Parameters[0], testStringParam
);

另外,可以使用ExpressionVisitor类来简化代码:

sealed class ReplaceVisitor : ExpressionVisitor
{
    private readonly Expression _from;
    private readonly Expression _to;
    private ReplaceVisitor(Expression from, Expression to)
    {
        _from = from;
        _to = to;
    }
    public override Expression Visit(Expression e)
    {
        if (ReferenceEquals(e, _from))
            return _to;
        return base.Visit(e);
    }
    public static T Execute(T expression, Expression from, Expression to)
        where T : Expression
    {
        var replacer = new ReplaceVisitor(from, to);
        return (T)replacer.Visit(expression);
    }
}

使用ReplaceVisitor类可以简化代码:

Expression> template = (o, s) => o == null ? null : o.ToString().Contains(s);
Expression> selector = e => e.BirthDate;
var pEmployee = selector.Parameters[0];
var pString = template.Parameters[1];
var condition = Expression.Lambda>(
    ReplaceVisitor.Execute(
        template.Body,
        template.Parameters[0],
        selector.Body
    ),
    pEmployee, pString
);

通过使用ReplaceVisitor类,可以在编译时检查模板的正确性,并避免在运行时进行调试。

0