Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IDesignTimeDbContextFactory[TContext] violates the

I'm currently working with this quickstart example and am having an absolute nightmare trying to get it working. I've given up with developing database-first due to running into endless exceptions, instead, hoping to generate the tables from the entities, which is not my preferred way of getting things working.

I seem to be having less luck with doing it this way around...

I'm currently up against this exception:

PM> dotnet ef database update CustomerDbContext System.ArgumentException: GenericArguments[0], 'IdentityServerWithAspIdAndEF.Migrations.CustomerDbContext', on 'Microsoft.EntityFrameworkCore.Design.IDesignTimeDbContextFactory1[TContext]' violates the constraint of type 'TContext'. ---> System.TypeLoadException: GenericArguments[0], 'IdentityServerWithAspIdAndEF.Migrations.CustomerDbContext', on 'Microsoft.EntityFrameworkCore.Design.IDesignTimeDbContextFactory1[TContext]' violates the constraint of type parameter 'TContext'. at System.RuntimeTypeHandle.Instantiate(RuntimeTypeHandle handle, IntPtr* pInst, Int32 numGenericArgs, ObjectHandleOnStack type) at System.RuntimeTypeHandle.Instantiate(Type[] inst) at System.RuntimeType.MakeGenericType(Type[] instantiation) --- End of inner exception stack trace --- at System.RuntimeType.ValidateGenericArguments(MemberInfo definition, RuntimeType[] genericArguments, Exception e) at System.RuntimeType.MakeGenericType(Type[] instantiation) at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.FindContextFactory(Type contextType) at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.FindContextTypes() at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.FindContextType(String name) at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContext(String contextType) at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.UpdateDatabase(String targetMigration, String contextType) at Microsoft.EntityFrameworkCore.Design.OperationExecutor.UpdateDatabase.<>c__DisplayClass0_1.<.ctor>b__0() at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action) GenericArguments[0], 'IdentityServerWithAspIdAndEF.Migrations.CustomerDbContext', on 'Microsoft.EntityFrameworkCore.Design.IDesignTimeDbContextFactory`1[TContext]' violates the constraint of type 'TContext'.

Which to me is about as descriptive as "error, there was an error"... I've taken the raw quickstart and made the following modifications.

Startup.ConfigureServices is now:

public void ConfigureServices(IServiceCollection services)
{
    string connectionString = Configuration.GetConnectionString("DefaultConnection");
    var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;

    services.AddDbContext<CustomerDbContext>(options =>
        options.UseSqlServer(connectionString)
    );

    services.AddIdentity<User, Role>()
        .AddUserStore<CustomerUserStore>()
        .AddUserManager<CustomerManager>()
        .AddRoleStore<CustomerRoleStore>()
        .AddRoleManager<RoleManager>()
        .AddSignInManager<CustomerSignInManager>()
        .AddDefaultTokenProviders();

    services.AddMvc();

    services.Configure<IISOptions>(iis =>
    {
        iis.AuthenticationDisplayName = "Windows";
        iis.AutomaticAuthentication = false;
    });

    var builder = services.AddIdentityServer(options =>
        {
            options.Events.RaiseErrorEvents = true;
            options.Events.RaiseInformationEvents = true;
            options.Events.RaiseFailureEvents = true;
            options.Events.RaiseSuccessEvents = true;
        })
        .AddAspNetIdentity<User>()
        // this adds the config data from DB (clients, resources)
        .AddConfigurationStore(options =>
        {
            options.ConfigureDbContext = b =>
                b.UseSqlServer(connectionString,
                    sql => sql.MigrationsAssembly(migrationsAssembly));
        })
        // this adds the operational data from DB (codes, tokens, consents)
        .AddOperationalStore(options =>
        {
            options.ConfigureDbContext = b =>
                b.UseSqlServer(connectionString,
                    sql => sql.MigrationsAssembly(migrationsAssembly));

            // this enables automatic token cleanup. this is optional.
            options.EnableTokenCleanup = true;
            // options.TokenCleanupInterval = 15; // frequency in seconds to cleanup stale grants. 15 is useful during debugging
        });

    if (Environment.IsDevelopment())
    {
        builder.AddDeveloperSigningCredential();
    }
    else
    {
        throw new Exception("need to configure key material");
    }

    services.AddAuthentication();
}

ApplicationDbContext has been renamed to CustomerDbContext and the OnModelCreating is now:

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

    ModelBuilder.Entity<User>(E =>
    {
        E.ToTable("Users");
    });

    ModelBuilder.Entity<Role>(E =>
    {
        E.ToTable("Roles");
    });

    ModelBuilder.Entity<RoleClaim>(E =>
    {
        E.ToTable("RoleClaims");
    });

    ModelBuilder.Entity<UserClaim>(E =>
    {
        E.ToTable("UserClaims");
    });

    ModelBuilder.Entity<UserRole>(E =>
    {
        E.ToTable("UserRoles");
    });

    // "Microsoft.AspNetCore.Identity.IdentityUserLogin<string>"
    ModelBuilder.Entity<Login>(E =>
    {
        E.Property(P => P.LoginId)
            .IsRequired()
            .HasColumnName("LoginId")
            .ValueGeneratedOnAdd();

        E.HasIndex(P => P.LoginProvider)
            .HasName("IX_Logins_LoginProvider");

        E.HasIndex(P => P.ProviderKey)
            .HasName("IX_Logins_ProviderKey");

        E.HasIndex(P => P.UserId)
            .HasName("IX_Logins_AccountId");

        E.ToTable("Logins");
    });

    // "Microsoft.AspNetCore.Identity.IdentityUserToken<string>"
    ModelBuilder.Entity<Token>(E =>
    {
        E.Property(P => P.TokenId)
            .IsRequired()
            .HasColumnName("TokenId")
            .ValueGeneratedOnAdd();

        E.ToTable("Tokens");
    });
}

And finally, I've just lumped a shed-load of stuff together to get the Models working; so the ApplicationUser file now contains:

public class User : IdentityUser<int>
{
}

public class Role : IdentityRole<int>
{
}

public class RoleClaim : IdentityRoleClaim<int>
{
}

public class UserClaim : IdentityUserClaim<int>
{
}

public class Login : IdentityUserLogin<int>
{
    public int LoginId { get; set; }
}

public class UserRole : IdentityUserRole<int>
{
}

public class Token : IdentityUserToken<int>
{
    public int TokenId { get; set; }
}
public class CustomerManager : UserManager<User>
{
    /// <summary>
    /// Constructs a new instance of <see cref="T:Microsoft.AspNetCore.Identity.UserManager`1" />.
    /// </summary>
    /// <param name="Store">The persistence store the manager will operate over.</param>
    /// <param name="OptionsAccessor">The accessor used to access the <see cref="T:Microsoft.AspNetCore.Identity.IdentityOptions" />.</param>
    /// <param name="PasswordHasher">The password hashing implementation to use when saving passwords.</param>
    /// <param name="UserValidators">A collection of <see cref="T:Microsoft.AspNetCore.Identity.IUserValidator`1" /> to validate users against.</param>
    /// <param name="PasswordValidators">A collection of <see cref="T:Microsoft.AspNetCore.Identity.IPasswordValidator`1" /> to validate passwords against.</param>
    /// <param name="KeyNormaliser">The <see cref="T:Microsoft.AspNetCore.Identity.ILookupNormalizer" /> to use when generating index keys for users.</param>
    /// <param name="Errors">The <see cref="T:Microsoft.AspNetCore.Identity.IdentityErrorDescriber" /> used to provider error messages.</param>
    /// <param name="Services">The <see cref="T:System.IServiceProvider" /> used to resolve services.</param>
    /// <param name="Logger">The logger used to log messages, warnings and errors.</param>
    public CustomerManager(
        IUserStore<User> Store,
        IOptions<IdentityOptions> OptionsAccessor,
        IPasswordHasher<User> PasswordHasher,
        IEnumerable<IUserValidator<User>> UserValidators,
        IEnumerable<IPasswordValidator<User>> PasswordValidators,
        ILookupNormalizer KeyNormaliser,
        IdentityErrorDescriber Errors,
        IServiceProvider Services,
        ILogger<UserManager<User>> Logger
    ) : base(
        Store,
        OptionsAccessor,
        PasswordHasher,
        UserValidators,
        PasswordValidators,
        KeyNormaliser,
        Errors,
        Services,
        Logger
    )
    { }
}

public class RoleManager : RoleManager<Role>
{
    /// <summary>
    /// Constructs a new instance of <see cref="T:Microsoft.AspNetCore.Identity.RoleManager`1" />.
    /// </summary>
    /// <param name="Store">The persistence store the manager will operate over.</param>
    /// <param name="RoleValidators">A collection of validators for roles.</param>
    /// <param name="KeyNormalizer">The normalizer to use when normalizing role names to keys.</param>
    /// <param name="Errors">The <see cref="T:Microsoft.AspNetCore.Identity.IdentityErrorDescriber" /> used to provider error messages.</param>
    /// <param name="Logger">The logger used to log messages, warnings and errors.</param>
    public RoleManager(
        IRoleStore<Role> Store,
        IEnumerable<IRoleValidator<Role>> RoleValidators,
        ILookupNormalizer KeyNormalizer,
        IdentityErrorDescriber Errors,
        ILogger<RoleManager<Role>> Logger
    ) : base(
        Store,
        RoleValidators,
        KeyNormalizer,
        Errors,
        Logger
    )
    { }
}

public class CustomerSignInManager : SignInManager<User>
{
    /// <summary>
    /// Creates a new instance of <see cref="T:Microsoft.AspNetCore.Identity.SignInManager`1" />.
    /// </summary>
    /// <param name="UserManager">An instance of <see cref="P:Microsoft.AspNetCore.Identity.SignInManager`1.UserManager" /> used to retrieve users from and persist users.</param>
    /// <param name="ContextAccessor">The accessor used to access the <see cref="T:Microsoft.AspNetCore.Http.HttpContext" />.</param>
    /// <param name="ClaimsFactory">The factory to use to create claims principals for a user.</param>
    /// <param name="OptionsAccessor">The accessor used to access the <see cref="T:Microsoft.AspNetCore.Identity.IdentityOptions" />.</param>
    /// <param name="Logger">The logger used to log messages, warnings and errors.</param>
    /// <param name="Schemes">The logger used to log messages, warnings and errors.</param>
    public CustomerSignInManager(
        UserManager<User> UserManager,
        IHttpContextAccessor ContextAccessor,
        IUserClaimsPrincipalFactory<User> ClaimsFactory,
        IOptions<IdentityOptions> OptionsAccessor,
        ILogger<SignInManager<User>> Logger,
        IAuthenticationSchemeProvider Schemes
    ) : base(
        UserManager,
        ContextAccessor,
        ClaimsFactory,
        OptionsAccessor,
        Logger,
        Schemes
    )
    { }
}

public class CustomerUserStore : UserStore<User, Role, CustomerDbContext, int, UserClaim, UserRole, Login, Token, RoleClaim>
{
    public CustomerUserStore(CustomerDbContext Context) : base(Context)
    {

    }
}

public class CustomerRoleStore : RoleStore<Role, CustomerDbContext, int, UserRole, RoleClaim>
{
    /// <summary>
    /// Constructs a new instance of <see cref="T:Microsoft.AspNetCore.Identity.EntityFrameworkCore.RoleStore`5" />.
    /// </summary>
    /// <param name="context">The <see cref="T:Microsoft.EntityFrameworkCore.DbContext" />.</param>
    /// <param name="describer">The <see cref="T:Microsoft.AspNetCore.Identity.IdentityErrorDescriber" />.</param>
    public CustomerRoleStore(
        CustomerDbContext context,
        IdentityErrorDescriber describer = null
    ) : base(
        context,
        describer
    )
    { }

    /// <summary>Creates a entity representing a role claim.</summary>
    /// <param name="role">The associated role.</param>
    /// <param name="claim">The associated claim.</param>
    /// <returns>The role claim entity.</returns>
    protected override RoleClaim CreateRoleClaim(Role role, Claim claim)
    {
        return new RoleClaim
        {
            RoleId = role.Id,
            ClaimType = claim.Type,
            ClaimValue = claim.Value
        };
    }
}

I've navigated to the src/IdentityServerWithAspIdAndEF folder, set the "Start-Up Project" to IdentityServerWithAspIdAndEF and then run the following commands to try to generate the tables:

PM> Add-Migration CustomerDbContext -Context CustomerDbContext and then PM> dotnet ef database update CustomerDbContext

The first generates the Migrations folder and the expected *.cs files; the second produces the error seen at the top of this post.

Can anyone please advise as to what could be generating this error?

like image 665
derpasaurus Avatar asked May 18 '18 15:05

derpasaurus


2 Answers

The most common fix is to delete any migrations with the same name in your migrations folder, however if that doesn't work I'd suggest deleting all your migrations and dropping your database if it's been created.

You should then be able to run Add-Migration CustomerDbContext -Context CustomerDbContext and dotnet ef database update without error.

like image 99
Daniel Hoffmann-Mitscherling Avatar answered Oct 23 '22 13:10

Daniel Hoffmann-Mitscherling


You should be able to run it after renaming the migration in the script.

Add-Migration **CustomerDbContext** -Context CustomerDbContext

It worked for me after renaming the migration.

like image 2
santmhrj Avatar answered Oct 23 '22 15:10

santmhrj