Добавление подкачки и фильтрации к типичному шаблону спецификации Linq?

У меня есть приложение ASP.NET MVC2, в котором интенсивно используются сетки. Я хотел бы посмотреть, есть ли способ добавить эффективную подкачку и фильтрацию к типичному шаблону спецификации.

По сути, вызов, с которого все начинается, выглядит так:

PatientByUserIdSpecification spc = new PatientByUserIdSpecification(userId);
return this.patientRepository.FindAll(spc).ToList();

Теперь я знаю, что могу сделать что-то подобное ниже и получить подмножество строк в зависимости от настроек сетки для разбиения по страницам и фильтрации:

return this.patientRepository.FindAll(spc).OrderBy(a => a.Id).Skip(start).Take(limit).ToList();

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

Код LinqRepository в основном:

public IQueryable<T> FindAll(ILinqSpecification<T> specification)
{
    return specification.SatisfyingElementsFrom(this.Session.Linq<T>());
}

public virtual IQueryable<TResult> SatisfyingElementsFrom(IQueryable<T> candidates)
{
    if (this.MatchingCriteria != null)
    {
        return candidates.Where(this.MatchingCriteria).ToList().ConvertAll(this.ResultMap).AsQueryable();
    }

    return candidates.ToList().ConvertAll(this.ResultMap).AsQueryable();
}

И PatientByUserIdSpecification, например, имеет MatchingCriteria:

public override Expression<Func<Patient, bool>> MatchingCriteria
{
    get { return p => p.Cases.Any(c => c.CaseUsers.Any(x => (x.User.Id == this.userId))); }
}

Я думал, что это сделает ниже, но запрос все еще слишком широк.

public virtual IQueryable<TResult> SatisfyingElementsFrom(IQueryable<T> candidates, int start, int limit, string sort, string dir)
{
    if (this.MatchingCriteria != null)
    {
    return candidates.Where(this.MatchingCriteria).Skip(start).Take(limit).ToList().ConvertAll(this.ResultMap).AsQueryable();
    }

    return candidates.ToList().ConvertAll(this.ResultMap).AsQueryable();
}

Могу ли я и как мне настроить возможность генерировать лучший SQL?


person alphadogg    schedule 23.12.2010    source источник
comment
Я не знаю о NHibernate, но обычный Linq2SQL реализует Skip() и Take() в результирующем SQL-запросе.   -  person mmix    schedule 15.03.2011
comment
Что с вызовом ToList() для ваших результатов? Если вы вызываете это, ваш запрос LINQ преобразуется в SQL, и запрашивается ваша база данных. Так почему же тогда вы возвращаетесь к IQueryable‹T›?   -  person mikesigs    schedule 16.03.2011


Ответы (1)


Вызовы .ToList() в следующем коде вызывают выполнение запроса до применения Dynamic LINQ:

public virtual IQueryable<TResult> SatisfyingElementsFrom(IQueryable<T> candidates) 
{
    if (this.MatchingCriteria != null)
    {
        return candidates.Where(this.MatchingCriteria).ToList().ConvertAll(this.ResultMap).AsQueryable();
    }
    return candidates.ToList().ConvertAll(this.ResultMap).AsQueryable(); 
} 

Вам нужно будет провести рефакторинг, чтобы удалить их, и запустить Dynamic LINQ для схемы объектов SQL, чтобы иметь возможность фильтровать на стороне сервера.

person timherby    schedule 17.03.2011