LINQ to entities中Where子句中的引号转义

本文关键字:转义 Where to entities LINQ 子句 | 更新日期: 2023-09-27 18:16:27

我想知道如何将LINQ中的引号转义到实体。

这是我的环境:实体框架5与Silverlight 5和WCF RIA服务,MySQL 5.6和MySQLConnector 6.5.6。

我有以下查询:

DomainContext.Load<Product>(DomainContext.GetProductQuery()
                                         .Where<Product>(p => p.name.Contains(parameter))
                                         .Take<Product>(30));

如果参数变量包含引号',将引发MySQL语法错误异常。不管使用什么方法(StartWith, Contains),它总是引发一个异常。

使用FilterDescriptorDomainDataSource做同样的事情。

重要提示:对于%或双引号"这样的字符,它不会引发任何异常。此外,如果操作符等于strict,则不会引发任何简单引号异常,如下所示。

DomainDataSource.FilterDescriptors.Add(new FilterDescriptor("productName", FilterOperator.IsEqualTo, SelectedProductName));

DomainContext.Load<Product>(DomainContext.GetProductQuery()
                                         .Where<Product>(p == parameter)
                                         .Take<Product>(30));

插入数据没有任何困难。

任何帮助都将非常感激。谢谢你!

更新:我忘了提到一些事情。

这是我在服务端的方法。

public IQueryable<Product> GetProduct()
{
    return this.ObjectContext.product;
}

我应该如何确保这对SQL注入?我必须写几十行代码来管理过滤器吗?

EDIT:问题已在EF的MySQL提供程序的最后一个版本中解决。

LINQ to entities中Where子句中的引号转义

对于您的确切问题的快速解决方案:

string cleanParameter = parameter.Replace("'", "''")

请看这里的一个更通用的解决方案,它描述了mysql_real_escape_string的c#等价。

string cleanParameter = MySQLEscape(parameter)

上述文章中描述的MySQLEscape:

private static string MySQLEscape(string str)
{
    return Regex.Replace(str, @"['x00'""'b'n'r't'cZ''%_]",
        delegate(Match match)
        {
            string v = match.Value;
            switch (v)
            {
                case "'x00":            // ASCII NUL (0x00) character
                    return "''0";   
                case "'b":              // BACKSPACE character
                    return "''b";
                case "'n":              // NEWLINE (linefeed) character
                    return "''n";
                case "'r":              // CARRIAGE RETURN character
                    return "''r";
                case "'t":              // TAB
                    return "''t";
                case "'u001A":          // Ctrl-Z
                    return "''Z";
                default:
                    return "''" + v;
            }
        });
}

旁注:您的代码听起来可能容易受到SQL注入攻击。(没有更多的背景我无法判断)。本文描述了什么是sql注入攻击以及如何防止它们。

我找到了一个解决这个问题的方法。

首先,我要感谢www.developpez.net的tomlevchamamo提供的帮助。

这是法语版讨论的直接链接http://www.developpez.net/forums/d1349604/services-web/wcf-ria-services-injection-sql/

这是修复此问题的包装器的源代码。

    class MySqlQueryableWrapper<T> : IQueryable<T>
{
    private readonly IQueryable<T> _queryable;
    private readonly IQueryProvider _provider;
    public MySqlQueryableWrapper(IQueryable<T> queryable)
    {
        _queryable = queryable;
        _provider = new MySqlQueryProviderWrapper(queryable.Provider);
    }
    public Type ElementType
    {
        get { return _queryable.ElementType; }
    }
    public Expression Expression
    {
        get { return _queryable.Expression; }
    }
    public IQueryProvider Provider
    {
        get { return _provider; }
    }
    public IEnumerator<T> GetEnumerator()
    {
        return _queryable.GetEnumerator();
    }
    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}
class MySqlQueryProviderWrapper : IQueryProvider
{
    private readonly MySqlExpressionFixer _visitor = new MySqlExpressionFixer();
    private readonly IQueryProvider _provider;
    public MySqlQueryProviderWrapper(IQueryProvider provider)
    {
        _provider = provider;
    }
    public IQueryable CreateQuery(Expression expression)
    {
        return _provider.CreateQuery(_visitor.Visit(expression));
    }
    public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
    {
        return _provider.CreateQuery<TElement>(_visitor.Visit(expression));
    }
    public object Execute(Expression expression)
    {
        return _provider.Execute(_visitor.Visit(expression));
    }
    public TResult Execute<TResult>(Expression expression)
    {
        return _provider.Execute<TResult>(_visitor.Visit(expression));
    }
}
class MySqlExpressionFixer : ExpressionVisitor
{    
    protected override Expression VisitMethodCall(MethodCallExpression node)
    {
        if ((node.Method.Name == "Contains" || node.Method.Name == "StartsWith") &&
            node.Method.DeclaringType == typeof(string) &&
            node.Arguments.Count == 1)
        {
            var c = node.Arguments[0] as ConstantExpression;
            if (c != null)
            {
                string s = c.Value as string;
                if (s != null)
                {
                    s = s.Replace("'", "''");
                    node = Expression.Call(node.Object, node.Method, Expression.Constant(s));
                }
            }
        }
        return base.VisitMethodCall(node);
    }
}

下面是一个例子。

public IQueryable<Product> GetProduct()
{
    return new MySqlQueryableWrapper<Product>(this.ObjectContext.product);
}