You need to have a context class that derives from DbContext when you're using Entity Framework.
Asp.net Identity uses EF and the default template creates the below class:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection", false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
This class does not derive directly from DbContext. For my own data (my classes that I want to persist to the db) should I create my own db context class?
If I want to do an operation that will update both the identity user and one of my own classes, I need to use both contexts. So this does not feel very natural.
Should I keep using the ApplicationDbContext class as context for my own classes as well? Would that work?
What is the best method to use EF for my own classes while using identity?
Use a single Context class inheriting from IdentityDbContext. See this answer for more info.
You need to add DbSets for all your classes into the ApplicationDbContext.
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection", false)
{
}
//Public DBSets
public DbSet<LeaveApplication> LeaveApplications { get; set; }
public DbSet<LeaveStatus> LeaveStatus { get; set; }
public DbSet<Department> Departments { get; set; }
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
Related
I am working with a database where I have more than 75 tables and I am using the repository and unit of work patterns with Entity Framework in an ASP.NET MVC project. I am little bit confused and some query in my mind about object creation. When UnitOfWork initializes, it creates object for all table's entity which is present in UnitOfWork. So it can be heavy for application load.
Here is the interface of unit of work:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Application.Repository;
using Application.Repository.General;
namespace Application.UnitOfWorks
{
public interface IUnitOfWork : IDisposable
{
IGeneralRegionMasterRepository GeneralRegionMasters { get; }
IGeneralSubRegionMasterRepository GeneralSubRegionMasters { get; }
IGeneralCountryMasterRepository GeneralCountryMasters { get; }
IGeneralStateMasterRepository GeneralStateMasters { get; }
IGeneralCityMasterRepository GeneralCityMasters { get; }
int Complete();
}
}
Implementation:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Application.EntityFramework;
using Application.Repository;
using Application.Repository.General;
namespace Application.UnitOfWorks
{
public class UnitOfWork : IUnitOfWork
{
public readonly InventoryDbContext _context;
public UnitOfWork(InventoryDbContext context)
{
_context = context;
GeneralRegionMasters = new GeneralRegionMasterRepository(_context);
GeneralSubRegionMasters = new GeneralSubRegionMasterRepository(_context);
GeneralCountryMasters = new GeneralCountryMasterRepository(_context);
GeneralStateMasters = new GeneralStateMasterRepository(_context);
GeneralCityMasters = new GeneralCityMasterRepository(_context);
}
public IGeneralRegionMasterRepository GeneralRegionMasters { get; private set; }
public IGeneralSubRegionMasterRepository GeneralSubRegionMasters { get; private set; }
public IGeneralCountryMasterRepository GeneralCountryMasters { get; private set; }
public IGeneralStateMasterRepository GeneralStateMasters { get; private set; }
public IGeneralCityMasterRepository GeneralCityMasters { get; private set; }
public int Complete()
{
return _context.SaveChanges();
}
public void Dispose()
{
_context.Dispose();
}
}
}
I want to know about performance effect of it on application. Thank you in advance for help.
I've run into the same problem that you are describing in the past. The structure of the code just feels really heavy since you are creating new instances of 70 repositories even though you may only need one of them. This is why I've just started to avoid adding my own UoW and Repositories when using EF directly because EF already has Repositories and UoW built in (DbSets = Repos, Save Changes does UoW save at the end of all DbSet changes). If you don't want to code directly against a DbContext, just have your DbContext implement the IUnitOfWork interface directly and go off of that. Also have all your DbSets exposed on that UnitOfWork. Then you could have it also implement IMyDbContext and have that expose the DbSets and have this interface also implement IUnitOfWork (or have DbContext -> IMyDbContext -> IUnitOfWork) or break them up if you don't want repo code having access to Save at the bottom. This just ends up making it easier in the long run. No weird code to maintain, no classes to create. If you switch to not use EF, you can still use those same interfaces behind the scenes and the only thing that would have to change would be the DbSet implementation (maybe you can even get that to be generic - create your on DbSets that implement another interface, too). Personally, I'm going down the CQS path so I don't have to worry about repos or UoW anymore. :)
Edit
Example the best I can here! :)
public interface IUnitOfWork
{
int Complete();
Task<int> CompleteAsync();
}
public interface IInventoryDbContext : IUnitOfWork
{
DbSet<GeneralRegionMaster> GeneralRegionMasters { get; }
DbSet<GeneralSubRegionMaster> GeneralSubRegionMasters { get; }
... etc
}
public class MyDbContext : DbContext, IInventoryDbContext
{
public DbSet<GeneralRegionMaster> GeneralRegionMasters { get; set; }
public DbSet<GeneralSubRegionMaster> GeneralSubRegionMasters { get; set;
}
public int Complete() => this.SaveChanges();
public Task<int> CompleteAsync() => this.SaveChangesAsync();
}
If you did a controller level only:
public class MyController : Controller
{
private readonly IInventoryDbContext _context;
public MyController(IInventoryDbContext context)
{
_context = context;
}
public JsonResult CreateGeneralRegionMaster(GeneralRegionMaster entity)
{
_context.GeneralRegionMaster.Add(entity);
var result = _context.Complete();
return Json(result == 1);
}
}
Again, you could do something different for the DbSets and do this instead:
public interface IRepo<T> where T: class
{
// Expose whatever methods you want here
}
public class MyDbSet<T> : DbSet<T>, IRepo<T> where T: class
{
}
Then this changes:
public interface IInventoryDbContext : IUnitOfWork
{
IRepo<GeneralRegionMaster> GeneralRegionMasters { get; }
IRepo<GeneralSubRegionMaster> GeneralSubRegionMasters { get; }
... etc
}
public class MyDbContext : DbContext, IInventoryDbContext
{
public MyDbSet<GeneralRegionMaster> GeneralRegionMasters { get; set; }
public MyDbSet<GeneralSubRegionMaster> GeneralSubRegionMasters { get; set; }
public IRepo<GeneralRegionMaster> GeneralRegionMastersRepo => GeneralRegionMasters;
public IRepo<GeneralSubRegionMaster> GeneralSubRegionMastersRepo => GeneralSubRegionMasters;
public int Complete() => this.SaveChanges();
public Task<int> CompleteAsync() => this.SaveChangesAsync();
}
Re:
When UnitOfWork initializes, it creates object for all table's entity which is present in UnitOfWork. So it can be heavy for application load.
You don't need to initialize all the repo instances in the UoW constructor.
You can create them when they are required in the corresponding getters (lazy initialization):
private IGeneralRegionMasterRepository _generalRegionMasters;
public IGeneralRegionMasterRepository GeneralRegionMasters {
get {
if (_generalRegionMasters == null) {
_generalRegionMasters = new GeneralRegionMasterRepository(_context);
}
return _generalRegionMasters;
}
}
I followed the ASP.NET MVC tutorial to build the ASP.NET MVC project with EF code-first.
I've created the SchoolContext and use EF code-first to create the Database for my current project.
public class SchoolContext:DbContext
{
public SchoolContext(DbContextOptions<SchoolContext> options) : base(options)
{
}
public DbSet<Course> Courses { get; set; }
public DbSet<Enrollment> Enrollments { get; set; }
public DbSet<Student> Students { get; set; }
public DbSet<Department> Departments { get; set; }
public DbSet<Instructor> Instructors { get; set; }
public DbSet<OfficeAssignment> OfficeAssignments { get; set; }
public DbSet<CourseAssignment> CourseAssignments { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Course>().ToTable("Course");
modelBuilder.Entity<Enrollment>().ToTable("Enrollment");
modelBuilder.Entity<Student>().ToTable("Student");
modelBuilder.Entity<Department>().ToTable("Department");
modelBuilder.Entity<Instructor>().ToTable("Instructor");
modelBuilder.Entity<OfficeAssignment>().ToTable("OfficeAssignment");
modelBuilder.Entity<CourseAssignment>().ToTable("CourseAssignment");
modelBuilder.Entity<CourseAssignment>()
.HasKey(c => new { c.CourseID, c.InstructorID });
}
}
Now I want to add the Authorization function to this project using ASP.NET Identity.
I notice that I have the ApplicationDbContext inside my project, how can I migrate the ApplicationDbContext to my existing SchoolContext? Or I need to create another database for the ApplicationDbContext?
Inherit SchoolContext from IdentityDbContext<ApplicationUser> then add aspnet identity tables to modelBuilder:
public class SchoolContext:IdentityDbContext<ApplicationUser>
{ ...
and OnModelCreating method changes like this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<IdentityUser>()
.ToTable("dbo.AspNetUsers");
modelBuilder.Entity<ApplicationUser>()
.ToTable("dbo.AspNetUsers");
modelBuilder.Entity<IdentityRole>()
.ToTable("dbo.AspNetRoles");
modelBuilder.Entity<ApplicationRole>()
.ToTable("dbo.AspNetRoles");
modelBuilder.Entity<IdentityUserClaim>().ToTable("dbo.AspNetUserClaims");
modelBuilder.Entity<IdentityUserRole>().ToTable("dbo.AspNetUserRoles");
modelBuilder.Entity<IdentityUserRole>().HasKey((IdentityUserRole r) => new { UserId = r.UserId, RoleId = r.RoleId }).ToTable("dbo.AspNetUserRoles");
modelBuilder.Entity<IdentityUserLogin>().ToTable("dbo.AspNetUserLogins");
...
Then you should replace all ApplicationDbContext to SchoolContext entire your solution
You can pass the same connection string to both the dbContext then all tables of both the context will be created in same database.
You can implement authorization on your controllers using [Authorize] attribute.
I am trying to update my database by using automatic migrations. I have recently added some properties to the Identity Model:
namespace CISC_Website.Models
{
public class ApplicationUser : IdentityUser
{
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public bool ConfirmedEmail { get; set; }
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("CISCDb")
{
}
}
}
However whenever I run the update-database command these changes are not pushed as my user tables are still the same as when they were first created.
Here is the contents of my migrations folder:
namespace CISC_Website.Migrations
{
using System;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using System.Linq;
using System.Collections.Generic;
internal sealed class Configuration : DbMigrationsConfiguration<CISC_Website.Models.CISCDb>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
}
protected override void Seed(CISC_Website.Models.CISCDb context)
{
//seed data...
}
}
}
I know originally when the enable migrations command was run, the identity model was represented as such:
namespace CISC_Website.Models
{
public class ApplicationUser : IdentityUser
{
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection")
{
}
}
}
Any idea how i can get these tables recognized??
Your application has two DbContexts: CISCDb and IdentityDbContext and you have configured migrations only for your CISCDb context. Therefore changes in the Identity context are not included in the CISCDb migrations.
You should be able to configure the migrations for the IdentityContext the same way you did for the other context to get it working.
I am using the standard MVC template that comes with VS 2013. It has a neat membership provider that makes using external logins (Google, Facebook etc) a breeze. There are also tutorials on how to extend the IdentityUser model to add new properties such as date of birth.
I would like to add more tables (of my application) to the already coded database context so as to enjoy the same code first migration features. How do I do it? The current db context is defined as follows:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
you are using asp.net MVC5 identity 2 then ApplicationDbContext already there in IdentityModels.cs .So you can add table (DbSet) like this.
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("ApplicationDbContext", throwIfV1Schema: false)
{
}
public DbSet<Department> Departments { get; set; }
public DbSet<Student> Students { get; set; }
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
The stock asp.net mvc 5 application creates the application users, aka identity users in a separate context, named a file called "IdentityModels.cs" - it looks like this
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
}
I am attempting to put the Application users in a regular data context, i.e. something like this
public class BlogProphetContext : DbContext
{
public DbSet<ApplicationUser> ApplicationUsers { get; set; }
public DbSet<Answer> Answers { get; set; }
public DbSet<Question> Questions { get; set; }
public DbSet<Tag> Tags { get; set; }
}
However, every time I do that I get the following error every time I try to create an account
The UserId field is required
In the AccountController.cs when I attempt to execure the following line of code
result = await UserManager.AddLoginAsync(user.Id, info.Login);
I get the feeling my approach is wrong, and that I cannot have the ApplicationUsers in the main data context file without some sort of external chicanery - does anyone know of some way to do this? All of the files are up to date.
This was a bit too easy - it turns out that all you have to do it remove the
<ApplicationUser>
when you call the context and everything turns out like you might expect (i.e. the MVC assumptions and the developers assumptions (in this case, mine) sync.
Here is is working properly
public class MyContext : IdentityDbContext
{
public MyContext()
: base("DefaultConnection")
{
}
public DbSet<ApplicationUser> ApplicationUsers { get; set; }
public DbSet<Answer> Answers { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
}
}
In this case, if you dont want your ApplicationUser to be associated with asp.net Identity the easiest thing would be to just remove the inheritance from the applicationUser class.
Go from ApplicationUser : IdentityUser to just ApplicationUser, then in that class just create an Id property.
You wont have to use IdentityDBContext then.
Of course you will have to totally rewrite AccountController, or get rid of it. You cant use UserManager anyways as thats linked to IdentityUser
Then obviously make sure you add your migration/update database if you are working with EF.
If you do want to keep the account controller as is and keep IdentityDbContext but add other entities then thats easy, just do this:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext() : base("DefaultConnection")
{
}
public DbSet<Dept> Dept{ get; set; }
public DbSet<Course> Course{ get; set; }
public DbSet<Etc> Etc{ get; set; }
}
Ensure that your ApplicationUser class inherits from IdentityUser.
Then you need to configure your context so that it maps all the ASP.NET Identity classes correctly.
These are:
ApplicationUser (your class)
IdentityUserClaim
IdentityUserLogin
IdentityUserRole
IdentityRole
Details of this is a bit too complicated to post here, but I have made a sample project that works with the release version of ASP.NET Identity. You can see it here:
https://github.com/onybo/Asp.Net-Identity-sample-app/tree/master/CustomUser
I am not sure if this will help you or if i fully understand your questions...but if you want try use your own "model" then change:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("PUT IN YOUR MODEL CONN HERE")
{
}
I do not know how much more than this you can configure or if this will really change the context.