Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I initialize an empty list within an Entity Framework query?

I've added a List field to my business models. It's not yet stored in the database, and I was hoping to map it temporarily with something like the following:

return MyContext.Foos.Select(foo=> new Foo
    {
        Id = foo.Id,
        Name = foo.Name,
        RequiredFeatures = new List<string>()
    }).ToList();

However, Entity Framework complains that it can't instantiate a new list inside a LINQ to Entities query:

A type that implements IEnumerable 'System.Collections.Generic.List`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]' cannot be initialized in a LINQ to Entities query.

Basically, List<string> is a complex type, and although it contains a default constructor, the constructor requires some initialization code to be run before the list can be used. Entity Framework just doesn't understand how to generate an empty list from the SQL side of things.

I also tried to achieve the same result with an empty array:

return MyContext.Foos
    .Select(foo=> new Foo
    {
        Id = foo.Id,
        Name = foo.Name,
        RequiredFeatures = new string[0]
    }).ToList();

The LINQ expression node type 'NewArrayBounds' is not supported in LINQ to Entities.

Again, the expression is not supported by Entity Framework, even though it is much simpler. So I tried the simplest expression of all, for a new array:

return MyContext.Foos
    .Select(foo=> new Foo
    {
        Id = foo.Id,
        Name = foo.Name,
        RequiredFeatures = { }
    }).ToList();

In constructors and initializers, only property or field parameter bindings are supported in LINQ to Entities.

I know that one alternative is to load all the objects into memory first, and then initialize the list through C#:

return MyContext.Foos.ToList()
    .Select(foo=> new Foo
    {
        Id = foo.Id,
        Name = foo.Name,
        RequiredFeatures = new List<string>()
    }).ToList();

However, this seems like a workaround, not an actual solution. If I don't want to re-iterate over the collection, how can I initialize the list?

like image 326
Andrew Williamson Avatar asked Jan 02 '23 16:01

Andrew Williamson


1 Answers

Surprisingly, although new string[0] and { } do not work, new string[] { } and new string[0] { } do!

return MyContext.Foos.ToList()
    .Select(foo=> new Foo
    {
        Id = foo.Id,
        Name = foo.Name,
        RequiredFeatures = new string[] { }
    }).ToList();

However, this does have the caveat that you can't add items to the list, as it is backed by an array, not an actual list. They all seem functionally identical, and I'm not sure why Entity Framework supports some but not the others.

Empty collections

Supported:

  • new string[] { }
  • new string[0] { }

Not supported:

  • { }
  • new string[0]
  • new List<string>()
  • new List<string> { }
  • new List<string>() { }

Non-empty collections

Supported:

  • new [] { "foo" }
  • new string[] { "foo" }
  • new string[1] { "foo" }
  • new List<string> { "foo" }
  • new List<string>() { "foo" }

Not supported:

  • { "foo" }

It is interesting that Entity Framework supports a list with values, but not an empty list.

like image 90
Andrew Williamson Avatar answered Jan 05 '23 18:01

Andrew Williamson