joshb
joshb

Reputation: 5220

conditionally populating anonymous type member in linq query

I'm having a problem with the following LINQ query. When the nested query (item.Items) has no objects I'm getting the exception "Value cannot be null. Parameter name: source.".

How can I get the inner query to return an empty list to items within the query?

var items = from item in _repository.GetStreamItems()
                        select new
                            {
                                Title = item.Title,
                                Description = item.Description,
                                MetaData = item.MetaData,
                                ItemType = item.ItemType,
                                Items = from subItem in item.Items
                                        select new
                                        {
                                            Title = subItem.Title,
                                            Description = subItem.Description,
                                            MetaData = subItem.MetaData,
                                            ItemType = subItem.ItemType
                                        }
                            };

Here's the same query written using method calls instead of query syntax. Same issue:

var items = _repository.GetStreamItems()
                .Select(x => new { Title = x.Title, Description = x.Description, MetaData = x.MetaData, ItemType = x.ItemType, 
                    Items = x.Items.Select(x2 => new { Title = x2.Title, Description = x2.Description, MetaData = x2.MetaData, ItemType = x2.ItemType, 
                        Items = x2.Items.Select(x3 => new { Title = x3.Title, Description = x3.Description, MetaData = x3.MetaData, ItemType = x3.ItemType }) }) });

Any ideas how to test for or avoid the null item.Items value? I have a feeling it's something simple I'm missing.

Upvotes: 4

Views: 1375

Answers (1)

Dan Abramov
Dan Abramov

Reputation: 268323

Assuming it's LINQ to Objects and single item class name is Item, go with this one:

var items = from item in _repository.GetStreamItems()
                        select new
                        {
                            Title = item.Title,
                            Description = item.Description,
                            MetaData = item.MetaData,
                            ItemType = item.ItemType,
                            Items = from subItem in (item.Items ?? Enumerable.Empty<Item>())
                                    select new
                                    {
                                        Title = subItem.Title,
                                        Description = subItem.Description,
                                        MetaData = subItem.MetaData,
                                        ItemType = subItem.ItemType
                                    }
                        };

?? is called null-coalescing operator and returns the value on the right if the value on the left is null.
In your specific example, we provide an empty sequence instead of null so the code does not crash.

The problem is you can't apply queries to a null object, and it seems like item.Items can be null in your case. A better solution would be to ensure that Items property returns a zero sequence when empty, not null.

If you have no control over the StreamItem class but have to do similar queries in many places, it might pay off to create a “safe” extension method that would return “denullified” items:

public static IEnumerable<Item> SafeGetSubItems(this StreamItem parent)
{
    return parent.Items ?? Enumerable.Empty<Item>();
}

This would allow you to always write:

Items = from subItem in item.SafeGetSubItems()
        select new
        {
            // ...
        }

Upvotes: 6

Related Questions