Условные Запросы Linq

Мы работаем над просмотром журнала. Использование будет иметь возможность фильтровать по пользователю, серьезности и т.д. В Sql днях я бы добавил к строке запроса, но я хочу сделать это с Linq. Как можно условно добавить предложения where?

12 ответов

  1. Просто используйте оператор s && C#:

    var items = dc.Users.Where(l => l.Date == DateTime.Today && l.Severity == "Critical")
    

    Edit: Ну, надо читать более внимательно. Вы хотели знать, как условно добавить дополнительные предложения. В таком случае, я понятия не имею. 🙂 То, что я, вероятно, сделаю, это просто подготовлю несколько запросов и выполню правильный, в зависимости от того, что мне нужно.

  2. если вы хотите фильтровать только если определенные критерии пройдены, сделайте что-то вроде этого

    var logs = from log in context.Logs
               select log;
    
    if (filterBySeverity)
        logs = logs.Where(p => p.Severity == severity);
    
    if (filterByUser)
        logs = logs.Where(p => p.User == user);
    

    Это позволит вашему дереву выражений быть именно тем, что вы хотите. Таким образом, созданный SQL будет именно то, что вам нужно, и ничего меньше.

  3. Можно использовать внешний метод:

    var results =
        from rec in GetSomeRecs()
        where ConditionalCheck(rec)
        select rec;
    
    ...
    
    bool ConditionalCheck( typeofRec input ) {
        ...
    }
    

    Это будет работать, но не может быть разбито на деревья выражений, что означает, что Linq to SQL будет запускать код проверки для каждой записи.

    Альтернативно:

    var results =
        from rec in GetSomeRecs()
        where 
            (!filterBySeverity || rec.Severity == severity) &&
            (!filterByUser|| rec.User == user)
        select rec;
    

    Это может работать в деревьях выражений, что означает оптимизацию Linq to SQL.

  4. Ну, я думал, что вы можете поместить условия фильтра в общий список предикатов:

        var list = new List<string> { "me", "you", "meyou", "mow" };
    
        var predicates = new List<Predicate<string>>();
    
        predicates.Add(i => i.Contains("me"));
        predicates.Add(i => i.EndsWith("w"));
    
        var results = new List<string>();
    
        foreach (var p in predicates)
            results.AddRange(from i in list where p.Invoke(i) select i);               
    

    Это приводит к списку, содержащему «me», «meyou»и » mow».

    Вы можете оптимизировать это, выполнив foreach с предикатами в совершенно другой функции, которая ORs все предикаты.

  5. Это не самая красивая вещь, но вы можете использовать лямбда-выражение и передать свои условия необязательно. В TSQL я делаю много следующего, чтобы сделать параметры необязательными:

    Где Field = @FieldVar или @FieldVar равно NULL

    Вы можете повторить тот же стиль со следующим лямбда (пример проверки подлинности):

    MyDataContext db = новый MyDataContext();

    void RunQuery (string param1, string param2, int? param3){

    Func checkUser = user =>

    ((param1.Length > 0)? пользователь.Param1 = = param1 : 1 == 1) &&

    ((param2.Length > 0)? пользователь.Param2 = = param2 : 1 == 1) &&

    ((param3 != null)? пользователь.Param3 = = param3 : 1 == 1);

    Пользователь foundUser = db.Пользователи.SingleOrDefault (checkUser);

    }

  6. Когда дело доходит до условного linq, я очень люблю фильтры и паттерны труб.
    http://blog.wekeroad.com/mvc-storefront/mvcstore-part-3/

    В основном вы создаете метод расширения для каждого случая фильтра, который принимает IQueryable и параметр.

    public static IQueryable<Type> HasID(this IQueryable<Type> query, long? id)
    {
        return id.HasValue ? query.Where(o => i.ID.Equals(id.Value)) : query;
    }
    
  7. Другой вариант — использовать что-то вроде PredicateBuilder, обсуждаемого здесь .
    Это позволяет писать код, как показано ниже:

    var newKids  = Product.ContainsInDescription ("BlackBerry", "iPhone");
    
    var classics = Product.ContainsInDescription ("Nokia", "Ericsson")
                      .And (Product.IsSelling());
    
    var query = from p in Data.Products.Where (newKids.Or (classics))
                select p;
    

    Обратите внимание, что у меня есть только это для работы с Linq 2 SQL. EntityFramework не реализует выражение.Invoke, который требуется для работы этого метода. У меня есть вопрос по этому вопросу здесь .

  8. Недавно у меня было подобное требование, и в конце концов я нашел это в MSDN.
    Образцы CSharp для Visual Studio 2008

    Классы, включенные в образец DynamicQuery загрузки, позволяют создавать динамические запросы во время выполнения в следующем формате:

    var query =
    db.Customers.
    Where("City = @0 and Orders.Count >= @1", "London", 10).
    OrderBy("CompanyName").
    Select("new(CompanyName as Name, Phone)");
    

    С помощью этого можно динамически построить строку запроса во время выполнения и передать ее в метод Where() :

    string dynamicQueryString = "City = \"London\" and Order.Count >= 10"; 
    var q = from c in db.Customers.Where(queryString, null)
            orderby c.CompanyName
            select c;
    
  9. Я закончил использовать ответ, похожий на Daren, но с интерфейсом IQueryable:

    IQueryable<Log> matches = m_Locator.Logs;
    
    // Users filter
    if (usersFilter)
        matches = matches.Where(l => l.UserName == comboBoxUsers.Text);
    
     // Severity filter
     if (severityFilter)
         matches = matches.Where(l => l.Severity == comboBoxSeverity.Text);
    
     Logs = (from log in matches
             orderby log.EventTime descending
             select log).ToList();
    

    Это создает запрос перед попаданием в базу данных. Команда не будет выполняться до .ToList () в конце.

  10. Если необходимо отфильтровать базу по списку / массиву, используйте следующее:

        public List<Data> GetData(List<string> Numbers, List<string> Letters)
        {
            if (Numbers == null)
                Numbers = new List<string>();
    
            if (Letters == null)
                Letters = new List<string>();
    
            var q = from d in database.table
                    where (Numbers.Count == 0 || Numbers.Contains(d.Number))
                    where (Letters.Count == 0 || Letters.Contains(d.Letter))
                    select new Data
                    {
                        Number = d.Number,
                        Letter = d.Letter,
                    };
            return q.ToList();
    
        }
    
  11. Делающий это:

    bool lastNameSearch = true/false; // depending if they want to search by last name,
    

    имея это в whereзаявлении:

    where (lastNameSearch && name.LastNameSearch == "smith")
    

    означает, что при создании окончательного запроса if lastNameSearchis falseв запросе будет полностью опущен любой SQL для поиска фамилии.

  12. Я решил это с помощью метода расширения, чтобы LINQ был условно включен в середине свободного выражения. Это устраняет необходимость разбивать выражение На ifоператоры.

    .If() метод расширения:

    public static IQueryable<TSource> If<TSource>(
            this IQueryable<TSource> source,
            bool condition,
            Func<IQueryable<TSource>, IQueryable<TSource>> branch)
        {
            return condition ? source : branch(source);
        }
    

    Это позволяет сделать это:

    return context.Logs
         .If(filterBySeverity, q => q.Where(p => p.Severity == severity))
         .If(filterByUser, q => q.Where(p => p.User == user))
         .ToList();
    

    Вот также IEnumerable<T>версия, которая будет обрабатывать большинство других выражений LINQ:

    public static IEnumerable<TSource> If<TSource>(
        this IEnumerable<TSource> source,
        bool condition,
        Func<IEnumerable<TSource>, IEnumerable<TSource>> branch)
        {
            return condition ? source : branch(source);
        }