Search code examples
c#linqprojection

Any way to project "original plus a few changes" with a LINQ query?


I have code that is building up a collection of objects. I'm trying to reduce the number of .ToList() calls I make, so I'm trying to keep it as IEnumerable<T> for as long as possible.

I have it almost finished except for two properties which need to be set to a value which is passed into the calling method:

private IEnumerable<X> BuildCollection(int setMe){
    IEnumerable<Y> fromService = CallService();
    IEnumerable<X> mapped = Map(fromService);
    IEnumerable<X> filteredBySomething = FilterBySomething(mapped);

    IEnumerable<X> sorted = filteredBySomething
                                .OrderBy(x=>x.Property1)
                                .ThenBy(x=>x.Property2);

    // Here's the problem: return "sorted", but with each "Property3"
    //  and "Property4" set to "setMe". I don't want to do:

    var sortedList = sorted.ToList();
    sortedList.ForEach(s=>
    {
        s.Property3 = setMe;
        s.Property4 = setMe;
    };

    return sortedList;
}

If one could use sort of a wildcard in a select, then I could do something like:

return from f in filteredBySomething
    order by f.Property1, f.Property2
    select new {
        f.*,
        f.Property3 = setMe,
        f.Property4 = setMe
    };

That is, I would like to stream back the sorted objects, but with Property3 and Property4 set to the passed-in value.

Is there an elegant way to do this?

P.S. I don't think it matters, but the collection will eventually be sent to an ASP.NET view as a viewmodel. Clearly, .ToList() may have to be called before the View gets it, but I'd like that to be the only time.

P.P.S. I should have said that type X has about 30 properties! using

select new {
    x.Property1,
    x.Property2,
    Property3 = setMe,
    Property4 = setme,
    // ...
}

would not be useful, as the ... would be another 26 properties.


Solution

  • Here's what I wound up with:

    private IEnumerable<X> BuildCollection(int setMe){
        IEnumerable<Y> fromService = CallService();
        IEnumerable<X> mapped = Map(fromService);
        IEnumerable<X> filteredBySomething = FilterBySomething(mapped);
    
        IEnumerable<X> sorted = filteredBySomething
                                    .OrderBy(x=>x.Property1)
                                    .ThenBy(x=>x.Property2);
        // The method already returns IEnumerable<X> - make it an iterator    
        foreach (var x in sorted)
        {
            x.Property3 = setMe;
            x.Property4 = setMe;
            yield return x;
        }
    }