I am trying to use code first migration approach(genrate database from code). but facing error. here is my code
using Microsoft.AspNet.Identity.EntityFramework;
namespace IMChatApp.Models
{
// You can add profile data for the user by adding more properties to your ApplicationUser class, please visit http://go.microsoft.com/fwlink/?LinkID=317594 to learn more.
public class ApplicationUser : IdentityUser
{
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection")
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.IncludeMetadataInDatabase = false;
}
public System.Data.Entity.DbSet<JustDoIt.Models.user> users { get; set; }
}
}
According to the documentation, the OnModelCreating method takes a DbModelBuilder, not a ModelBuilder.
Related
I'm trying to use Entity Framework Core Identity in an ASP.NET Core application
I have created Database Context and its interface as follows:
public class AppDbContext : IdentityDbContext<AppUser>, IAppDbContext
{
public AppDbContext (DbContextOptions<AppDbContext> options) : base(options)
{ }
public DbSet<AppUser> AppUser { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
}
}
public interface IAppDbContext
{
DbSet<AppUser> AppUser { get; set; }
int SaveChanges();
Task<int> SaveChangesAsync();
}
Here the issue is, it shows an error in AppDbContext, stating
'AppDbContext' does not implement interface member
'IAppDbContext.SaveChangesAsync()'
If the AppDbContext is inherited from the DbContext insted of IdentityDbContext error would not appear, but to use Identity it should be inherited from IdentityDbContext.
How can I get around this?
It's weird, because this error should shown in both cases. Anyway, DbContext doesn't have method:
Task<int> SaveChangesAsync()
It has method:
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken))
To get around this case, you should wrap DbContext.SaveChangesAsync method:
public class AppDbContext : IdentityDbContext<AppUser>, IAppDbContext
{
...
public Task<int> SaveChangesAsync() => base.SaveChangesAsync();
}
In my app I use code-first approach and I'm adding entities to DbContext only via IEntityTypeConfiguration<>.
My goal is to achieve pluralized table names i.e. Models for Model.
After reading documentation, article, This question
my understading would be that my pluralizer should be registered and as IPluralizer used during creating migration, however it is not.
Of course I could have implicitly use DbSet<Model> Models {get;set;} or use builder.ToTable("Models");, but in my generic scenario I'd like to avoid that, especially as I would like some models not to override abstract generic scenario.
Question is, am I doing something wrong, or I misunderstand the way it should behave
AppDesignService.cs
public class AppDesignService : IDesignTimeServices
{
public void ConfigureDesignTimeServices(IServiceCollection services)
{
Debugger.Launch();
services.AddSingleton<IPluralizer, InflectorPluralizer>();
}
}
MyDbContext.cs
public class AppDbContext : IdentityDbContext<AppUser,AppRole,Guid>
{
public EmsDbContext(DbContextOptions options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.ApplyConfigurationsFromAssembly(Assembly.GetAssembly(typeof(AppDbContext)));
}
}
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
var connectionString = Configuration.GetConnectionString("DefaultConnection");
services.AddDbContext<DbContext, AppDbContext>(opt =>
{
opt.UseSqlServer(connectionString, sqlOpt =>
{
sqlOpt.EnableRetryOnFailure(3);
});
});
// code skipped for brevity
}
Config
public interface IEntity
{
int Id {get;set;}
}
public class Model : IEntity
{
public int Id {get;set;}
public string Name {get;set;}
}
public abstract class DbEntityConfig<T> : IEntityTypeConfiguration<T>
where T : class, IEntity
{
public void Configure(EntityTypeBuilder<T> builder)
{
builder.HasKey(m => m.Id);
}
}
public class ModelEntityConfig : DbEntityConfig<Model>
{
public void Configure(EntityTypeBuilder<Model> builder)
{
base.Configure(builder);
// Of course I want to avoid this call, cos TheOtherModel might not override base method
// builder.ToTable("Models");
builder.Property(m => m.Name).HasMaxLength(25);
}
}
Result
public partial class Test : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Model",
columns: table => new
// code skipped for brevity
}
}
Expected result:
public partial class Test : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Models",
columns: table => new
// code skipped for brevity
}
}
The linked article is incorrect. As you can see from Pluralization hook for DbContext Scaffolding EF Core documentation:
EF Core 2.0 introduces a new IPluralizer service that is used to singularize entity type names and pluralize DbSet names.
Shortly, it is used only by scaffolding commands, hence cannot be used for changing the table name model conventions.
In general migration tools use the model the way it is configured by conventions, data annotations and fluent API. So applying custom convention should be with model API inside OnModelCreating. Something like this:
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
entityType.Relational().TableName = GetTableName(entityType);
where GetTableName method implements your naming convention:
string GetTableName(IEntityType entityType)
{
// use entiityType.Name and other info
return ...;
}
Update (EF Core 3.0+): Use entityType.SetTableName(...) in place of entityType.Relational().TableName = ...
For the last few days, I have been playing around with Asp.net's Identity framework.
I have been able to get the Register and Login working however when I try to extend the functionality to saving data against specific users, I find it is different to how I would normally implement it with stock standard EF.
Normally I would use something like below to save data:
using(var context = myDbContex())
{
context.Add(object);
context.SaveChanges();
}
However, when I try to use this approach after inheriting the IdentityDbContext it is expecting an argument. Is it okay for me to create a default constructor that doesn't take any arguments or should I be passing something in?
My Context currently looks like this:
public class AppContext : IdentityDbContext<ApplicationUser>
{
//I am not really sure why options needs to be specified as an argument
public AppContext(DbContextOptions<AppContext> options)
: base(options)
{
}
public DbSet<ApplicationUser> ApplicationUsers { get; set; }
public DbSet<Xxxxx> Xxxxx { get; set; }
public DbSet<Yyyyy> Yyyyy { get; set; }
public DbSet<Zzzzz> Zzzzz { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
}
}
In Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<AppContext>(options =>
options.UseSqlite("Data Source=App.db"));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<AppContext>()
.AddDefaultTokenProviders();
});
Why is this implementation of the context different to the standard dbContext, and how can I save data using this context?
Thanks
Because of this line
services.AddDbContext<AppContext>(options => options.UseSqlite("DataSource=App.db"));
You need to provide a constructor that has the DbContextOptions as paramter, which has nothing todo with IdentityDbContext.
You have two choices now.
Use dependency injection, that is how you are supposed to use it anyway
public class MyController : Controller
{
private AppContext context;
public MyController(AppContext context)
{
this.context = context;
}
}
Secondly you could register your context differently.
services.AddDbContext<AppContext>();
And apply changes in your context, remove the constructor and override OnConfiguring(DbContextOptionsBuilder optionsBuilder)
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite("Data Source=App.db");
}
Now you can use it as you usually would do.
using(var context = new AppContext())
{
// do stuff
}
EDIT:
Not part of the actual question but signin, registration and role managing is handled by these classes, that can be injected when using IdentityDbContext
SignInManager
UserManager
RoleManager
I tried to extend the asp.net identity user with a "Name" property, I therefore followed the description in this post How to extend available properties of User.Identity
But after I did that and tried to login I get this error "The model backing the 'ApplicationDbContext' context has changed since the database was created. Consider using Code First Migrations to update the database"
Can I fix this or can you only extend the asp.net ueser identity before the database is created the first time?
Based on Asp.Net template database will generate the bellow structure:
In order to extend asp.net identity user and keep the things simple, please update IdentityModels.cs with following code:
//CODE
using System.Data.Entity;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using System.Data.Entity.Migrations;
namespace WebApplication.Models
{
// You can add profile data for the user by adding more properties to your ApplicationUser class, please visit http://go.microsoft.com/fwlink/?LinkID=317594 to learn more.
public class ApplicationUser : IdentityUser
{
public string Name { get; set; }
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
Database.SetInitializer(new MigrateDatabaseToLatestVersion<ApplicationDbContext, ApplicationDbContextConfiguration>());
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<IdentityRole>().ToTable("AspNetRoles");
modelBuilder.Entity<IdentityUserRole>().ToTable("AspNetUserRoles");
modelBuilder.Entity<IdentityUserLogin>().ToTable("AspNetUserLogins");
modelBuilder.Entity<IdentityUserClaim>().ToTable("AspNetUserClaims");
modelBuilder.Entity<ApplicationUser>().ToTable("AspNetUsers");
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
internal sealed class ApplicationDbContextConfiguration : DbMigrationsConfiguration<ApplicationDbContext>
{
public ApplicationDbContextConfiguration()
{
ContextKey = "WebApplication.Models.ApplicationDbContext"; //Retrieved from the database table dbo.__MigrationHistory
#if DEBUG
AutomaticMigrationsEnabled = true;
AutomaticMigrationDataLossAllowed = true;
#else
AutomaticMigrationsEnabled = false;
AutomaticMigrationDataLossAllowed = false;
#endif
}
protected override void Seed(ApplicationDbContext context)
{
base.Seed(context);
}
}
}
The output is:
PS: You can rename default names for asp.net generated tables, personally I prefer to remove AspNet suffix
So maybe this is a stupid question, because I know that when creating a Code-first model from an existing database, Visual Studio will create a new ADO.NET Entity Data Model, and add the models in a new DbContext.
I am using Microsoft Identity in my project, hence there is already a ApplicationDbContext (IdentityDbContext). I think just having all my models in a single DbContext would be easier to handle. I am generating my code-first models from an existing database.
But Is there a way such that the generated models add up into the already existing DbContext (In this case, the IdentityDbContext?)
I have like, many models, so currently I am compelled to add each of them into existing ApplicationDbContext manually, and remove from the created DbContext.
As far as I remember there is no way to add the generated models objects to the existing DbContext automatically. You need to add them manually.
public partial class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("name=DefaultConnection")
{
}
//Add your Model objects here, You can copy them from automatically generated DbContext
public virtual DbSet<ModelObjectName> PropertyName { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//Copy the modelBuilder configuration here from automatically generated DbContext here
base.OnModelCreating(modelBuilder);
}
}
Here's an alternative that works:
Mark your ApplicationDbContext class as partial:
public partial class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext() : base("DefaultConnection")
{
//unchanged
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//unchanged
}
}
Do the same with your custom Data model class, and remove the inheritance from DbContext. Also, remove constructor form it and change the OnModelCreating into a regular private method with some different name. Keep the rest unchanged.
public partial class MyDataModels
{
//unchanged
private void OnModelCreating2(DbModelBuilder modelBuilder)
{
//unchanged
}
}
Refactor (Ctrl + R + R : default shortcut) name of your Data model class, and change it to ApplicationDbContext. Visual Studio might give you a conflict warning during refactoring, ignore that and refactor. Finally, call OnModelCreating2() from OnModelCreating() method of ApplicationDbContext class.
public partial class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext() : base("DefaultConnection")
{
//unchanged
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//unchanged
OnModelCreating2(modelBuilder);
}
}
Another nice approach suggested by #DevilSuichiro is to simply inherit your Data model class from ApplicationDbContext.
Cheers!