ASP.net Identity Custom Tables using OwinContext - c#

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());

Related

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>
{
}

Using ApplicationDbContext with DI from appsettings.json

I am trying to abstract any connection information away from my ApplicationDbContext class so that I can take advantage of different databases for development, staging, production. I start by registering a service from Startup.cs
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
My ApplicationDbContext class:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
}
}
When running this application I get the following error:
InvalidOperationException: Could not create an instance of type 'SquadApps.Data.ApplicationDbContext'. Model bound complex types must not be abstract or value types and must have a parameterless constructor.
So naturally I tried adding a parameterless constructor
public ApplicationDbContext() { }
Now getting another error:
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 object in its constructor and passes it to the base constructor for DbContext.
If I go back to having a connection string stored in the ApplicationDbContext class like so:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("........");
}
Then everything works fine but obviously this is not ideal and probably a bad practice. I think there is something i'm missing about the DI process and any suggestions or advice would be appreciated.
The solution turned out to be how I was trying to call the DI. I had incorrectly assumed DI would be able to be called per each IActionResult inside my controllers but in fact it must occur within the constructor of the controller. This makes the DI available to all IActionResult methods within the controller.
Example of the working DI call:
public class HomeController : Controller
{
private readonly ApplicationDbContext _ctx;
private readonly CompanySettings _companySettings;
public HomeController(ApplicationDbContext ctx, IOptions<CompanySettings> settings)
{
_ctx = ctx;
_companySettings = settings.Value;
}
public IActionResult Index()
{
var model = new HomeViewModel();
// _ctx and _companySettings can be used here
return View(model);
}
}

How to create an IdentityDbContext with a variable connection string?

Having my own implementation of the IdentityDbContext, I would like it to be able to connect to a custom database according to the user's choice. So I have created 2 constructors for both default database and user selected database.
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("Users", throwIfV1Schema: false)
{
//Aici am adaugat
Configuration.ProxyCreationEnabled = true;
Configuration.LazyLoadingEnabled = true;
}
public ApplicationDbContext(String connectionName)
: base(connectionName, throwIfV1Schema: false)
{
//Aici am adaugat
Configuration.ProxyCreationEnabled = true;
Configuration.LazyLoadingEnabled = true;
}
}
My problem is now the way I would bring the custom connectionName into the class.
The constructor is called in this method:
public static string conn;
public static ApplicationDbContext Create()
{
if(conn == null)
return new ApplicationDbContext();
else
return new ApplicationDbContext(conn);
}
Using session variables is impossible because in this context HttpContext.Current is null. Adding a string argument to the Create method is also impossible, because right in the Startup class, before any user selection, Owin decides on using a default database:
app.CreatePerOwinContext(() => ApplicationDbContext.Create());
Even passing an argument there would not help because it wouldn't have been chosen by the user.
What can I do about it?
Thank you very much!
I would use cookies.
Try this:
Startup.Auth.cs
app.CreatePerOwinContext<ApplicationDbContext>(ApplicationDbContext.Create);
ApplicationDbContext.cs
public static ApplicationDbContext Create(IdentityFactoryOptions<ApplicationDbContext> options, IOwinContext context)
{
// Do things here.
string choice = context.Request.Cookies...;
// Make sure that the cookie is correct.
return new ApplicationDbContext(connectionName);
}
What you can do is have a static class that uses a provider to produce the connection string.
You can then let the user select the connection and store it wherever it works for you. The provider will read the connection string and feed it to the context initializer.
This way you can set the provider before all this takes place. You can mock it if you want to for unit testing purposes.
I'm writing from my cellphone. I'll improve the answer with code samples when I'm in front of a desktop.
Update (I am now in front of a desktop):
The easiest way to implement this is to modify the context class by adding a constructor that takes a nameOrConnectionString parameter and having the Create() method call that constructor with a provider that gets you the connection string:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("aspNetIdentity", throwIfV1Schema: false)
{
}
public ApplicationDbContext(string nameOrConnectionString)
: base(nameOrConnectionString)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext(ConnectionStringProvider.ConnectionString);
}
}
The ConnectionStringProvider class is a static class that holds the connection string for you:
public static class ConnectionStringProvider
{
public static string ConnectionString { get; set; }
}
You can then set the connection string upon startup to a default and let the user change it. Upon change, store in this property.
Mind you, this is a simplistic answer meant to indicate a possible solution. Keep in mind that since the class is static, only one instance will exist and the connection string will be served to all callers so you need to account for that with concepts like dependency injection and session/user tracking.
For instance, the class could use an internal provider that is managed by a Dependency Resolver. You then let the DI container manage the scope of the provider.

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());

Single Context in ASP.NET MVC5 Entity Framework application

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.

Categories

Resources