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();
}
Related
I am looking to create a signalR Hub to get the SQL record updates in real time, using SQLDependency.
I used the EFCore Scaffolding database to create models as well as the DBContext, and using the repository pattern to work on retrieving data from the DB.
private Func<DBAContext> _contextFactory;
public Repository(Func<DBAContext> ContextFactory)
{
this._contextFactory = ContextFactory;
}
public someMethod()
{
using (var context = _contextFactory())
{
return context.Account.LastOrDefault();
}
}
Here's the issue: on invoking context, I get "Constructor on Type DBContext not found.
DBAContext.cs
public partial class DBAContext : DbContext
{
public DBAContext(DbContextOptions<DBAContext> options)
: base(options)
{
}
public virtual DbSet<Account> Account { get; set; }
}
.
.
.//OnConfiguring
.//Autogenerated onModelCreating
Here is where the error occurs:
public static void AddDbContextFactory<TDataContext>(this IServiceCollection services, string
connectionString) where TDataContext : DbContext
{
services.AddSingleton<Func<TDataContext>>((ctx) =>
{
var options = new DbContextOptionsBuilder()
.UseSqlServer(connectionString)
.Options;
return () => (TDataContext)Activator.CreateInstance(typeof(TDataContext), options);
});
}
}
Activator.CreateInstance is unable to resolve a constructor on the DBAContext class.
Startup.cs
services.AddDbContextFactory<DBAContext>
(Configuration.GetConnectionString("DefaultConnection"));
If anyone could explain me the issue, I'd really appreciate it
Thanks in Advance
I currently have PropertyApplication DbContext as below,
public partial class PropertyContext : DbContext
{
public PropertyContext()
{
}
public PropertyContext(DbContextOptions<PropertyContext> options)
: base(options)
{
}
public virtual DbSet<Address> Address { get; set; }
public virtual DbSet<BoundaryChangeEvent> BoundaryChangeEvent { get; set; }
I would like to inheritance from this PropertyDbContext. Is this being done correctly in the constructor? Attempting to make unit test pass below, it overrides save changes to bring in auditing user information. Just curious if specifically the constructor statements below look correct? Or should I try to attempt option 2 below with AuditablePropertyContext options?
public class AuditablePropertyContext : PropertyContext
{
private int _user;
public AuditablePropertyContext()
{
}
public AuditablePropertyContext(DbContextOptions<PropertyContext> options, UserResolverService userService)
: base(options)
{
_user = userService.GetUser();
}
public void ApplyCreatedBy()
{
var modifiedEntities = ChangeTracker.Entries<ICreatedByUserId>().Where(e => e.State == EntityState.Added);
foreach (var entity in modifiedEntities)
{
entity.Property("CreatedByUserId").CurrentValue = _user;
}
}
public override int SaveChanges()
{
ApplyCreatedBy();
return base.SaveChanges();
}
}
Option 2:
I was receiving error trying to conduct this,
public AuditablePropertyContext(DbContextOptions<AuditablePropertyContext> options, UserResolverService userService)
: base(options)
{
_user = userService.GetUser();
}
Error:
Error CS1503 Argument 1: cannot convert from 'Microsoft.EntityFrameworkCore.DbContextOptions IPTS.PropertyManagement.Infrastructure.Auditable.Data.AuditablePropertyContext' to 'Microsoft.EntityFrameworkCore.DbContextOptions IPTS.PropertyManagement.Infrastructure.Data.PropertyContext '
*Sometimes company utilizes SQL Server, sometimes InMemory, or SQLite
Unit Test is failing:
services.AddSingleton(a =>
{
var mock = new Mock<IUserResolverService>();
mock.Setup(b => b.GetUser()).Returns(5);
return mock.Object;
});
services.AddDbContext<PropertyContext>(
options => options.UseInMemoryDatabase("Ipts").UseQueryTrackingBehavior(QueryTrackingBehavior.TrackAll),
ServiceLifetime.Singleton);
services.AddSingleton<DbContext, PropertyContext>();
services.AddDbContext<AuditablePropertyContext>(
options => options.UseInMemoryDatabase("Ipts").UseQueryTrackingBehavior(QueryTrackingBehavior.TrackAll),
ServiceLifetime.Singleton);
services.AddSingleton<AuditablePropertyContext>();
services.RegisterMappingProfiles(new ApplicationServicesMappingProfile(),
new PropertyManagementDataMappingProfile());
return services;
}
Unit Test: Error
Message:
System.InvalidOperationException : No database provider has been configured for this DbContext. A provider can be configured by overriding the DbContext.OnConfiguring method or by using AddDbContext on the application service provider. If AddDbContext is used, then also ensure that your DbContext type accepts a DbContextOptions<TContext> object in its constructor and passes it to the base constructor for DbContext.
Stack Trace:
DbContextServices.Initialize(IServiceProvider scopedProvider, IDbContextOptions contextOptions, DbContext context)
DbContext.get_InternalServiceProvider()
DbContext.get_DbContextDependencies()
Change the constructor PropertyContext class to the following code:
public PropertyContext(DbContextOptions options)
: base(options)
{
}
then change the constructor AuditablePropertyContext class to the following code:
public AuditablePropertyContext(DbContextOptions options, UserResolverService userService)
: base(options)
{
_user = userService.GetUser();
}
notice: Delete the default constructor in both classes when you don't need it.
You could also provide the specialized DbContextOptions<Repo> only on the concrete subtype.
eg
public abstract class BaseRepo: DbContext
{
public BaseRepo(DbContextOptions options) : base(options)
{
}
}
public sealed class Repo : BaseRepo
{
public Repo(DbContextOptions<Repo> options) : base(options)
{
}
}
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 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.
On an ASP.NET Core 1.0.1 project, using Entity Framework Core and ASP.NET Identity, I have the following context:
public class Context : IdentityDbContext<User, Role, Int32, UserClaim, UserRole, UserLogin, RoleClaim, UserToken> {
public Context(DbContextOptions options) : base(options) { }
protected override void OnModelCreating(ModelBuilder builder) {
base.OnModelCreating(builder);
}
}
And the following entities:
public class User : IdentityUser<Int32, UserClaim, UserRole, UserLogin> { }
public class Role : IdentityRole<Int32, UserRole, RoleClaim> { }
public class RoleClaim : IdentityRoleClaim<Int32> { }
public class UserClaim : IdentityUserClaim<Int32> { }
public class UserLogin : IdentityUserLogin<Int32> { }
public class UserRole : IdentityUserRole<Int32> { }
public class UserToken : IdentityUserToken<Int32> { }
On Startup I have the following:
services.AddDbContext<Context>(x => x.UseSqlServer(connectionString, y => y.MigrationsHistoryTable("__Migrations")));
services
.AddIdentity<User, Role>()
.AddEntityFrameworkStores<Context, Int32>()
.AddDefaultTokenProviders();
When I run dotnet ef migrations add "FirstMigration" I get the following error:
An error occurred while calling method 'ConfigureServices' on startup
class 'WebProject.Startup'. Consider using IDbContextFactory to
override the initialization of the DbContext at design-time. Error:
GenericArguments[0], 'WebProject.User', on
'Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserStore`4[TUser,TRole,TContext,TKey]'
violates the constraint of type 'TUser'.
How to solve this problem?
I apologise for posting a partial answer but it will be usefull for many...
An error occurred while calling method 'ConfigureServices' on startup class
Your method Startup.ConfigureServices(...) is being called and it is throwing an exception. The exception probably happens because when running dotnet ef the application entry point is not Program.Main() as usual.
Try
dotnet ef migrations add "FirstMigration" --verbose
That will print the error message and you will be able to better understand the issue.