Single Context in ASP.NET MVC5 Entity Framework application - c#

As demonstrated in my last question here: "Define the key for this EntityType." when including attribute of type ApplicationUser
I need to use a single database context, whereas my current setup is one context (defined within IdentityModel.cs) for the login stuff and another context (defined externally) for all other database operations in the application.
How would I go about using a single context? I do not care about existing data.

You can certainly use single context.
Below is what i did:
public class DataContext : IdentityDbContext<ApplicationUser>
{
public DataContext()
: base("DefaultConnection")
{
}
public IDbSet<Project> Projects { set; get; }
public IDbSet<Task> Tasks { set; get; }
}
Then in account controller:
public class AccountController : Controller
{
public AccountController()
: this(new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new DataContext())))
{
}
when you create a new project:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection")
{
}
}
you get this which doesnt have much information, you can use this as well and add your dbsets. It has a reference from AccountController.

Just sub-class the data context used for ASP.NET Identity.

Related

Getting IdentityFramework, IDesignTimeDbContextFactory, .NetCore 2.1, and DbContext to play nice together

I have an existing .Net core project where I have the DBContext broken out into it's own library. In this project I use IDesignTimeDbContextFactory to create the DbContext for the class library. This allows me to use commands like add-migration and update-database despite the fact that there is no actual appsettings.json or app.config file to pull the connection string from.
The problem I have now is that I would like to do the same thing, only this time add in IdentityFramework. From what I can read online it looks like the standard way to do this is to abandon plain old DbContext and make everything inherit from IdentityDbContext. This seems to be OK right up until the point where I try to add the IDesignTimeDbContextFactory. IDesignTimeDbContextFactory expects DBContext and cannot use IdentityDbContext.
How can I have a class library that supports IdentityFramework and IDesignTimeDbContextFactory? Anyone have an example?
My Project Structure is:
.Web
.Api
.Models (Contains the Identity Models)
public class AppUser : IdentityUser
{
public string MyExtraProperty { get; set; }
}
public class AppRole : IdentityRole
{
public AppRole() : base() { }
public AppRole(string name) : base(name) { }
}
.Service (Contains the DbContext and Identity Framework)
ApplicationDbContext
public class ApplicationDbContext : IdentityDbContext<AppUser>
{
public ApplicationDbContext() : base("DefaultConnection", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
DBContextFactory (Does not like ApplicationDbContext)
public class DBContextFactory : IDesignTimeDbContextFactory<ApplicationDbContext>
{
public ApplicationDbContext CreateDbContext(string[] args)
{
var builder = new DbContextOptionsBuilder<ApplicationDbContext>();
builder.UseSqlServer(
#"Server=(localdb)\MSSQLLocalDB;Database=IdentityFrameWork;AttachDBFilename=IdentityFramework.mdf;Trusted_Connection=True;MultipleActiveResultSets=true;");
}
}
I need a working example of .Net Core and Identity framework in a separate .Net Standard class. Can anyone help me out?
Git Hub Link
Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityDbContext inherits from Microsoft.EntityFrameworkCore.DbContext as you can see here, which leads me to wonder if you are possibly using a prior version of Identity Framework and AspNet.Identity.EntityFramework6.IdentityDbContext?

Issues with DbContexts causing Errors

I currently have multiple projects with multiple DbContexts:
Projects (DbContext):
Model
DataAccessLayer (EntityContext)
ServiceLayer
Web (IdentityContext)
I have a Model in the Model's project called Department that is created by the EntityContext and i reference to it in the ApplicationUser.cs in the Web Project.
When the IdentityContext is trying to create the AspNet Tables it also tries to recreate the Department Table and i get the following error:
"There is already an object named 'Departments' in the database."
Here's my Identity Files:
The IdentityContext is my Web Project:
ApplicationDbContext.cs
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext() : base("EntityContext", throwIfV1Schema: false)
{
Configuration.LazyLoadingEnabled = false;
Configuration.AutoDetectChangesEnabled = false;
Configuration.ProxyCreationEnabled = false;
Configuration.ValidateOnSaveEnabled = false;
}
static ApplicationDbContext()
{
Database.SetInitializer(new MigrateDatabaseToLatestVersion<ApplicationDbContext, ApplicationDbInitializer>());
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
ApplicationDbInitializer.cs
public class ApplicationDbInitializer : DbMigrationsConfiguration<ApplicationDbContext>
{
public ApplicationDbInitializer()
{
AutomaticMigrationsEnabled = false;
#if DEBUG
AutomaticMigrationDataLossAllowed = true;
AutomaticMigrationsEnabled = true;
#endif
}
protected override void Seed(ApplicationDbContext context)
{
base.Seed(context);
}
ApplicationUser.cs
public class ApplicationUser : IdentityUser
{
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
return userIdentity;
}
/// <summary>
/// Linked Department Id
/// </summary>
public Guid? DepartmentId { get; set; }
/// <summary>
/// Linked Department Object
/// </summary>
[ForeignKey("DepartmentId")]
public virtual Department Department { get; set; }
}
I then commented out the "public DbSet Department { get; set; }" in the EntityContext and now it builds but when i try to access it i get this error:
"The entity type Department is not part of the model for the current context."
I understand both errors, but i can't figure out how to get them to be on the same context, as they are in different projects.
Traditionally in a situation like this I would rather you make your Context inside of the DataAccessLayer be the IdentityContext. Since you have ties between the IdentityContext and that particular context anyway, it gives you a single context to represent your application and doesn't introduce a shared context issue.
This would result in removing the DBContext from your web project, and changing the base class of your Context inside of the DataAccessLayer to become IdentityDbContext<ApplicationUser> rather than the current DbContext base class.
Most likely, this will result in you moving a few entities into the lower DLL, but again it makes that DLL more representative of the entire solution.

Error: default DbConfiguration instance was used by Entity Framework

I have a DAL project with this ApplicationContext.cs
[DbConfigurationType(typeof(MyConfiguration))]
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext() : base("DefaultConnection", throwIfV1Schema: false) { }
...
}
but when I instance it in an other project
using (var databaseContext = new ApplicationDbContext())
{ }
I get this error:
The default DbConfiguration instance was used by the Entity Framework
before the 'MyConfiguration' type was discovered. An instance of
'MyConfiguration' must be set at application start before using any
Entity Framework features or must be registered in the application's
config file. See http://go.microsoft.com/fwlink/?LinkId=260883 for
more information.
I tried to solve it updating the web.config file, inserting codeConfigurationType in node, but I get the same error.
Can anyone help me?
UPDATE
My custom class is this:
public class MyConfiguration : DbConfiguration
{
public MyConfiguration()
{
SetExecutionStrategy("System.Data.SqlClient", () => new System.Data.Entity.SqlServer.SqlAzureExecutionStrategy(2, TimeSpan.FromSeconds(30)));
}
}
What I understand from the error you should create an instance of ApplicationDbContext class on application start on Startup class
Startup class
app.CreatePerOwinContext(ApplicationDbContext.Create);
ApplicationDbContext class
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
this.Database.Create();
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
Also if you can share the full example it will be better.
SOLUTION
After reading this article Moving DbConfiguration:
https://msdn.microsoft.com/en-us/data/jj680699
I solved changing my Context class as below:
[DbConfigurationType("DAL.Context.MyConfiguration, DAL")]
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
}

Register IDbContext in Unity

I'm trying to register IDbContext in Unity but I'm getting this
error
"The type IDbContext does not have an accessible constructor."
UnityConfig.cs
public static void RegisterTypes(IUnityContainer container)
{
container.RegisterType<IProductServices, ProductServices>();
container.RegisterType(typeof(IRepository<>), typeof(Repository<>));
//---all ERROR below---
//container.RegisterType(typeof(IDbContext), typeof(DbContext));
//container.RegisterType<IDbContext>();
var connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["ApplicationContext"].ConnectionString;
container.RegisterType(typeof(IDbContext), typeof(DbContext), new ContainerControlledLifetimeManager(), new InjectionConstructor(connectionString));
}
IDbContext.cs
public interface IDbContext
{
IDbSet<TEntity> Set<TEntity>() where TEntity : BaseEntity;
int SaveChanges();
}
ApplicationContext.cs
public partial class ApplicationContext : DbContext, IDbContext
{
public ApplicationContext()
: this("name=ApplicationContext")
{
}
public ApplicationContext(string name)
: base(name)
{
}
public DbSet<Product> Products { get; set; }
public new IDbSet<TEntity> Set<TEntity>() where TEntity : BaseEntity
{
return base.Set<TEntity>();
}
}
You have 2 constructor in your DbContext therefore Unity can not initialize it simply remove ApplicationContext(string name) constructor an let default exist.
public partial class ApplicationContext : DbContext, IDbContext
{
public ApplicationContext()
: this("name=ApplicationContext")
{
}
public DbSet<Product> Products { get; set; }
public new IDbSet<TEntity> Set<TEntity>() where TEntity : BaseEntity
{
return base.Set<TEntity>();
}
}
An in unity side write:
container.RegisterType<IDbContext, ApplicationContext>();
But if you want keep your constructors try this:
container.RegisterType<IDbContext, ApplicationContext>(
new InjectionConstructor("YourConnectionStringYouProvidedSomewhere"));
So you could do something like this:
public class MyContext : DbContext
{
public MyContext() : base("connectionstringproperty") { }
}
public interface IMyContextFactory : IDbContextFactory<MyContext>
{
}
public class MyContextFactory : IMyContextFactory
{
public MyContext Create()
{
return new MyContext();
}
}
Register Like this:
container.RegisterType<IMyContextFactory, MyContextFactory>(new PerResolveLifetime());
And use in class like so:
public class UserService
{
IMyContextFactory contextFactory
public UserService(IMyContextFactory contextFactory)
{
contextFactory = contextFactory;
}
public List<User> GetUsers()
{
using(var context = this.contextFactory.Create())
{
return context.Users.ToList();
}
}
}
I don't use repositories until I really have to (which is nearly never :) ). But if UserService was a repository class instead (it kinda is at the moment) the implementation would be the same using constructor injection.
DbContext
If you want testability, EF 6 did a good job in making all methods and properties virtual so you could override them rather than creating an interface to back it.
For dependency injection, EF created IDbContextFactory<T> where T : DbContext
You should be inheriting from DbContext so that you can create some tables in the database. When you inherit you can create different constructors depending on your needs, the easiest I find is to create a parameterless one and call the base constructor overload which takes the web.config connection string property.
DbContext does not have a public, default constructor. The most minimial public constructor is one which takes a string value containing the name of the connection string to use (defined in your app.config or web.config file) or the actual connection string to the database itself.
If you have such a connection string defined in your app.config or web.config (or you have a suitable default connection string to be used throughout your application), try the following Unity registration:
// In the code below, "connection_string" is either the full database
// connection string or the name of the connection string as defined
// in the app.config or web.config files
//
// NOTE: This method requires a LifetimeManager.
// I used the default "singleton container" provided by unity.
// This may not be appropriate for your application--please use an
// appropriate container lifetime manager.
container.RegisterType(typeof(IDbContext), typeof(DbContext),
new ContainerControlledLifetimeManager(),
new InjectionConstructor("connection_string"));
If you have multiple databases you will need to connect to, then you may want to subclass DbContext (e.g. MyDB1DbContext, MyDB2DbContext) or use named registrations. If you subclass DbContext, then you could have the subclasses implement a public default constructor, passing a suitable connection string to the base DbContext class--at which point, your context registrations become simpler.
For one application-wide DbContext though, I'd probably just use the code snippet above.
Update after OP's Update
Unless you'll need to use multiple connection strings with your application-defined DbContext, I would simply define as follows:
public class ApplicationDbContext() : System.Data.Entity.DbContext
{
public ApplicationDbContext() : base("my_connection_string_name")
{ }
... // Rest of context class definition
}
Then, to register:
// NOTE: Can also use the generic version--may need to also change the
// lifetime management of the context depending on your application.
container.Register(typeof(IDbContext), typeof(ApplicationDbContext),
new ContainerControlledLifetimeManager());

ASP.net Identity Custom Tables using OwinContext

I'm trying to implement custom tables to store users/roles etc. in a SQL server DB using Entity framework 6.
I've create a base DbContext that derives from IdentityDbContext
public class MainContext : IdentityDbContext<ApplicationUser>
{
public MainContext()
: base("name=Main")
{
}
public static MainContext Create()
{
return new MainContext();
}
}
I also have a custom user class that inherits IdentityUser
public class ServiceUser : IdentityUser{
}
In the ConfigureAuth the defualt code was:
public void ConfigureAuth(IAppBuilder app)
{
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
..
}
I want to be able to use my MainContext here instead of the ApplicationDBContext, when I try the following
app.CreatePerOwinContext(MainContext.Create());
I get an error
'The type arguments for method
'Owin.AppBuilderExtensions.CreatePerOwinContext(Owin.IAppBuilder,
System.Func)' cannot be inferred from the usage. Try specifying the
type arguments
explicitly.
'
Considering the default ApplicationDbContext looks like:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("Main", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
I cannot see what is different that would cause this error?
Try with
app.CreatePerOwinContext<IdentityDbContext>(MainContext.Create);
Foolish mistake, it should have been
app.CreatePerOwinContext(MainContext.Create);
and not
app.CreatePerOwinContext(MainContext.Create());

Categories

Resources