Enable Migrations in Multi-Tier MVC Application - asp.net-mvc

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?

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.

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.

Identity ApplicationDbContext with ninject

I am using ninject for my entities and have created a custom dbcontext, which holds my poco classes. This is CusDbContext . I then add *this to identity by adding it as a parameter in the base class constructor for ApplicationDbContext
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("CusDbContext", throwIfV1Schema: false)
{
}
So now in my webconfig the connection string is set to CusDbContext. This all works fine. On the initial create ( when i register a user and .net creates the db in background with all the user tables )
This database is local by the way (local)\v11.0
The tables are created and the user is added and all works fine. Then when i add migrations for code first it states
More than one context type was found in the assembly
Cool so i use my CusDbContext.
PM>Enable-Migrations -ContextTypeName Library.Data.Concrete.CusDbContext
PM>Add-Migration init
And i get the error
Library.Data.Concrete.IdentityUserLogin: : EntityType
'IdentityUserLogin' has no key defined. Define the key for this
EntityType.
That is a viewModel though (O.o)
Why is the migration trying to add a viewmodel as an entity? Is there any way to have one context type using identities default files? I still want to use ninject.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
This was needed on the ApplicationDbContext in order to build.

MVC project not creating database

Situation
MVC5 EF Code-First project
Database migrations enabled
Database in App_Data is not checked in via sourcecontrol
Connectionstring points to non existing database
Problem
When someone starts the project the database isn't created according to the connectionstring
My solution
Edit the webconfig dbcontext connectionstring and modify the initial catalog string and now it recreates the the database.
Gut feeling
This doesn't feel right. Isn't there another whay to do this that doesn't make my skin crawl like my own solution.
Thanx for any ideas.
Create your own DbConfiguration
public class MyDbConfiguration : DbConfiguration
{
public MyDbConfiguration()
{
SetDatabaseInitializer<MyDbContext>(new CreateDatabaseIfNotExists<MyDbContext>());
}
}
And then Add attribute to your DbContext
[DbConfigurationType(typeof(MyDbConfiguration))]
public class MyDbContext : DbContext
{
}
For more information about DbConfiguration please check here

Asp Identity Custom context

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

Resources