-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Description
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:
efcore/src/EFCore/Metadata/Conventions/NonNullableConventionBase.cs
Lines 54 to 55 in 0e1e95b
| 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
dotnet new maui- Add
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.0-preview.1.22076.6" /> - Change
MainPage.xaml.csto:
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" });
}
}dotnet build -f net6.0-android -r android-arm64 -t:Run
The app will crash with the above exception when it is loaded.