Scaffolding: Controller scaffolding using EF fails when project contains middleware configured using secrets

Created on 5 Oct 2016  路  4Comments  路  Source: dotnet/Scaffolding

It seems EntityFrameworkServices.GetModelMetadata will fail at the step _Attempting to figure out the EntityFramework metadata for the model and DbContext_ when building the application services in EntityFrameworkServices.TryCreateContextUsingAppCode, if the Startup class adds some middleware with required options being populated from the secret manager.

I have created a new ASP.Net Core 1.0.0 project using the default Web Application template adding individual accounts authentication.
Then I have added the google authentication middleware:

    "Microsoft.AspNetCore.Authentication.Google": "1.0.0"

I have updated my startup class with the Google middleware, where the ClientId and Secret are read from the SecretManager tool:

app.UseGoogleAuthentication(new GoogleOptions
{
    ClientId = Configuration["GoogleClientId"],
    ClientSecret = Configuration["GoogleClientSecret"]             
});

If I then:

  1. Create a new model class
  2. Either add a dbSet to the default ApplicationDbContext or create a new dbContext class
  3. Run the MVC Controller with Views, using Entity Framework scaffolding selecting that model class and context

I get the following error:

C:\Program Files\dotnet\dotnet.exe aspnet-codegenerator --project "C:\Users\Daniel.Garcia\Documents\Github\WebApplication3\src\WebApplication3" controller --force --controllerName ArticlesController --model WebApplication3.Models.Article --dataContext WebApplication3.Data.ApplicationDbContext --relativeFolderPath Controllers --controllerNamespace WebApplication3.Controllers --referenceScriptLibraries --useDefaultLayout
Finding the generator 'controller'...
Running the generator 'controller'...
Attempting to compile the application in memory
Attempting to figure out the EntityFramework metadata for the model and DbContext: Article
info: Microsoft.Extensions.DependencyInjection.DataProtectionServices[0]
      User profile is available. Using 'C:\Users\Daniel.Garcia\AppData\Local\ASP.NET\DataProtection-Keys' as key repository and Windows DPAPI to encrypt keys at rest.
The 'ClientId' option must be provided. StackTrace:There was an error creating the DbContext instance to get the model. No parameterless constructor defined for this object.

   at Microsoft.AspNetCore.Authentication.OAuth.OAuthMiddleware`1..ctor(RequestDelegate next, IDataProtectionProvider dataProtectionProvider, ILoggerFactory loggerFactory, UrlEncoder encoder, IOptions`1 sharedOptions, IOptions`1 options)
No parameterless constructor defined for this object.   at Microsoft.AspNetCore.Authentication.Google.GoogleMiddleware..ctor(RequestDelegate next, IDataProtectionProvider dataProtectionProvider, ILoggerFactory loggerFactory, UrlEncoder encoder, IOptions`1 sharedOptions, IOptions`1 options)

--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at Microsoft.Extensions.Internal.ActivatorUtilities.ConstructorMatcher.CreateInstance(IServiceProvider provider)
   at Microsoft.Extensions.Internal.ActivatorUtilities.CreateInstance(IServiceProvider provider, Type instanceType, Object[] parameters)
   at Microsoft.VisualStudio.Web.CodeGeneration.ActionInvoker.<BuildCommandLine>b__6_0()   at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.<>c__DisplayClass3_0.<UseMiddleware>b__0(RequestDelegate next)

   at Microsoft.Extensions.CommandLineUtils.CommandLineApplication.Execute(String[] args)   at Microsoft.AspNetCore.Builder.Internal.ApplicationBuilder.Build()

   at Microsoft.VisualStudio.Web.CodeGeneration.CodeGenCommand.Execute(String[] args)   at Microsoft.AspNetCore.Hosting.Internal.WebHost.BuildApplication()

   at Microsoft.AspNetCore.Hosting.WebHostBuilder.Build()
   at Microsoft.VisualStudio.Web.CodeGeneration.EntityFrameworkCore.EntityFrameworkServices.TryCreateContextUsingAppCode(Type dbContextType, ModelType startupType)
RunTime 00:00:07.10

It seems to me the environment isn't set as Development when building the app in EntityFrameworkServices.TryCreateContextUsingAppCode. If I update my Startup constructor to always call builder.AddUserSecrets(); regardless of whether the environment is Development or not, then the scaffolding works fine.

//if (env.IsDevelopment())
//{
//    builder.AddUserSecrets();
//}
builder.AddUserSecrets();

Most helpful comment

Another quick workaround would be setting things up so there are some dummy non-null values that will be overwritten in the dev environment with the values from the user secrets.

Just adding some dummy values to appsettings.json or registering the options like this:

    app.UseGoogleAuthentication(new GoogleOptions
    {
        ClientId = Configuration["GoogleClientId"] ?? "MissingClientId",
        ClientSecret = Configuration["GoogleClientSecret"] ?? "MissingClientSecret",
    });

All 4 comments

Another quick workaround would be setting things up so there are some dummy non-null values that will be overwritten in the dev environment with the values from the user secrets.

Just adding some dummy values to appsettings.json or registering the options like this:

    app.UseGoogleAuthentication(new GoogleOptions
    {
        ClientId = Configuration["GoogleClientId"] ?? "MissingClientId",
        ClientSecret = Configuration["GoogleClientSecret"] ?? "MissingClientSecret",
    });

This is fixed in 1.0.0-msbuild2-final and later

Even with 1.0.0-msbuild2-final I still got the issue. @DaniJG solution worked for me.

Thanks for this @DaniJG, had me stumped for a while and all packages up to date

Was this page helpful?
0 / 5 - 0 ratings