在Framework 3.5中,Linq-To-Sql动态Where子句是否可能?

Pri*_*ERO 5 c# where-clause .net-3.5 linq-to-sql

更新:现在正在工作
我终于完成了它.下面的答案中详述了一个工作示例(我将在2天内进行标记).


以下所有内容都是原始问题的一部分

在过去的3天里,我一直在尝试使用来自此处和其他来源的问题的代码示例在DBML DataContext上构建一个dynamic-where子句...... 没有一个有效!

由于以下原因,我开始怀疑在Framework 3.5下是否可能使用它:

  1. Predicate Builder在他们的网站上注意到Framework 4.0.
  2. 这里的一些答案谈论Invoke4.0中的等价版本(所以我在这里有一些希望).
  3. ......我可以继续,但你明白了.

我真的很茫然,似乎在"抓住弦乐"......我需要一些关于如何处理这个问题的合理建议.

原始版本有一些成功,但只有在以下情况:
我只有成功的"成功"数据出现(所有6178行),但没有WHERE CLAUSE应用.这可以通过缺乏任何WHERE CLAUSE适用于SQL发现的证据来证明dataContext.GetCommand(query).CommandText.

其他版本#1失败:
并生成此错误:"方法'System.Object DynamicInvoke(System.Object [])'没有支持的SQL转换."

// VERSION 1:
public static class PredicateBuilder
{
    public static Expression<Func<T, bool>> True<T>() { return f => true; }
    public static Expression<Func<T, bool>> False<T>() { return f => false; }

    public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
    {
        var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
        return Expression.Lambda<Func<T, bool>>(Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters);
    }
    public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
    {
        var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
        return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(expr1.Body, invokedExpr), expr1.Parameters);
    }
    public static Expression<Func<T, bool>> StringLike<T>(Expression<Func<T, string>> selector, string pattern)
    {
        var predicate = PredicateBuilder.True<T>();
        var parts = pattern.Split('%');
        if (parts.Length == 1) // not '%' sign
        {
            predicate = predicate.And(s => selector.Compile()(s) == pattern);
        }
        else
        {
            for (int i = 0; i < parts.Length; i++)
            {
                string p = parts[i];
                if (p.Length > 0)
                {
                    if (i == 0)
                    {
                        predicate = predicate.And(s => selector.Compile()(s).StartsWith(p));
                    }
                    else if (i == parts.Length - 1)
                    {
                        predicate = predicate.And(s => selector.Compile()(s).EndsWith(p));
                    }
                    else
                    {
                        predicate = predicate.And(s => selector.Compile()(s).Contains(p));
                    }
                }
            }
        }
        return predicate;
    }
}
// VERSION 1:
public List<QuickFindResult> QueryDocuments(string searchText, string customerSiteId, List<int> filterIds)
{
    var where = PredicateBuilder.True<vw_QuickFindResult>();

    var searches = new List<String>(searchText.Split(' '));
    searches.ForEach(productName =>
    {
        string like = productName.Replace('"', '%')
                                 .Replace('*', '%');

        where = PredicateBuilder.StringLike<vw_QuickFindResult>(x => x.DocumentName, like);
    });


    var results = DocumentCollectionService.ListQuickFind(where, null);

    // Do other stuff here...

    return results;
}
// VERSION 1:
public static List<vw_QuickFindResult> ListQuickFind(Expression<Func<vw_QuickFindResult, bool>> where, Expression<Func<vw_QuickFindResult, bool>> orderBy)
{
    var connectionString = GetConnectionString(ES_DOCUMENTS_CONNECTION_NAME);
    List<vw_QuickFindResult> results = null;

    using (HostingEnvironment.Impersonate())
    {
        using (var dataContext = new ES_DocumentsDataContext(connectionString))
        {
            IQueryable<vw_QuickFindResult> query = dataContext.vw_QuickFindResults;
            query = query.Where(where);

            results = query.ToList();
        }
    }

    return results;
}
Run Code Online (Sandbox Code Playgroud)

其他版本#2失败:
并生成此错误:"方法"布尔类似(System.String,System.String)'不能在客户端上使用;它仅用于转换为SQL."

