Efcore: Collection Navigation property without Setter error on UWP

Created on 9 Jan 2020  路  11Comments  路  Source: dotnet/efcore

On UWP, when a navigation property of type ICollection does not have a setter and is auto-initialized, an error occurs when retrieving the object or when adding it:

Value cannot be null.
Parameter name: frameworkName
   at Microsoft.EntityFrameworkCore.Metadata.Internal.ClrCollectionAccessorFactory.Create(INavigation navigation)
   at Microsoft.EntityFrameworkCore.Metadata.Internal.Navigation.<>c.<get_CollectionAccessor>b__21_0(Navigation n)
   at Microsoft.EntityFrameworkCore.Internal.NonCapturingLazyInitializer.EnsureInitialized[TParam,TValue](TValue& target, TParam param, Func`2 valueFactory)
   at Microsoft.EntityFrameworkCore.Metadata.Internal.Navigation.get_CollectionAccessor()
   at Microsoft.EntityFrameworkCore.NavigationExtensions.GetCollectionAccessor(INavigation navigation)
   at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.CustomShaperCompilingExpressionVisitor.VisitExtension(Expression extensionExpression)
   at System.Linq.Expressions.Expression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at System.Dynamic.Utils.ExpressionVisitorUtils.VisitBlockExpressions(ExpressionVisitor visitor, BlockExpression block)
   at System.Linq.Expressions.ExpressionVisitor.VisitBlock(BlockExpression node)
   at System.Linq.Expressions.BlockExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at System.Linq.Expressions.ExpressionVisitor.VisitConditional(ConditionalExpression node)
   at System.Linq.Expressions.ConditionalExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at System.Dynamic.Utils.ExpressionVisitorUtils.VisitBlockExpressions(ExpressionVisitor visitor, BlockExpression block)
   at System.Linq.Expressions.ExpressionVisitor.VisitBlock(BlockExpression node)
   at System.Linq.Expressions.BlockExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at System.Linq.Expressions.ExpressionVisitor.VisitLambda[T](Expression`1 node)
   at System.Linq.Expressions.Expression`1.Accept(ExpressionVisitor visitor)
   at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.VisitShapedQueryExpression(ShapedQueryExpression shapedQueryExpression)
   at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.VisitExtension(Expression extensionExpression)
   at System.Linq.Expressions.Expression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at Microsoft.EntityFrameworkCore.Query.QueryCompilationContext.CreateQueryExecutor[TResult](Expression query)
   at Microsoft.EntityFrameworkCore.Storage.Database.CompileQuery[TResult](Expression query, Boolean async)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQueryCore[TResult](IDatabase database, Expression query, IModel model, Boolean async)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass12_0`1.<ExecuteAsync>b__0()
   at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQueryCore[TFunc](Object cacheKey, Func`1 compiler)
   at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQuery[TResult](Object cacheKey, Func`1 compiler)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteAsync[TResult](Expression query, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.ExecuteAsync[TResult](Expression expression, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ExecuteAsync[TSource,TResult](MethodInfo operatorMethodInfo, IQueryable`1 source, Expression expression, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ExecuteAsync[TSource,TResult](MethodInfo operatorMethodInfo, IQueryable`1 source, LambdaExpression expression, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.SingleOrDefaultAsync[TSource](IQueryable`1 source, Expression`1 predicate, CancellationToken cancellationToken)

It works correctly in .NET Core 3.1 web app and it worked correctly in 2.2 on both platforms. UWP uses .NET Standard 2.0, could this be the cause of a different behavior compared to .NET Core 3.1?

Steps to reproduce

This test pass on netcoreapp3.1 but fails on Unit Test App (UWP)
````cs
[TestClass]
public class UniversalAppTests
{
[TestMethod]
public void CollectionNavigationPropertyWithoutSetter()
{
var options = new DbContextOptionsBuilder()
.UseInMemoryDatabase(databaseName: "Add_writes_to_database")
.Options;

    using (var context = new TestContext(options))
    {
        // If you remove the include or add the setter to the navigation property, the error is not generated
        context.WithCollections.Include(c => c.Simples).ToArray();

        // The error is generated with any operation on an object that does not have a setter on the collection property navigation
        context.WithCollections.Add(new WithCollection());
    }
}

}

public class TestContext : DbContext
{
public TestContext(DbContextOptions options)
: base(options)
{

}

public DbSet<WithCollection> WithCollections { get; set; }
public DbSet<Simple> Simples { get; set; }

}

public class WithCollection
{
public int Id { get; set; }

// Add setter to fix error on UWP
public ICollection<Simple> Simples { get; /*set;*/ } = new HashSet<Simple>();

}

public class Simple
{
public int Id { get; set; }
public int WithCollectionId { get; set; }
}
````

Further technical details

EF Core version: 3.1
Database provider: Microsoft.EntityFrameworkCore.Sqlite or InMemory
Target framework: .NET Standard 2.0
Operating system: Windows 10.0.18362
IDE: Visual Studio 2019 16.4.2

area-change-tracking area-external area-uwp blocked customer-reported punted-for-5.0 type-bug

Most helpful comment

Notes from investigation: This appears to be a bug in delegate compilation for setting read-only fields. I was able to reproduce using EF, but so far my attempts to repro outside of EF have failed--It may be that the offending code has to be compiled as a .NET Standard 2.0 library, like EF is.

@YZahringer @adamhewitt627 @Mys73rion A couple of workarounds:

  • Don't have properties with read-only fields. For example, add a private setter to the property so that the compiler-generated field is not read-only.
  • Use modelBuilder.UsePropertyAccessMode(PropertyAccessMode.Property); to force EF to read and write to properties rather than fields.

All 11 comments

I happened to just update a few minutes ago and hit this same issue.

We just updated our application to EF Core 3.1 and faced the same issue.
The error message is, how to say, completely confusing and just a kind of blabla. Would be nice if the message could help you to find the problem.
We hat to introduce multiple nonsense setters which is kind of stupid.

@bricelam @AndriySvyryd Do either of you have a machine already setup for UWP development where you could test this?

Notes from investigation: This appears to be a bug in delegate compilation for setting read-only fields. I was able to reproduce using EF, but so far my attempts to repro outside of EF have failed--It may be that the offending code has to be compiled as a .NET Standard 2.0 library, like EF is.

@YZahringer @adamhewitt627 @Mys73rion A couple of workarounds:

  • Don't have properties with read-only fields. For example, add a private setter to the property so that the compiler-generated field is not read-only.
  • Use modelBuilder.UsePropertyAccessMode(PropertyAccessMode.Property); to force EF to read and write to properties rather than fields.

We ran into this error when running in .Net Framework 4.8 ASP.NET Web Forms application.
Adding private setters resolved the issue.

Related to #19588

Since EF Core 5.0 no longer supports .NET Standard 2.0, this is still relevant?

Currently UWP does not support .NET Standard 2.1, so how do I use EF Core 5.0?
UWP is still supported by EF Core 5.0?

@YZahringer We are watching with interest how the various U.I. platforms develop. Given all the issues we have had trying to create a good experience for UWP together with internal usage data, we have currently put it on the backburner.

@YZahringer What are your plans in general? Will you be converting your apps use WinUI 3 on top of .NET 5/Win32, or do you intend to remain on .NET Native/UWP?

Also, are you considering .NET MAUI (the evolution of Xamarin.Forms) as a viable alternative?

@bricelam We mainly use EF Core SQLite on UWP with Xamarin.Forms in multiple cross-platform applications (UWP, Android, iOS). Currently we do not use WinUI "natively", but Xamarin.Forms uses WinUI 2.4 to render on UWP.

WinUI 3 is still in preview and maybe supported by Xamarin.Forms 5 !11955 or reported to MAUI.

The future with .NET MAUI and .NET 6 is very interesting, but it won't be before the end of next year. So in the meantime, it would be useful to find a solution to use the latest version of EF Core on cross-platform apps with Xamarin.Forms 馃槂

Was this page helpful?
0 / 5 - 0 ratings