Entity Framework通配符和Linq

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.“like” queries in Entity Framework 2.exact and wildcard searching conditional on search term

3.使用RawSql


1
我知道这是一个老问题,但你可以使用这里的答案中的想法:https://dev59.com/2XNA5IYBdhLWcg3wPbOe#42307642。虽然字符串中没有添加通配符的可能性,但你可以构建所需的LIKE查询。 - user4864425
3个回答

2
如果您使用EDMX文件作为实体模型的基础,那么您可以尝试创建一个概念模型函数,然后在SQL中执行LIKE操作。我不确定这是否适用于Oracle。然后您应该能够执行以下操作:
.Where(entity => Like(entity.Name, "Foo%Bar"))

我正在使用Code First而不是EDMX来操作现有的数据库,但我会研究一下,谢谢。 - philreed
我正在努力寻找一种不需要EDMX的方法来完成这个任务,虽然这是个好主意。 - philreed
@philreed 我尝试查找有关使用Code First进行类似操作的信息,但没有找到。我找到的唯一一个参考是来自2011年微软论坛上的一个请求功能,但当时已知不支持该功能。 - Adam Gritt

1
使用 SqlFunctions.PatIndex,代码如下:
.Where(entity => SqlFunctions.PatIndex("Foo%Bar", entity.Name) > 0)

抱歉,刚看到你在使用Oracle,这可能会有所不同,我不确定patindex是否适用于它。 - BlackICE
是的,我刚刚更新了问题,显示我正在使用Oracle,如果没有相应的解决方案,我会感到惊讶。我现在正在研究它,谢谢。 - philreed
我找不到PatIndex的Oracle版本,但这让我想到了string.IndexOfAny(searchTerm.Split('%'))的潜力。需要稍微修改一下,以确保拆分的术语在正确的顺序中。 - philreed
我非常怀疑EF提供者会实现那个。 - BlackICE

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;
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接