Asp Identity Custom context - asp.net-mvc

I am building a single page application, so I used the visual studio default template.
When It was on development I had 2 databases Entityframework.mdf and Identity.mdf, because thats what the default configuration does, but now I need relation ship with the users and I can't reach them easly because they are in another database.
in an MVC template you can easly do it like this:
public class ApplicationUser: IdentityUser
{
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection")
{
}
public DbSet<CustomTableItem> CustomTable{ get; set; }
//You can add more tables
}
when you use the single page application it configures the user management in a way I don't understand.
UserManagerFactory = () => new UserManager<IdentityUser>(new UserStore<IdentityUser>());
then from this article
This code uses the default constructor for the UserStore class, which will create a new instance of an IdentityDbContext object since an object isn’t supplied. If you want to use your own IdentityDbContext derived class, like the MVC 5 project does, you can modify the above initialization code and pass in your own context.
it says I can modify it but it does not show how :(, and I have tried but I can’t make it work. This is what I am trying to do
UserManagerFactory = () => new UserManager<ApplicationUser>(new UserStore<ApplicationUser>());
what am I missig?

If you use default constructor (with not parameters) for UserStore class, this happens:
public UserStore()
{
this..ctor((DbContext) new IdentityDbContext());
this.DisposeContext = true;
}
Identity framework creates it's own database context for you with default connection string and no relation to your own models or DbContext.
What Scott says in his article is that UserStore has a constructor defined like this:
public UserStore(DbContext context)
{
base..ctor(context);
}
In other words you can supply your DbContext as a parameter into the constructor of UserStore:
UserManagerFactory = () => new UserManager<ApplicationUser>(
new UserStore<ApplicationUser>(new ApplicationDbContext()))
Where ApplicationDbContext is defined as you describe in the question.
You'll need to create a migration on ApplicationDbContext that will create Identity tables in Entityframework.mdf. Then you'll have to move data from Identity.mdf into your main database. Connect to both datbases and run something like this:
insert into EntityFramework.dbo.IdenetityUsers
select * from Identity.dbo.IdentityUsers
However, I've only done data migration from one DB to another one within single SQL Server instance, not between LocalDbs (I presume you used these). So this method might not work and you'll have to export data from Identity.mdf into csv files and import them into EntityFramework.mdf

Related

Onion architecture : respecting dependencies in the MVC Layer of the application

I am making a website using ASP.NET MVC and an onion architecture. I have the following architecture:
Domain : Entities / Domain Interfaces
Repository : Generic repository (for now) using Entity Framework Code First Approach
Service : Generic Service that calls the Repository
MVC
Now I am trying to create a method in my controller to start testing the methods I have implemented in Repository and Service, and I am having a hard time as to what I am allowed to create in this controller. I want to test a simple Get method in the Repository, but to do that I need GenericService object and GenericRepository object in my controller. To demonstrate what I mean here's a snippet of my GenericRepository(I will skip the interfaces):
public class GenericRepository<T> : IGenericRepository<T> where T : class
{
private readonly PrincipalServerContext context;
private DbSet<T> entities;
public Repository(PrincipalServerContext context)
{
this.context = context;
entities = context.Set<T>();
}
}
Now my GenericService:
public class GenericService<T> : IGenericService<T> where T : class
{
private IRepository<T> repository;
public GenericService(IRepository<T> repository)
{
this.repository = repository;
}
public T GetEntity(long id)
{
return repository.Get(id);
}
}
And finally, my question, am I allowed to create these objects in my controller as follows (using my dbcontext called PrincipalServerContext):
public class NavigationController : Controller
{
private IGenericService<DomainModelClassHere> domainService;
private IGenericRepository<DomainModelClassHere> domainRepo;
private PrincipalServerContext context;
public ActionResult MyMethod(){
context = new PrincipalServerContext();
domainRepo = new GenericRepository<DomainModelClassHere>(context);
domainService = new GenericService<DomainModelClassHere>(domainRepo);
if(domainService.GetEntity(1)==null)
return View("UserNotFound");//Just as an example
return View();
}
}
Is this allowed? According to Jeffrey Palermo, UI can depend on Service and Domain so I don't know about the Repository. Technically I am not using methods from repository, but I do need to add a reference to the project.
If I can't then how can I create a new GenericService if I don't have a GenericRepository? Is there a better way to instantiate my objects ?
EDIT I think the answer to my question resides in Startup.cs where I can put something like service.addScoped(typeof(IGenericRepository<>),typeof(GenericRepository<>));
but I 'm not sure about this, any ideas?
I'll answer this on my own if ever someone encounters the same problem. There are configuration methods we can use to create instances of classes when needed. In the Startup.cs file you have to add ConfigureServices(IServiceCollection services) method and inside there are several methods that can be applied to services to create these instances. For example you can use:
services.AddTransient(IGenericRepository, GenericRepository)
What is the difference between services.AddTransient, service.AddScope and service.AddSingleton methods in Asp.Net Core 1? (this link explains differences between methods).
AddTransient is good in my case because it creates an instance of an object through the whole lifespan of the application, which is what I need. This means UI is dependant on the rest of the solution, because Startup.cs needs to know about the Repositories as well as the Services.
A pretty good answer can be found here :Onion Architecture : Can UI depend on Domain.

Add custom column in AspNetRoles results in Invalid Column Name Discriminator

I'm using ASP.NET MVC5 with Identity 2 framework, Database first
I'm trying to custom AspNetRoles, by adding a column called MyCustomColumn
However, my app crashes because :
Invalid Column Name Discriminator
There is a lot of resources on SO and elsewhere on the web, but most of them are with CodeFirst approach and I can't use them in my app.
How to deal with it ?
Actually, none of this is necessary. Most likely you failed to update your context to inherit from IdentityDbContext<TUser, TRole, TKey, TUserLogin, TUserRole, TUserClaim>, rather than the default of IdentityDbContext<TUser>. Since you did not pass your custom role entity as the TRole type parameter, the context instead uses IdentityRole as the class type. It then creates a table for IdentityRole, sees that your custom role inherits from IdentityRole, and therefore adds the Discriminator column so that it can tell the different between an instance of IdentityRole and your custom role, in the database (single table inheritance is the default strategy EF employs).
This will technically work, but your custom role will never actually be utilized. Use the right generic abstract context class, and you'll be fine.
For what it's worth, you should do away with the EDMX stuff, as well. It's deprecated, buggy, and unnecessary. Despite the name, "Code First" can be used with an existing database or to create a new one.
DON'T
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
DO
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, CustomRole, string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>
Ok, as I spend some hours to find a solution, I post it here, if it could help someone else.
First, in AspNetRoles, create your custom column AND a column called Discriminator (which is a nvarchar(max) ). Update your .edmx
Then, we have to create a class which inherits from IdentityRole. We will use this class to access our custom column we just created :
In Models folder
public ApplicationRole()
: base() { }
public ApplicationRole(string name, long myCustomValue)
: base(name)
{
MyCustomValue = myCustomValue;
}
public virtual long MyCustomValue { get; set; }
Then, let's create a class which inherits from RoleManager<ApplicationRole>.
I placed it in IdentityConfig.cs, but maybe it's a best practice to place it elsewhere...
For information, I get inspired by this blog, Re-Implementing RoleStore and ApplicationRoleManager paragraph
public class ApplicationRoleManager : RoleManager<ApplicationRole>
{
public ApplicationRoleManager(
IRoleStore<ApplicationRole, string> roleStore)
: base(roleStore)
{
}
public static ApplicationRoleManager Create(
IdentityFactoryOptions<ApplicationRoleManager> options, IOwinContext context)
{
return new ApplicationRoleManager(
new RoleStore<ApplicationRole>(context.Get<ApplicationDbContext>()));
}
}
ApplicationRoleManager has a constructor which calls our previously created ApplicationRole class.
Now we have to register our ApplicationRoleManager at startup, so we have to add this line after the others CreatePerOwinContext
In App_Start\Startup.auth.cs, ConfigureAuth(IAppBuilder app) method
app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
So now we can use our ApplicationRoleManager, correctly instanciated :
var rm = new ApplicationRoleManager(new RoleStore<ApplicationRole>(new ApplicationDbContext()));
And then use it as we want
var roleName = rm.FindByName("Admin");
string myCustomData = roleName.CustomData.ToString();

Use the same database for both my custom entities and identity context?

I want to make an MVC website with user logins/authentication etc.
I'm using EF CodeFirst to create my database.
If I create a new MVC project and select Authentication: Individual User Accounts, it will create a new project with an already existing IdentityDbContext etc.
Am I meant to continue using this along with my own DbContext? One context for my projects entities, and the other context for the Identity entities? Does this mean I'll have two separate databases, or can I give them both the same connection string?
I know this may be an open ended question, but are there any good resources/tutorials for AspNet Identity?
So far I've only been finding resources about AspNet Identity itself, and not how to integrate it with the rest of the project/database
You can specify the same connection string for both of your custom models context and identity context, all you have to do is to change the connection string in the constructor of the ApplicationDbContext class that resides in IdentityModels.cs file like so:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("YouCustomConnectionString", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
and the tables needed for identity will be created in the same database as your other entities, as for resource there is a good set of articles on identity here.

Enable Migrations in Multi-Tier MVC Application

I am creating a new application using MVC 5 (Razor Views), Entity Framework 6 (Code First), ASP.Net Identity 2.0 and Web API. Trying to create a decent de-coupled architecture, I wanted to enable migrations in my data layer, but I have a misunderstanding of how migrations work. Here's my basic solution architecture:
MyApplication Solution
|-Domain Project
|-Entity1
|-Entity2
|-Entity3
|-Data Layer Project (References Domain Project)
|-DbContext (Inherits from IdentityDbContext)
|-Migrations Folder
|-Service Layer Project (Web API)
|-Business Layer Project
|-MVC Project
|-Views
As shown above, the Data Layer Project I have enabled migrations by executing the Enable-Migrations command in the Package Manager Console. The problem is, it only scripts the models related to Identity (AspNetUserRoles, AspNetUserLogins, etc.). How does a migration know to include an entity? I need it to script the models in my Domain project and not quite sure how to tell EF/Migrations to do so.
UPDATE:
Per a request, my simple (out-of-the-box via scaffolded by new project) DbContext class below:
public class MyDbContext : IdentityDbContext<ApplicationUser>
{
public MyDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public static MyDbContext Create()
{
return new MyDbContext();
}
}
The problem is, it only scripts the models related to Identity
(AspNetUserRoles, AspNetUserLogins, etc.). How does a migration know
to include an entity?
The migration "knows" what to script as it scripts on a per context basis. Look at the Configuration.cs class in the Migrations folder and you will notice that it is a generic class with the DbContext type passed in as follows:
internal sealed class Configuration : DbMigrationsConfiguration<MyDbContext>
So it "knows" because you're explicitly telling it by passing in a MyDbContext generic type parameter.
So if you want to add your own entities classes for migration then you should have a DbSet for each of the entities in your DbContext. i.e.
public class MyDbContext : IdentityDbContext<ApplicationUser> {
public DbSet<Entity1> Entity1s {get;set;}
public MyDbContext()
: base("DefaultConnection", throwIfV1Schema: false){}
public static MyDbContext Create()
{
return new MyDbContext();
} }
If you don't want to reuse MyDbContext for your custom entites then you can create another context, and add your entity DbSets to that. But then you will have to explicitly enable and update the migrations as per How do I enable EF migrations for multiple contexts to separate databases?

moving mvc5 account controller code to separate project

I have a solution that contains a WebApi2,MVC5 & DAL project (all RTM).
I am wanting to use the new membership bits that are now baked-in, but I don't like all the account stuff being all in the account controller. Doing a file new project (asp.net) has all of the membership stuff coupled to the account controller.
Within my DAL I am using EF6 as I like the ideal of code-first as it suits what I am trying to do. I am trying to take the account controller code and move it into my separate project.
My context within the DAL is nice and simple (taken from the MVC site)
public class ApplicationUser : IdentityUser
{
//a user can belong to multiple stores
public virtual ICollection<StoreModel> Stores { get; set; }
}
public class DataContext : IdentityDbContext<ApplicationUser>
{
public DataContext(): base("name=DefaultConnection")
{
}
public DbSet<Business> Businesses { get; set; }
public DbSet<ConsumerModel> Consumers { get; set; }
public DbSet<StoreModel> Stores { get; set; }
}
From my account controller within my login actionresult I try
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (ModelState.IsValid)
{
var user = await UserManager.FindAsync(model.UserName, model.Password);
if (user != null)
{
It throws an error with User.FindAsync
The entity type ApplicationUser is not part of the model for the
current context.
What do I need to do to allow ApplicationUser to be used in the current context?
I have done something similar. In order to implement separation of concerns, I fetch the UserManager from my Repository and then use it in the Presentation layer. Repository internally creates the UserManager from UserStore using the internal LoginDbContext. That way, the DbContext and Store are separated from the controller.
If you create WebApi project or somthing with VisualStudio template,
please carefully see UserManagerFactory = () => new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext())); in Startup.Auth.cs file.
You might miss (new ApplicationDbContext()) part. By default, it has empty parameter.
You need to create a UserManager which takes in the userstore which takes in your dbcontext
public UserController()
: this(new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext())))
{
}
public UserManager<ApplicationUser> UserManager { get; private set; }
public UserController(UserManager<ApplicationUser> userManager)
{
UserManager = userManager;
}
I believe this will help. I am pretty new to MVC5 and I have been wanting to separate my model layer from my MVC website, mainly because I imagine I will want to share the models across my various projects. I have been unable to follow all the programming mumbo jumbo I have found on the various help sites. I always end up with a lot of errors which I am unable to resolve with my limited knowledge. However, I have found an easy way to move my ApplicationDbContext out of my MVC5 model and with hardly any errors. All the work is done by the wizards already provided by Microsoft. I would like to share my little find with everyone. This is what you do (step by step):
1. Create a MVC5 project with authentication. Call it ModelProject.
2. Exclude everything from the project except
a. Properties
b. References
c. Models
d. packages.config
3. The ModelProject will hold all your models (even ApplicationDbContext.) Rebuild it.
4. Now, create a new MVC5 project with authentication. Call this Mvc5Project
5. Import the ModelProject project into Mvc5Project .
6. Wire the ModelProject into this project i.e. link in the reference.
7. Exclude the following from the MVc5Project from the Models folder
a. AccountViewModels.cs
b. IdentityModels.cs
c. ManageViewModels.cs
8. If you rebuild now, you will get a bunch of errors. Just go to the errors and resolve them using the right click method to get the new namespace from ModelProject. The namespace will show if you have wired the project in correctly.
9. Also, dont forget to go to View/Manage and Views/Account of Mvc5Project and change the models in there to the new location otherwise you will get some weird cryptic errors.
That's it! Now you have a project with the models all separated out (including the applicationDbContext) -And NO ERRORS!! Good luck!

Resources