EtienneT
EtienneT

Reputation: 5396

How to select a variable plus some other fields in Linq to object?

Consider the following linq query:

from r in result
join oUS in onOrderUS on r.ItemGUID equals oUS.ItemGUID into jOnOrderUS
from oUS in jOnOrderUS.DefaultIfEmpty()
let OnOrderUS = oUS != null ? oUS.UnitQty : 0
select (new Func<ItemMinMaxView>(() =>
{
   r.OnOrderUS = OnOrderUS;

   return r;
})).Invoke()

I want to select r from result, but fill a field with the data from oUS. Is there a better way to do this?

Upvotes: 1

Views: 2544

Answers (2)

Enigmativity
Enigmativity

Reputation: 117029

I too would like to see the query split into more logical parts to aid readability. But I would then take advantage of the Reactive Extensions (Rx) to cause the side effect you want (ie the value assignment).

First up I would just create the query like Anthony suggested, but I want to check that your query is correct.

If there are more than one onOrderUS records returned then you'll only assign the last one found to the ItemMinMaxView record.

If there will only ever be zero or one record then your query is fine, but it could be simpler.

Either way, try this query instead:

var query =
    from r in result
    join oUS in onOrderUS on r.ItemGUID equals oUS.ItemGUID into goUSs
    let OnOrderUS = goUSs.Sum(x => x.UnitQty)
    select new
    {
        Result = r,
        OnOrderUS
    };

Then I'd get the final results (with the assignment side effect) like this:

var output = query
    .Do(x => x.Result.OnOrderUS = x.OnOrderUS)
    .Select(x => x.Result)
    .ToArray();

The .Do(...) method is part of Rx and is explicitly there for these kinds of side effects.

You do get a load of good extension methods for IEnumerable<T> in Rx so if you haven't checked it out you should. It's even recently been promoted to a supported Microsoft developer tool.

Upvotes: 1

Anthony Pegram
Anthony Pegram

Reputation: 126804

I'd rather see your functionality split into its constituent parts. The initial query (join, projection, etc.), the mutation, and then return the specific results you need. Such as

var query = from r in result 
            join oUS in onOrderUS on r.ItemGUID equals oUS.ItemGUID into jOnOrderUS 
            from oUS in jOnOrderUS.DefaultIfEmpty() 
            let OnOrderUS = oUS != null ? oUS.UnitQty : 0 
            select new { r, OnOrderUS };

foreach (var item in query)
{
    item.r.OnOrderUS = item.OnOrderUS;
}

return query.Select(item => item.r);
// assumes this is in a method returning IEnumerable<R>

If you do not want to code the foreach, you could write it like this. I don't like mutations from queries, but this will do what you want

Func<R, int, R> mutateR = (r, onOrderUS) => { r.OnOrderUS = onOrderUS; return r; };

var query = from r in result 
            join oUS in onOrderUS on r.ItemGUID equals oUS.ItemGUID into jOnOrderUS 
            from oUS in jOnOrderUS.DefaultIfEmpty() 
            let OnOrderUS = oUS != null ? oUS.UnitQty : 0 
            select mutateR(r, OnOrderUS);

Upvotes: 2

Related Questions