50

I am trying to move the Identity model to a class library using the methods in this link:

ASP.NET Identity in Services library

Problem 1: It seems to keep using the Website project's connection string. I overcame it by specifying the full connection string in the class library. Can I make the IdentityDbContext use the class library's connection string?

Problem 2: Due to the problem 1, if I remove the Entity Framework from the website project. It will give the following error that it is looking for EF's SqlClient in the Website project.

An exception of type 'System.InvalidOperationException' occurred in EntityFramework.dll but was not handled in user code

Additional information: No Entity Framework provider found for the ADO.NET provider with invariant name 'System.Data.SqlClient'. Make sure the provider is registered in the 'entityFramework' section of the application config file. See http://go.microsoft.com/fwlink/?LinkId=260882 for more information.

Other solutions are welcome as long as it omits all Data Access Layer references like EF in the Website project.

Community
  • 1
  • 1
Joe Yap
  • 501
  • 1
  • 5
  • 3
  • Perhaps this will help. http://stackoverflow.com/a/29519572/1467014 . The only thing left for me to do is remove a few ef references in a controller. Then, unless I've missed something, there are no other ef dependencies in my web project. – puddleglum Apr 08 '15 at 16:45
  • If I understood things correctly, the connection string is supposed in be placed in the web.config of the website project...because all the configurations is supposed to be placed in main(){...}, which in the ASP.NET web app's case is the Startup.cs class or the Global.asax in the Website project. – jflaga Jun 13 '18 at 03:42
  • I think you always need a reference to EF in your Website project,, because you will need DbContext from EF in your configurations. – jflaga Jun 13 '18 at 03:54

6 Answers6

76

To move the IdentityModel into a class library (which is the right thing to do according to the SRP), follow these steps:

  1. Create a class library. (ClassLibrary1)
  2. Using NuGet, add a reference to Microsoft.AspNet.Identity.EntityFramework. This will also auto-add some other references.
  3. Add a reference in your website to ClassLibrary1
  4. Find WebSite/Models/IdentityModel.cs and move it to ClassLibrary1.
  5. Make IdentityModel.cs look like this:

    public class ApplicationUser : IdentityUser
    {
    }
    
    public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
    {
        public ApplicationDbContext()
            : base("YourContextName")
        {
        }
    }
    
  6. Make sure your website's Web.config has YourContextName pointing to the right database in the section. (Note: this database can and should house your application data).

    <add name="YourContextName" connectionString="YourConnectionStringGoesHere"
      providerName="System.Data.SqlClient" />
    
  7. Make your EF Context class inherit from your ApplicationDbContext:

    public class YourContextName : ApplicationDbContext
    {
        public DbSet<ABizClass1> BizClass1 { get; set; }
        public DbSet<ABizClass2> BizClass2 { get; set; }
        // And so forth ...
    }
    

When anyone in your site tries to log in or register, the Identity system will route them to your database with all your data which includes the Identity tables.

Good to go!

Rap
  • 6,851
  • 3
  • 50
  • 88
  • 6
    I'd also suggest breaking out the ApplicationDbContext into its own file in the data project outside of the IdentityModels file – Matt Mombrea Oct 22 '14 at 20:42
  • I was trying to accomplish this and am not finding a good way to have Models/Data/Services/Presentation while using ASP.net Identity Framework. I shouldn't need to load my data access layer (Entity Framework) into my Core assembly in order to create my models. – Sam Dec 22 '14 at 14:38
  • @Sam, move your models to a separate assembly (where the rest of your models already are, right?). Then reference them from there. – Mike Devenney Mar 20 '15 at 15:45
  • @MattMombrea, that's how I did it as well. Smelled weird having the data access plumbing sitting in the same file/assembly as a model. I really wish MS would build their "out of the box" functionality following some (any?) solid design patterns. New developers pop off a new MVC template, see data access code living right next to a model class and think that since MS does it that way, so should I! – Mike Devenney Mar 20 '15 at 15:48
  • I tired this solution, but `ConfigureAuth` needs `app.CreatePerOwinContext(ApplicationDbContext.Create);` and `user.GenerateUserIdentityAsync(manager)`, should I add this 2 methods to `IdentityModel.cs`? – Mehdi Dehghani Jul 23 '15 at 17:40
  • 5
    I like this solution very much. I needed to do it, so, I published my version of this solution in github: https://github.com/jjroman/ASPNetIdentityToLib. I hope that it can help to another developer. – jjroman Aug 02 '15 at 03:14
  • If you are using Microsoft.AspNet.Identity.EntityFramework rc1 this doesn't seem to work, you can't add the nuget package to a class library. – alanh May 06 '16 at 17:28
  • Best of the best. Thanks. it help me a lot after blowing internet – mahdi moghimi Nov 28 '21 at 07:02
11

