thesverdish
thesverdish

Reputation: 23

eliminate .net lambda expression

I'm assessing a performance issue in my C# .NET web application and have traced the bottleneck to a chained lambda expression. I would like to remove the lambda expression entirely so I can evaluate the performance of each step in the chain however I am relatively new to lambda expressions. Does anyone have any thoughts on how the second lambda express could be refactored into more traditional code so that each step or action can traced?

IEnumerable<OurPerformance> validPerformances = package.TimeFilteredValidPerformances(visitDateAndTime);

IEnumerable<WebPerformance> webPerformances = performanceGroup.RegularNonPassedPerformances
            .Where(performance => validPerformances.Select(perf => perf.PerformanceId).Contains(performance.PerformanceId))                
            .Select(performance =>
                new WebPerformance
                {
                    Date = performance.PerformanceDate.ToJavaScriptDateString(),
                    PerformanceId = performance.PerformanceId,
                    Title = performance.UserFriendlyTitle,
                    ProductionSeasonId = performance.ProductionSeasonId,
                    AvailableCount = performance.AvailableCount
                });

**IEnumerable<WebProduction> webProductions = webPerformances
            .GroupBy(performance => performance.ProductionSeasonId)
            .ToDictionary(grouping => SheddProduction.GetOurProduction(grouping.Key), grouping => grouping.ToList())
            .Select(perfsByProduction =>
                new WebProduction
                {
                    ProductionSeasonId = perfsByProduction.Key.ProductionSeasonNumber,
                    Duration = perfsByProduction.Key.Duration,
                    Title = perfsByProduction.Key.UserFriendlyTitle,
                    Synopsis = perfsByProduction.Key.UserFriendlySynopsis,
                    ThumbnailImage = perfsByProduction.Key.PreviewImage,
                    Performances = perfsByProduction.Value
                });**

Upvotes: 2

Views: 116

Answers (3)

Jan Palas
Jan Palas

Reputation: 1895

Here is lamba-refactoring of your second lambda expression into "traditional" code:

IEnumerable<IGrouping<string, WebProduction>> groupedPerformances 
    = webPerformances.GroupBy(performance => performance.ProductionSeasonId);

var dictionary = new Dictionary<string, List<WebProduction>>();
foreach (IGrouping<string, WebProduction> grouping in groupedPerformances)
{
    var group = new List<WebProduction>();
    foreach (WebProduction webProduction in grouping)
        group.Add(webProduction);

    dictionary.Add(grouping.Key, group);
}

var result = new List<WebProduction>();
foreach (KeyValuePair<string, List<WebProduction>> item in dictionary)
{
    var wp = new WebProduction
    {
        ProductionSeasonId = perfsByProduction.Key.ProductionSeasonNumber,
        Duration = perfsByProduction.Key.Duration,
        Title = perfsByProduction.Key.UserFriendlyTitle,
        Synopsis = perfsByProduction.Key.UserFriendlySynopsis,
        ThumbnailImage = perfsByProduction.Key.PreviewImage,
        Performances = perfsByProduction.Value
    };
    result.Add(wp);
}

I do not know result-type of your SheddProduction.GetOurProduction method, thus I made minor changes, but you can get the gist...

Upvotes: 1

N_A
N_A

Reputation: 19897

Its actually fairly straight-forward to break this up into smaller pieces which are more obviously converted into "traditional" code. Just store each linq expression result in a local variable like this:

var groupedWebPerformances = webPerformances.GroupBy(performance => performance.ProductionSeasonId);
var webPerformancesDictionary = groupedWebPerformances .ToDictionary(grouping => SheddProduction.GetOurProduction(grouping.Key), grouping => grouping.ToList());
IEnumerable<WebProduction> webProductions = webPerformancesDictionary.Select(perfsByProduction =>
                new WebProduction
                {
                    ProductionSeasonId = perfsByProduction.Key.ProductionSeasonNumber,
                    Duration = perfsByProduction.Key.Duration,
                    Title = perfsByProduction.Key.UserFriendlyTitle,
                    Synopsis = perfsByProduction.Key.UserFriendlySynopsis,
                    ThumbnailImage = perfsByProduction.Key.PreviewImage,
                    Performances = perfsByProduction.Value
                });

That being said, your performance problem is that you're using IEnumerable everywhere. IEnumerables might re-evaluate each time they are used (depends on what the underlying type is), so validPerformances is getting re-evaluated once for each item in RegularNonPassedPerformances and each item in webPerformances is not getting evaluated until the whole enumeration is forced to evaluate, so the performance problem appears to be later but is most likely here:

validPerformances.Select(perf => perf.PerformanceId).Contains(performance.PerformanceId)

Make sure you force all of your enumerables to evaluate a single time by doing a ToList on them when they are created like this:

List<OurPerformance> validPerformances = package.TimeFilteredValidPerformances(visitDateAndTime).ToList();

List<WebPerformance> webPerformances = performanceGroup.RegularNonPassedPerformances
            .Where(performance => validPerformances.Select(perf => perf.PerformanceId).Contains(performance.PerformanceId))                
            .Select(performance =>
                new WebPerformance
                {
                    Date = performance.PerformanceDate.ToJavaScriptDateString(),
                    PerformanceId = performance.PerformanceId,
                    Title = performance.UserFriendlyTitle,
                    ProductionSeasonId = performance.ProductionSeasonId,
                    AvailableCount = performance.AvailableCount
                })
             .ToList();

List<WebProduction> webProductions = webPerformances
            .GroupBy(performance => performance.ProductionSeasonId)
            .ToDictionary(grouping => SheddProduction.GetOurProduction(grouping.Key), grouping => grouping.ToList())
            .Select(perfsByProduction =>
                new WebProduction
                {
                    ProductionSeasonId = perfsByProduction.Key.ProductionSeasonNumber,
                    Duration = perfsByProduction.Key.Duration,
                    Title = perfsByProduction.Key.UserFriendlyTitle,
                    Synopsis = perfsByProduction.Key.UserFriendlySynopsis,
                    ThumbnailImage = perfsByProduction.Key.PreviewImage,
                    Performances = perfsByProduction.Value
                })
            .ToList();

Upvotes: 1

Harutyun Imirzyan
Harutyun Imirzyan

Reputation: 464

I can't suggest any algorithm to exclude lambda expressions, but I have another suggestion. Try using List for webPerformances instead of IEnumerable collection. Call ToList() for getting List webPerformances. In this case the second lambda expression will work with list and won't reevaluate the IEnumerable collection.

Upvotes: 2

Related Questions