// VERSION 2:
public List<QuickFindResult> QueryDocuments(string searchText, string customerSiteId, List<int> filterIds)
{
    Func<vw_QuickFindResult, bool> where = null;
    Func<string, Func<vw_QuickFindResult, bool>> buildKeywordPredicate = like => x => SqlMethods.Like(x.DocumentName, like);
    Func<Func<vw_QuickFindResult, bool>, Func<vw_QuickFindResult, bool>, Func<vw_QuickFindResult, bool>> buildOrPredicate = (pred1, pred2) => x => pred1(x) || pred2(x);

    // Build LIKE Clause for the WHERE
    var searches = new List<String>(searchText.Split(' '));
    searches.ForEach(productName =>
    {
        string like = productName.Replace('"', '%')
                                 .Replace('*', '%');

        where = (where == null) ? buildKeywordPredicate(like) : buildOrPredicate(where, buildKeywordPredicate(like));
    });

    var results = DocumentCollectionService.ListQuickFind(where, null);

    // Do other stuff here...

    return results;
}
// VERSION 2:
public static List<vw_QuickFindResult> ListQuickFind(Expression<Func<vw_QuickFindResult, bool>> where, Expression<Func<vw_QuickFindResult, bool>> orderBy)
{
    var connectionString = GetConnectionString(ES_DOCUMENTS_CONNECTION_NAME);
    List<vw_QuickFindResult> results = null;

    using (HostingEnvironment.Impersonate())
    {
        using (var dataContext = new ES_DocumentsDataContext(connectionString))
        {
            var query = dataContext.vw_QuickFindResults.AsEnumerable();
            query = query.Where(where);

            results = query.ToList();
        }
    }

    return results;
}
Run Code Online (Sandbox Code Playgroud)

Pri*_*ERO 0

这是正确的答案
这是为需要它的人提供的工作版本。这个问题是多种因素的结合。其中第一行设置为True:

var where = PredicateBuilder.True<vw_QuickFindResult>();

它应该是False...

var where = PredicateBuilder.False<vw_QuickFindResult>();

我不知道为什么......但还需要其他改变。

public static class PredicateBuilder
{
    public static Expression<Func<T, bool>> True<T>() { return f => true; }
    public static Expression<Func<T, bool>> False<T>() { return f => false; }

    public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
    {
        var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
        return Expression.Lambda<Func<T, bool>>(Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters);
    }
    public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
    {
        var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
        return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(expr1.Body, invokedExpr), expr1.Parameters);
    }
}
public List<QuickFindResult> QueryDocuments(string searchText, string customerSiteId, List<int> filterIds)
{
    var wildCards = new string[] { "*", "\"" };
    var where = PredicateBuilder.False<vw_QuickFindResult>();
    var searches = new List<String>(searchText.Split(' ')); // TODO: <-- If more complex searches are needed we'll have to use RegularExpressions

    // SEARCH TEXT - WHERE Clause
    searches.ForEach(productName =>
    {
        Boolean hasWildCards = (productName.IndexOfAny(new char[] { '"', '*' }) != -1);
        if (hasWildCards)
        {
            Int32 length = productName.Length;
            if (length > 1)
            {
                string like = productName.Replace("%", "")
                                         .Replace("*", "");

                string first = productName.Substring(0, 1);
                string last = productName.Substring(length - 1);

                // Contains
                if (wildCards.Contains(first) && wildCards.Contains(last))
                    where = where.Or(p => p.DocumentName.Contains(like) ||
                                         p.DocumentTitle.Contains(like));

                // EndsWith
                else if (wildCards.Contains(first))
                    where = where.Or(p => p.DocumentName.EndsWith(like) ||
                                          p.DocumentTitle.EndsWith(like));

                // StartsWith
                else if (wildCards.Contains(last))
                    where = where.Or(p => p.DocumentName.StartsWith(like) ||
                                          p.DocumentTitle.StartsWith(like));

                // Contains (default)
                else
                    where = where.Or(p => p.DocumentName.Contains(like) ||
                                          p.DocumentTitle.Contains(like));
            }
            else // Can only perform a "contains"
                where = where.Or(p => p.DocumentName.Contains(productName) ||
                                             p.DocumentTitle.Contains(productName));
        }
        else // Can only perform a "contains"
            where = where.Or(p => p.DocumentName.Contains(productName) ||
                                         p.DocumentTitle.Contains(productName));
    });

    // FILTER IDS - WHERE Clause
    var filters = GetAllFilters().Where(x => filterIds.Contains(x.Id)).ToList();
    filters.ForEach(filter =>
    {
        if (!filter.IsSection)
            where = where.And(x => x.FilterName == filter.Name);
    });

    var dataSource = DocumentCollectionService.ListQuickFind(where);
    var collection = new List<QuickFindResult>();

    // Other UNRELATED stuff happens here...

    return collection;
}
public static List<vw_QuickFindResult> ListQuickFind(Expression<Func<vw_QuickFindResult, bool>> where)
{
    var connectionString = GetConnectionString(ES_DOCUMENTS_CONNECTION_NAME);
    List<vw_QuickFindResult> results = null;

    using (HostingEnvironment.Impersonate())
    {
        using (var dataContext = new ES_DocumentsDataContext(connectionString))
        {
            var query = dataContext.vw_QuickFindResults.Where(where).OrderBy(x => x.DocumentName).OrderBy(x => x.DocumentTitle);
            results = query.ToList();
        }
    }

    return results;
}
Run Code Online (Sandbox Code Playgroud)