3

是否可以构造一个包含通配符的有效 Linq 查询?

我已经看到了这个问题的各种答案,建议使用:

.Where(entity => entity.Name.Contains("FooBar"))
.Where(entity => entity.Name.EndsWith("Bar")) 
.Where(entity => entity.Name.StartsWith("Foo"))

或构建 RawSql:

var commandText =
    @"SELECT field
    FROM     table
    WHERE    field LIKE @search";

var query = new ObjectQuery<Profile>(commandText, context);
query.Parameters.Add(new ObjectParameter("search", wildcardSearch));

如果通配符不在字符串的开头或结尾,例如searchTerm = "Foo%Bar".

使用 RawSql 的第二个解决方案并不适合我,而且感觉像是一种廉价的出路。但它确实有效。

我尚未尝试的第三个选项是创建可以解析搜索词并构建有效的 Linq 查询的东西,这是@Slauma 在下面的链接 2 中尝试的东西。但是,如果通配符不在搜索词的开头或结尾,这仍然不起作用。

所以问题是:是否可以构造一个包含通配符的有效 Linq 查询?

编辑:值得一提的是,在这种情况下,我使用的是 Oracle 数据访问组件 (ODAC/ODP),但我认为在这种情况下并没有太大的不同。

链接:

1. Entity Framework 中的“like”查询

2.以搜索词为条件的精确和通配符搜索

3.使用RawSql

4

3 回答 3

2

如果您使用 EDMX 文件作为实体模型的基础,那么也许您可以尝试创建一个概念模型函数,然后在 SQL 中执行 LIKE。我不确定这是否适用于 Oracle。然后,您应该能够执行以下操作:

.Where(entity => Like(entity.Name, "Foo%Bar"))
于 2013-07-18T13:05:23.983 回答
0

使用 SqlFunctions.PatIndex,它看起来像这样:

.Where(entity => SqlFunctions.PatIndex("Foo%Bar", entity.Name) > 0)
于 2013-07-18T12:25:07.293 回答
0

我为 Oracle 找到了一个很好的解决方案。这是这里另一个答案的一部分,部分是我写的。

    public static class LinqExtensions
{
    public static IQueryable<T> WhereLike<T>(this IQueryable<T> source, String Name, String value)
    {
        Type model = typeof(T);
        ParameterExpression param = Expression.Parameter(typeof(T), "m");
        PropertyInfo key = model.GetProperty(Name);
        MemberExpression lhs = Expression.MakeMemberAccess(param, key);
        Expression<Func<T, String>> lambda = Expression.Lambda<Func<T, String>>(lhs, param);

        return source.Where(BuildLikeExpression(lambda, value));
    }
    public static IQueryable<T> WhereLike<T>(this IQueryable<T> source, Expression<Func<T, String>> valueSelector, String value)
    {
        return source.Where(BuildLikeExpression(valueSelector, value));
    }
    public static Expression<Func<T, Boolean>> BuildLikeExpression<T>(Expression<Func<T, String>> valueSelector, String value)
    {
        if (valueSelector == null)
            throw new ArgumentNullException("valueSelector");
        value = value.Replace("*", "%");        // this allows us to use '%' or '*' for our wildcard
        if (value.Trim('%').Contains("%"))
        {
            Expression myBody = null;
            ParsedLike myParse = Parse(value);
            Type stringType = typeof(String);
            if(myParse.startwith!= null)
            {
                myBody = Expression.Call(valueSelector.Body, stringType.GetMethod("StartsWith", new Type[] { stringType }), Expression.Constant(myParse.startwith));
            }
            foreach (String contains in myParse.contains)
            {
                if (myBody == null)
                {
                    myBody = Expression.Call(valueSelector.Body, stringType.GetMethod("Contains", new Type[] { stringType }), Expression.Constant(contains));
                }
                else
                {
                    Expression myInner = Expression.Call(valueSelector.Body, stringType.GetMethod("Contains", new Type[] { stringType }), Expression.Constant(contains));
                    myBody = Expression.And(myBody, myInner);
                }
            }
            if (myParse.endwith != null)
            {
                if (myBody == null)
                {
                    myBody = Expression.Call(valueSelector.Body, stringType.GetMethod("EndsWith", new Type[] { stringType }), Expression.Constant(myParse.endwith));
                }
                else
                {
                    Expression myInner = Expression.Call(valueSelector.Body, stringType.GetMethod("EndsWith", new Type[] { stringType }), Expression.Constant(myParse.endwith));
                    myBody = Expression.And(myBody, myInner);
                }
            }
            return Expression.Lambda<Func<T, Boolean>>(myBody, valueSelector.Parameters.Single());
        }
        else
        {
            Expression myBody = Expression.Call(valueSelector.Body, GetLikeMethod(value), Expression.Constant(value.Trim('%')));
            return Expression.Lambda<Func<T, Boolean>>(myBody, valueSelector.Parameters.Single());
        }
    }
    private static MethodInfo GetLikeMethod(String value)
    {
        Type stringType = typeof(String);

        if (value.EndsWith("%") && value.StartsWith("%"))
        {
            return stringType.GetMethod("Contains", new Type[] { stringType });
        }
        else if (value.EndsWith("%"))
        {
            return stringType.GetMethod("StartsWith", new Type[] { stringType });
        }
        else
        {
            return stringType.GetMethod("EndsWith", new Type[] { stringType });
        }
    }
    private class ParsedLike
    {
        public String startwith { get; set; }
        public String endwith { get; set; }
        public String[] contains { get; set; }
    }
    private static ParsedLike Parse(String inValue)
    {
        ParsedLike myParse = new ParsedLike();
        String work = inValue;
        Int32 loc;
        if (!work.StartsWith("%"))
        {
            work = work.TrimStart('%');
            loc = work.IndexOf("%");
            myParse.startwith = work.Substring(0, loc);
            work = work.Substring(loc + 1);
        }
        if (!work.EndsWith("%"))
        {
            loc = work.LastIndexOf('%');
            myParse.endwith = work.Substring(loc + 1);
            if (loc == -1)
                work = String.Empty;
            else
                work = work.Substring(0, loc);
        }
        myParse.contains = work.Split(new[] { '%' }, StringSplitOptions.RemoveEmptyEntries);
        return myParse;
    }
}
于 2014-11-26T16:08:46.380 回答