Eastrall / EntityFrameworkCore.DataEncryption

A plugin for Microsoft.EntityFrameworkCore to add support of encrypted fields using built-in or custom encryption providers.
MIT License
329 stars 55 forks source link

Exception with EF Core 3.1.1 #4

Closed RichardD2 closed 3 years ago

RichardD2 commented 4 years ago

Trying to use this library with EF Core 3.1.1 in an ASP.NET MVC5 project produces an exception:

MissingMethodException: 
Method not found: 'System.Type Microsoft.EntityFrameworkCore.Metadata.IProperty.get_ClrType()'.
   at Microsoft.EntityFrameworkCore.DataEncryption.ModelBuilderExtensions.UseEncryption(ModelBuilder modelBuilder, IEncryptionProvider encryptionProvider) +0
   BaseContext.OnModelCreating(ModelBuilder modelBuilder)
   Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.CreateModel(DbContext context, IConventionSetBuilder conventionSetBuilder) +139
   Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.GetModel(DbContext context, IConventionSetBuilder conventionSetBuilder) +223
   Microsoft.EntityFrameworkCore.Internal.DbContextServices.CreateModel() +153
   Microsoft.EntityFrameworkCore.Internal.DbContextServices.get_Model() +64
   Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitFactory(FactoryCallSite factoryCallSite, RuntimeResolverContext context) +21
   Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument) +142
   Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType) +221
   Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite singletonCallSite, RuntimeResolverContext context) +71
   Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument) +356
   Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context) +167
   Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument) +274
   Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType) +221
   Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite singletonCallSite, RuntimeResolverContext context) +71
   Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument) +356
   Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope) +42
   Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType) +107
   Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider) +51
   Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies() +52
   Microsoft.EntityFrameworkCore.DbContext.get_InternalServiceProvider() +422
   Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies() +34
   Microsoft.EntityFrameworkCore.DbContext.Set() +27

My OnModelCreating method is simply:

protected override void OnModelCreating([NotNull] ModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);
    modelBuilder.ApplyConfigurationsFromAssembly(typeof(BaseContext).Assembly);
    if (EncryptionProvider != null)
    {
        modelBuilder.UseEncryption(EncryptionProvider);
    }
}

The IEncryptionProvider is injected via DI:

public static IServiceCollection AddEfEncryptionProvider(this IServiceCollection services, string key, string iv)
{
    if (services is null) throw new ArgumentNullException(nameof(services));
    if (string.IsNullOrWhiteSpace(key)) throw new ArgumentNullException(nameof(key));
    if (string.IsNullOrWhiteSpace(iv)) throw new ArgumentNullException(nameof(iv));

    byte[] encryptionKey = Convert.FromBase64String(key);
    byte[] encryptionIv = Convert.FromBase64String(iv);
    services.AddSingleton<IEncryptionProvider>(new AesProvider(encryptionKey, encryptionIv));
    return services;
}

This works with EF Core 2.x.

RichardD2 commented 4 years ago

After replacing the conditional PackageReference elements which reference either v2.1.0 or v3.0.0 with unconditional references to v3.1.1 and recompiling, the application works again.

NB: 3.1.0 reintroduced support for .NET Standard 2.0: https://github.com/dotnet/efcore/issues/18141

Eastrall commented 4 years ago

Hi ! Thank you for using EntityFrameworkCore.DataEncryption and your feedback. I didn't knew that EFCore 3.1 had reintroduced support for .NET Standard 2.0; thank you for info!

Can you provide more information about what framework you are targeting (netstandard2.0, netstandard2.1) ?

RichardD2 commented 4 years ago

It's an MVC5 app targetting .NET Framework 4.7.2.

Eastrall commented 4 years ago

I guess one of the best solutions as you said, would be to remove the conditionnal PackageReference in the csproj file and generate a new nuget package that targets only Microsoft.EntityFrameworkCore >= 3.1.0.

In the mean time, maybe you can use version 1.0.1 that targets .NET Standard 2.0 and Microsoft.EntityFrameworkCore >= 2.1.0.

Eastrall commented 3 years ago

Do you still have the error? Or can we close this issue?

RichardD2 commented 3 years ago

I'm happy to close this now. :)