Entity Framework Core NotMapped attribute ignored

Tags: UWP, Entity Framework

Recently I was migrating one of our larger side projects from Lex.db to Entity Framework Core. We’ve been great fans of Lex.db for several of our Windows 8.1 apps and were waiting for an (in progress) v2 release supporting Windows 10. But since Lex.db was a spare time project as well, we knew there wasn’t a fixed release date. As sources of v2 weren’t public available yet and v1.x introduced some issues for Windows 10 and .NET native compilation, we decided to take our losses and move forward. Hopefully one day Lex.db v2 will be released as I still like the product.

But back to our codebase. As this is a rather large project, I managed to complete 75% of the conversion to EF Core RC 1 (back then still versioned EF 7) before I had to shift priorities. When returning to the migration, EF Core was just released and I could finish up with the final bits. There are however a few changes between RC 1 and RTM, but everything is explained in these steps which I just executed together:

Moving from RC1 to RC2

This one is pretty straight forward:

  • Switch packages as EF 7 got renamed to EF Core 1.
  • Fix namespaces.
  • Run the commands for the migrations (as we’re in a migration, I don’t have to drag along an existing SQLite database).

Moving from RC2 to RTM

This one is pretty straight forward:

  • Update packages
  • Add binding redirects

Quoting from the upgrade page:

Attempting to run EF commands on Universal Windows Platform (UWP) projects results in the following error:

System.IO.FileLoadException: Could not load file or assembly
    ‘System.IO.FileSystem.Primitives, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a’
    or one of its dependencies. The located assembly’s manifest definition does not match the assembly reference.

You need to manually add binding redirects to the UWP project. Create a file named App.config in the project root folder and add redirects to the correct assembly versions.

<configuration>
 <runtime>
   <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
     <dependentAssembly>
       <assemblyIdentity name="System.IO.FileSystem.Primitives"
                         publicKeyToken="b03f5f7f11d50a3a"
                         culture="neutral" />
       <bindingRedirect oldVersion="4.0.0.0"
                        newVersion="4.0.1.0"/>
     </dependentAssembly>
     <dependentAssembly>
       <assemblyIdentity name="System.Threading.Overlapped"
                         publicKeyToken="b03f5f7f11d50a3a"
                         culture="neutral" />
       <bindingRedirect oldVersion="4.0.0.0"
                        newVersion="4.0.1.0"/>
     </dependentAssembly>
   </assemblyBinding>
 </runtime>
</configuration>

Everything worked fine and it was time to merge with the main branch which had happily continued along, resulting in a nice bunch of merge conflicts.

The bug

In one of the models, we had a property of the type Guid[]. This array gave a beautiful InvalidOperationException in Entity Framework:

System.InvalidOperationException: The property 'User.Tokens' could not be mapped, because it is of type 'Guid[]' which is not a supported primitive type or a valid entity type. Either explicitly map this property, or ignore it.
    at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.PropertyMappingValidationConvention.Apply(InternalModelBuilder modelBuilder)
    at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.OnModelBuilt(InternalModelBuilder modelBuilder)
    at Microsoft.EntityFrameworkCore.Metadata.Internal.Model.Validate()
    at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalModelBuilder.Validate()
    at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.CreateModel(DbContext context, IConventionSetBuilder conventionSetBuilder, IModelValidator validator)
    at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.<>c__DisplayClass14_0.<GetModel>b__0(Object k)
    at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
    at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.GetModel(DbContext context, IConventionSetBuilder conventionSetBuilder, IModelValidator validator)
    at Microsoft.EntityFrameworkCore.Internal.DbContextServices.CreateModel()
    at Microsoft.EntityFrameworkCore.Internal.LazyRef`1.get_Value()
    at Microsoft.EntityFrameworkCore.Internal.DbContextServices.get_Model()
    at Microsoft.EntityFrameworkCore.Infrastructure.EntityFrameworkServiceCollectionExtensions.<>c.<AddEntityFramework>b__0_4(IServiceProvider p)
    at Microsoft.Extensions.DependencyInjection.ServiceLookup.FactoryService.Invoke(ServiceProvider provider)
    at Microsoft.Extensions.DependencyInjection.ServiceProvider.ScopedCallSite.Invoke(ServiceProvider provider)
    at Microsoft.Extensions.DependencyInjection.ServiceProvider.<>c__DisplayClass12_0.<RealizeService>b__0(ServiceProvider provider)
    at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType)
    at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetService[T](IServiceProvider provider)
    at Microsoft.EntityFrameworkCore.Design.Internal.DesignTimeServicesBuilder.<>c__DisplayClass7_0.<ConfigureContextServices>b__9(IServiceProvider _)
    at Microsoft.Extensions.DependencyInjection.ServiceLookup.FactoryService.Invoke(ServiceProvider provider)
    at Microsoft.Extensions.DependencyInjection.ServiceProvider.TransientCallSite.Invoke(ServiceProvider provider)
    at Microsoft.Extensions.DependencyInjection.ServiceLookup.ConstructorCallSite.Invoke(ServiceProvider provider)
    at Microsoft.Extensions.DependencyInjection.ServiceProvider.TransientCallSite.Invoke(ServiceProvider provider)
    at Microsoft.Extensions.DependencyInjection.ServiceProvider.<>c__DisplayClass12_0.<RealizeService>b__0(ServiceProvider provider)
    at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType)
    at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
    at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
    at Microsoft.EntityFrameworkCore.Design.MigrationsOperations.AddMigration(String name, String outputDir, String contextType)
    at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigrationImpl(String name, String outputDir, String contextType)
    at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigration.<>c__DisplayClass0_1.<.ctor>b__0()
    at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.<>c__DisplayClass3_0`1.<Execute>b__0()
    at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)

I had fixed it before using the NotMappedAttribute, which worked perfectly. So I was a bit surprised to see it pop up again, even though the attribute was present.

The fix

Somehow my project file got merged incorrectly and the App.config got lost and overlooked. Simply re-adding the config file with binding redirects fixed the error.

A small repro is available on GitHub.

Comments

comments powered by Disqus