Coalescing results in Linq

回眸只為那壹抹淺笑 提交于 2019-12-24 11:19:12

问题


have looked through many posts here on SO, and haven't found any that address this. Just a note that all code presented here is simplified but representative of the real code. I have a data table that describes some properties of coverage plans. The query to bring back the best match looks something like this:

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

This is a small table (a few dozen rows) that gets hit a lot and changes infrequently, so I want to bring it into memory and use Linq to select the data to speed this up.

I have this function which returns the first match exactly as I want it to:

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;
}

My question is: Is there a better way (faster, less code, less repetition) to do this Linq query?

Update: I ended up with this function as a replacement:

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;
}

The DefaultIfEmpty wants an instance instead of an IEnumeration of instances. This lead me to adding the First/FirstOrDefault on the fallback subqueries, and it turns out that DefaultIfEmpty hates it if you give it a null, so I used the null coalescing operater to roll up the fallback levels.

I'm not sure why they don't give you a DefaultIfEmpty that takes an IEnumeration, it would just be this:

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

Actually, I think I'll use that extension method, and have my function be this:

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:


I believe that .Select(x => x); actually does nothing. So that can be removed. You can join your queries using a union function. As for the if no results checks, you could investigate using this function DefaultIfEmpty().

I would also recommend resharper at being helpful with suggestions on optimizing LINQ.

I also think you should abide by DRY principals and not have this line of code:

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

instead replacing it with something like:

x.Equals(coverage,defaultPlanCode,stateCode)

I propose your linq for your method would look like this (make sure you add that equals method optimization as well as this):

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;

}


来源:https://stackoverflow.com/questions/8752209/coalescing-results-in-linq

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!