Объединение результатов в Linq - PullRequest
1 голос
/ 06 января 2012

просмотрели много постов здесь на SO, и не нашли ни одного такого адреса. Просто обратите внимание, что весь код, представленный здесь, является упрощенным, но представляет реальный код. У меня есть таблица данных, которая описывает некоторые свойства планов покрытия. Запрос на возвращение лучшего соответствия выглядит примерно так:

select coalesce
(
(select c.PercentOfCoverageA from CoveragePlans c
where c.coverage = :COVERAGE
and c.plancode = :PLANCODE
and c.statecode = :STATECODE),

(select c.PercentOfCoverageA from CoveragePlans c
where c.coverage = :COVERAGE
and c.plancode = :DEFAULTPLANCODE
and c.statecode = :STATECODE),

(select c.PercentOfCoverageA from CoveragePlans c
where c.coverage = :COVERAGE
and c.plancode = :DEFAULTPLANCODE
and c.statecode = :COUNTRYWIDE)
) as PercentOfCoverageA
from dual

Это небольшая таблица (несколько десятков строк), которая часто попадает и редко меняется, поэтому я хочу перенести ее в память и использовать Linq для выбора данных, чтобы ускорить это.

У меня есть эта функция, которая возвращает первое совпадение именно так, как я хочу:

decimal GetCoveragePercentage(string coverage, string planCode, string stateCode)
{
    IEnumerable<CoveragePlan> result = Coverages
        .Where(x => x.Coverage == coverage && x.PlanCode == planCode && x.StateCode == stateCode)
        .Select(x => x);

    if (!result.Any())
    {
        result = Coverages
            .Where(x => x.Coverage == coverage && x.PlanCode == defaultPlanCode && x.StateCode == stateCode)
            .Select(x => x);
    }

    if (!result.Any())
    {
        result = Coverages
            .Where(x => x.Coverage == coverage && x.PlanCode == defaultPlanCode && x.StateCode == countryWide)
            .Select(x => x);
    }

    return result.First().PercentOfCoverageA;
}

Мой вопрос: есть ли лучший способ (быстрее, меньше кода, меньше повторений) выполнить этот запрос Linq?

Обновление: Я получил эту функцию в качестве замены:

decimal GetCoveragePercentage(string coverage, string planCode, string stateCode)
{
    return Coverages.Where(x => x.Equals(coverage, planCode, stateCode))
        .DefaultIfEmpty(Coverages.Where(x => x.Equals(coverage, defaultPlanCode, stateCode)).FirstOrDefault()
            ?? Coverages.Where(x => x.Equals(coverage, defaultPlanCode, defaultStateCode)).First())
        .First().PercentOfCoverageA;
}

DefaultIfEmpty хочет экземпляр вместо IEnumeration экземпляров. Это привело меня к добавлению First / FirstOrDefault в резервные подзапросы, и оказалось, что DefaultIfEmpty ненавидит его, если вы присваиваете ему значение null, поэтому я использовал оператор объединения нулей для свертывания уровней отката.

Я не уверен, почему они не дают вам DefaultIfEmpty, который принимает IEnumeration, это было бы просто так:

public static IEnumerable<TSource> DefaultIfEmpty<TSource>(this IEnumerable<TSource> source, IEnumerable<TSource> defaultValue)
{
    return (source != null && source.Any()) ? source : defaultValue;
}

На самом деле, я думаю, что я буду использовать этот метод расширения, и моя функция будет такой:

decimal GetCoveragePercentage(string coverage, string planCode, string stateCode)
{
    return Coverages.Where(x => x.Equals(coverage, planCode, stateCode))
        .DefaultIfEmpty(Coverages.Where(x => x.Equals(coverage, defaultPlanCode, stateCode)))
        .DefaultIfEmpty(Coverages.Where(x => x.Equals(coverage, defaultPlanCode, defaultStateCode)))
        .First().PercentOfCoverageA;
}

1 Ответ

2 голосов
/ 06 января 2012

Я считаю, что .Select(x => x); на самом деле ничего не делает. Так что это может быть удалено. Вы можете присоединиться к вашим запросам, используя функцию объединения. Что касается, если нет результатов проверки, вы можете исследовать с помощью этой функции DefaultIfEmpty().

Я бы также порекомендовал resharper помочь с предложениями по оптимизации LINQ.

Я также думаю, что вы должны соблюдать принципы DRY и не иметь эту строку кода:

x.Coverage == coverage && x.PlanCode == defaultPlanCode && x.StateCode == stateCode

вместо того, чтобы заменить его чем-то вроде:

x.Equals(coverage,defaultPlanCode,stateCode)

Я полагаю, что ваш linq для вашего метода будет выглядеть следующим образом (убедитесь, что вы добавляете метод оптимизации, равный и этому):

decimal GetCoveragePercentage(string coverage, string planCode, string stateCode)
{
    return Coverages
        .Where(x => x.Coverage == coverage && x.PlanCode == planCode && x.StateCode == stateCode)
        .DefaultIfEmpty(Coverages.Where(x => x.Coverage == coverage && x.PlanCode == defaultPlanCode && x.StateCode == stateCode))
        .DefaultIfEmpty(Coverages.Where(x => x.Coverage == coverage && x.PlanCode == defaultPlanCode && x.StateCode == countryWide))First().PercentOfCoverageA;

}
...