An update to @Rap's answer for EF6 and Identity 2.0:

  1. Create a class library. (ClassLibrary1)
  2. Using NuGet, add a reference to Microsoft.AspNet.Identity.EntityFramework and Microsoft.AspNet.Identity.Owin.
  3. Add a reference in your website to ClassLibrary1
  4. Find WebSite/Models/IdentityModel.cs and move it to ClassLibrary1.
  5. IdentityModel.cs should look like this, no need to change anything:

    public class ApplicationUser : IdentityUser
    {
        public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager, string authenticationType)
        {
            // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
            var userIdentity = await manager.CreateIdentityAsync(this, authenticationType);
            // Add custom user claims here
            return userIdentity;
        }
    }
    
    public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
    {
        public ApplicationDbContext()
            : base("DefaultConnection", throwIfV1Schema: false)
        {
        }
    
        public static ApplicationDbContext Create()
        {
            return new ApplicationDbContext();
        }
    }
    
  6. Make sure your website's Web.config has a context pointing to the right database in the section. (Note: this database can and should house your application data).

    <connectionStrings>
      <add name="DefaultConnection" connectionString="Data Source=localhost;Initial Catalog=Project;Integrated Security=sspi;Pooling=false;" providerName="System.Data.SqlClient" />
    </connectionStrings>
    
  7. Make your EF Context class inherit from your ApplicationDbContext:

    public class YourContextName : ApplicationDbContext
    {
        public DbSet<ABizClass1> BizClass1 { get; set; }
        public DbSet<ABizClass2> BizClass2 { get; set; }
        // And so forth ...
    }
    
Ogglas
  • 62,132
  • 37
  • 328
  • 418
2

I suppose i'm a bit late to the party but for the future readers, here's a good read.

Adil H. Raza
  • 1,649
  • 20
  • 25
  • Not sure why it is down voted, explains what the OP asked for: quote: "Other solutions are welcome as long as it omits all Data Access Layer references like EF in the Website project." I stumbled upon this page while searching for solution to similar problem and other people will in the future. Can the down voter explain the reason for down vote? – Adil H. Raza Sep 27 '18 at 15:18
  • I suppose I AM a bit late to the party as well: you were downvoted because the entirety of your answer is a link. This is discouraged because the link may expire (or change content) and then your answer becomes worthless. PS: I am NOT the person who downvoted you, just trying to help. – Shai Cohen Mar 07 '19 at 18:43
  • Cool, thanks for the feedback. so what is suggested in this case? That link is a 4 articles series, should i copy paste 4 articles and give credit to the author? – Adil H. Raza Mar 08 '19 at 09:56
  • Generally, you would want to include the relevant sections from the link that specifically answer your original question. Maybe think of it this way: if another user had posted this as an answer, would you find it useful as an **answer**? If you feel this just doesn't make sense / apply in this specific situation, then maybe a better place would have been as an edit on your original question? – Shai Cohen Apr 25 '19 at 23:55
  • Also, I wasn't alerted that you responded to my comment: _"The post author will always be notified of your comment. **To also notify a previous commenter**, mention their user name... "._ @ShaiCohen in my case. That's why you were alerted without me having to tag your name, but I wasn't. – Shai Cohen Apr 25 '19 at 23:58
1

What do you mean by moving Identity into a class library? to be used as a reference? I have my custom user manager and user in a separate library, but thats all.

The identity stuff requires a data store of some sort. If you configure Identity to use EF, be sure to get the additional nuget package for it, and you should be able to pass the connection string when you create the context.

Off the top of my head...

var mgr = new UserManager<ApplicationUser>(
     new IUserStore_ofYourChoice<ApplicationUser>(
       new DbContextName("ConnectionStringOverload"));

I think the EF store is "UserStore", gotta verify.

There are nearly a dozen different nuget packages for datastores for Identity now. You don't have to use EF, but it requires a store of some sort.

** EDIT **

Also, as a reference it will always use the config, and hence its defined connection strings, of the main project by default, thats how its suppose to work, so thats ok.

William
  • 1,375
  • 12
  • 27
  • I already say that on the (problem 1) in my question that I putting full connection string for the Identitycontext. I further found out that if I using DB 1st approach, I cant override the context connection string. And even I can do all operation to create/manage the model at the class library. When run time, it will have error say cannot find connection string in web.config file. Sure it is not at the asp.net project, it suppose to be at class library app.config. – Joe Yap May 10 '14 at 16:17
  • It dose not have issue when using DataSet designer, it can split up the DAL completely in a class library. And then use by asp.net project without any connection string. – Joe Yap May 10 '14 at 16:24
1

There are a few rules you must be cognizant of and remember.

First, the Web project will ALWAYS and ONLY use the web.config file(s) that it finds in its own project - period. It does not matter what you put in any other config file anywhere else in your solution. The web project can ONLY use what it finds in its own project. If you have a connection string in another project, you must replicate it to the web project, else it will never be found.

Second, assuming the web project is your startup project, and assuming you are using data migrations (since Identity uses it), note that the package manager will AlWAYS use the connection string that it finds in the startup project. Hence, the package manager, for update-database, will not use the connection string in your model project.

The simple solution is: 1. Copy your connection string from your model project to your web project. 2. In the package manager console, make sure that the dropdown to select the context is pointing to your model project.

silverfox1948
  • 857
  • 10
  • 15
0

Question 1:-

I guess can use the solution found at the link below:-

Setup Entity Framework For Dynamic Connection String

Question 2:-

I believe the entity framework build the way that when execution time will use the web.config of that project. Instruction from Microsoft also suggest that

http://msdn.microsoft.com/en-us/library/vstudio/cc716677(v=vs.100).aspx

Community
  • 1
  • 1
Joe Yap
  • 501
  • 1
  • 5
  • 3