Возможны ли динамические-где-пункты Linq-To-Sql даже в Framework 3.5? - PullRequest
5 голосов
/ 16 ноября 2011

ОБНОВЛЕНИЕ: теперь работает
Я смог наконец закончить это. Рабочий пример подробно описан в ответе ниже (который я смогу уточнить через 2 дня).


Все ниже здесь было частью первоначального вопроса

В течение последних 3 дней я пытался создать динамическое предложение where для DBML DataContext , используя примеры кода из вопросов, размещенных здесь. и из других источников , а также ... ни один не работал !

По указанным ниже причинам я начинаю задумываться, возможно ли это вообще использовать в Framework 3.5:

  1. Predicate Builder отмечает Framework 4.0 на своем сайте.
  2. Некоторые ответы здесь говорят о эквивалентных Invoke версиях в 4.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;
}

Ошибка другой версии # 2:
И генерирует эту ошибку: «Метод« Boolean Like (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;
}

Ответы [ 3 ]

2 голосов
/ 16 ноября 2011

Вы пытались создать запрос самостоятельно, используя только классы Exression? Здесь не должно быть особых проблем. Это на самом деле относительно легко учиться. Вы можете написать пример запроса, а затем в отладке посмотреть, как он составлен:

Expression<Func<string, bool>> exp = (s) => s.Contains("your query");

Затем просто посмотрите на переменную exp в часах, и вы увидите структуру. Этот конкретный пример должен быть составлен так:

Expression constant = Expression.Constant("your query");
Expression p = Expression.Param(typeof(string);
Expression contains = Expression.Call(p, "Contains", constant);
Expression<Func<string, bool>> lambda = Expression.Lamba(contains, p);
//  Now you can send this to your ORM
0 голосов
/ 17 ноября 2011

ЭТО ПРАВИЛЬНЫЙ ОТВЕТ
Вот рабочая версия для тех, кому это нужно. Вопрос был в комбинации вещей. Первая из которых была следующая строка была установлена ​​на 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;
}
0 голосов
/ 16 ноября 2011

Насколько я могу вам сказать, я использовал LinqKit и PredicateBuilder еще в начале 2010 года с .Net 3.5, EF 1.0 и EF Poco Adapter.Тогда LinqKit был скомпилирован для Net 3.5

Возможно, если вы спросите автора (Албахари), он может отправить вам (или опубликовать на сайте) версию 3.5.У меня его больше нет, потому что он находится в проектах на моем старом рабочем месте, и у меня нет к ним доступа.

В качестве примечания, я чувствую, что ваша боль вынуждена работать с 3.5 после почти 2лет существования .Net 4.

...