Skip to content

Consider setting NullabilityInfoContextSupport=true in EF's NuGet package #27474

@eerhardt

Description

@eerhardt

When using EF in a .NET Maui Android/iOS application, developers will get a runtime error because .NET MAUI sets NullabilityInfoContextSupport=false here:

https://github.com/xamarin/xamarin-android/blob/c80dfff7a3183e21d356d2c5835aa0821fd9bd90/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets#L95
and
https://github.com/xamarin/xamarin-macios/blob/e25163f573d31b28fa60f000ce084b8cdb0ca697/dotnet/targets/Xamarin.Shared.Sdk.targets#L136

Since this setting is set to false, when EF tries to create a NullabilityInfo object, an exception is thrown here:

PropertyInfo propertyInfo => nullabilityInfoContext.Create(propertyInfo),
FieldInfo fieldInfo => nullabilityInfoContext.Create(fieldInfo),

02-18 17:55:25.483 14402 14402 E AndroidRuntime: Process: com.companyname.mauioptimisertest, PID: 14402
02-18 17:55:25.483 14402 14402 E AndroidRuntime: android.runtime.JavaProxyThrowable: System.InvalidOperationException: NullabilityInfoContext is not supported in the current application because 'System.Reflection.NullabilityInfoContext.IsSupported' is set to false. Set the MSBuild Property 'NullabilityInfoContextSupport' to true in order to enable it.
02-18 17:55:25.483 14402 14402 E AndroidRuntime:    at System.Reflection.NullabilityInfoContext.EnsureIsSupported()
02-18 17:55:25.483 14402 14402 E AndroidRuntime:    at System.Reflection.NullabilityInfoContext.Create(PropertyInfo propertyInfo)
02-18 17:55:25.483 14402 14402 E AndroidRuntime:    at Microsoft.EntityFrameworkCore.Metadata.Conventions.NonNullableConventionBase.IsNonNullableReferenceType(IConventionModelBuilder modelBuilder, MemberInfo memberInfo)
02-18 17:55:25.483 14402 14402 E AndroidRuntime:    at Microsoft.EntityFrameworkCore.Metadata.Conventions.NonNullableReferencePropertyConvention.Process(IConventionPropertyBuilder propertyBuilder)
02-18 17:55:25.483 14402 14402 E AndroidRuntime:    at Microsoft.EntityFrameworkCore.Metadata.Conventions.NonNullableReferencePropertyConvention.ProcessPropertyAdded(IConventionPropertyBuilder propertyBuilder, IConventionContext`1 context)
02-18 17:55:25.483 14402 14402 E AndroidRuntime:    at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.ImmediateConventionScope.OnPropertyAdded(IConventionPropertyBuilder propertyBuilder)
02-18 17:55:25.483 14402 14402 E AndroidRuntime:    at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.OnPropertyAddedNode.Run(ConventionDispatcher dispatcher)
02-18 17:55:25.483 14402 14402 E AndroidRuntime:    at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.DelayedConventionScope.Run(ConventionDispatcher dispatcher)
02-18 17:55:25.483 14402 14402 E AndroidRuntime:    at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.ConventionBatch.Run()
02-18 17:55:25.483 14402 14402 E AndroidRuntime:    at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.ConventionBatch.Dispose()
02-18 17:55:25.483 14402 14402 E AndroidRuntime:    at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.ImmediateConventionScope.OnModelInitialized(IConventionModelBuilder modelBuilder)
02-18 17:55:25.483 14402 14402 E AndroidRuntime:    at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.OnModelInitialized(IConventionModelBuilder modelBuilder)
02-18 17:55:25.483 14402 14402 E AndroidRuntime:    at Microsoft.EntityFrameworkCore.Metadata.Internal.Model..ctor(ConventionSet conventions, ModelDependencies modelDependencies, ModelConfiguration modelConfiguration)
02-18 17:55:25.483 14402 14402 E AndroidRuntime:    at Microsoft.EntityFrameworkCore.ModelBuilder..ctor(ConventionSet conventions, ModelDependencies modelDependencies, ModelConfiguration modelConfiguration)
02-18 17:55:25.483 14402 14402 E AndroidRuntime:    at Microsoft.EntityFrameworkCore.ModelConfigurationBuilder.CreateModelBuilder(ModelDependencies modelDependencies)
02-18 17:55:25.483 14402 14402 E AndroidRuntime:    at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.CreateModel(DbContext context, IConventionSetBuilder conventionSetBuilder, ModelDependencies modelDependencies)
02-18 17:55:25.483 14402 14402 E AndroidRuntime:    at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.GetModel(DbContext context, ModelCreationDependencies modelCreationDependencies, Boolean designTime)
02-18 17:55:25.483 14402 14402 E AndroidRuntime:    at Microsoft.EntityFrameworkCore.Internal.DbContextServices.CreateModel(Boolean designTime)
02-18 17:55:25.483 14402 14402 E AndroidRuntime:    at Microsoft.EntityFrameworkCore.Internal.DbContextServices.get_Model()
02-18 17:55:25.483 14402 14402 E AndroidRuntime:    at Microsoft.EntityFrameworkCore.Infrastructure.EntityFrameworkServicesBuilder.<>c.<TryAddCoreServices>b__8_4(IServiceProvider p)

We should consider defaulting NullabilityInfoContextSupport=true in EF's NuGet package, so when a .NET Maui app starts using EF, they automatically get this support turned on and their app works out of the box.

Repro steps

  1. dotnet new maui
  2. Add <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.0-preview.1.22076.6" />
  3. Change MainPage.xaml.cs to:
using Microsoft.EntityFrameworkCore;

namespace MauiOptimiserTest;

public partial class MainPage : ContentPage
{

    public MainPage()
    {
        InitializeComponent();

        var dbpath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "test.db");
        using (var context = new TestContext(dbpath))
        {
            context.Database.EnsureCreated();
            var records = context.MyRecords.ToList();
            BindingContext = records;
        }
    }

}

public class MyRecord
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class TestContext : DbContext
{
    public DbSet<MyRecord> MyRecords { get; set; }

    public TestContext() : this(":memory:")
    {
    }

    public TestContext(string path)
    {
        sqliteConnectionString.DataSource = path;
    }

    private readonly Microsoft.Data.Sqlite.SqliteConnectionStringBuilder sqliteConnectionString = new();
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        base.OnConfiguring(optionsBuilder);

        optionsBuilder.UseSqlite(sqliteConnectionString.ToString());
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<MyRecord>().HasData(new MyRecord { Id = 1, Name = "Apple" });
        modelBuilder.Entity<MyRecord>().HasData(new MyRecord { Id = 2, Name = "Banana" });
        modelBuilder.Entity<MyRecord>().HasData(new MyRecord { Id = 3, Name = "Coconut" });
    }
}
  1. dotnet build -f net6.0-android -r android-arm64 -t:Run

The app will crash with the above exception when it is loaded.

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions