ASP.Net Identity 2.0 without Entity Framework?

Aug 22, 2014 at 12:48 AM
Can anyone point me in the right direction on how to go about creating an Entity Framework-free implementation of Identity 2.0? The only examples I've come across so far have either used the Entity Framework or have been for Identity 1.0 (and I understand there have been some... significant differences since then).

I understand the basic concept - that I'll need to implement at least the Microsoft.AspNet.Identity.Core interfaces. Is there anything else critical that I'm missing on this?
Developer
Aug 22, 2014 at 6:39 PM
You can check this link http://www.asp.net/identity/overview/extensibility It has articles that let you write a custom store not dependent on EntityFramework
Aug 23, 2014 at 10:00 PM
Thanks for the link.
Aug 28, 2014 at 7:12 PM
Joel,

We have done this but be warned performance is terrible without implementing your own caching logic due to the way they wrote this framework.

To give you an example look at this example:
var manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();
try
{
    manager.BeginTransaction();
    IdentityUser user = manager.FindByName("user@somewhere.org"); //Causes 5 database hits, the user, roles, claims, logins, previous passwords because our implementation doesn't have lazy loading support like an ORM like NHibernate or Entity Framework provides
    System.Security.Claims.Claim claim = new System.Security.Claims.Claim("test", "testval"); //Add a claim
    manager.AddClaim(user.Id, claim); //Causes 7 database hits: 5 to load user AGAIN (ARGH!), adds data to userclaim table, then also runs UpdateUser
    manager.RemoveClaim(user.Id, claim); //Causes 7 database hits: 5 to load user AGAIN (ARGH!), adds data to userclaim table, then also runs UpdateUser
    manager.CommitTransaction();
}
catch (Exception ex)
{
    if (manager.HasTransaction())
        manager.RollbackTransaction();
}
If you look at the code for AddClaimAsync for example you get this:
public async virtual Task<IdentityResult> AddClaimAsync(TKey userId, Claim claim)
{
    ((UserManager<TUser, TKey>) this).ThrowIfDisposed();
    IUserClaimStore<TUser, TKey> claimStore = ((UserManager<TUser, TKey>) this).GetClaimStore();
    if (claim == null)
    {
        throw new ArgumentNullException("claim");
    }
    TUser user = await ((UserManager<TUser, TKey>) this).FindByIdAsync(userId).WithCurrentCulture<TUser>();
    if (user == null)
    {
        throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resources.UserIdNotFound, new object[] { userId }));
    }
    await claimStore.AddClaimAsync(user, claim).WithCurrentCulture();
    return await ((UserManager<TUser, TKey>) this).UpdateAsync(user).WithCurrentCulture<IdentityResult>();
}
As you can see from the above code, just to add a claim it reloads the user account with the following line:
 TUser user = await ((UserManager<TUser, TKey>) this).FindByIdAsync(userId).WithCurrentCulture<TUser>();
Which causes a huge amount of traffic since it needs to load a lot of stuff if your implementation does not include lazy loading support and caching (which straight ADO .NET doesn't have). So just adding a claim to a user account is a monumental effort unless you completely rewrite the framework to stop reloading the entire user object everytime it wants to save something, in addition to saving the entire User object at the end (UpdateAsync(user)). As you can see, just adding a simple claim record with the way things are written is hugely poor performing.

Our implementation ended up still using straight ADO .NET but it was a pain. In hindsight we should have used NHibernate (we don't have privilege of using entity framework driver since we use DB2 which has no free entity framework drivers).
Sep 6, 2014 at 1:12 AM
Matt,

Thanks for the awesome reply (and sorry for my late response!). It sounds like you have a lot more experience in dealing with things like this than I have.

It's interesting to hear about the performance impact of caching. That's actually one of the reasons I'm not too interested in using the Entity Framework for what I'm doing - the database/models that I'm working with aren't totally normalized so that I can avoid doing a lot of additional look-ups. I have one operation in particular that is the most commonly used operation (probably 75%+ of my calls), and need it to run as fast as possible. By using a de-normalized model, I'm only hitting a single table instead of at least three different tables. Right now I'm using the MVC 3 framework without the Entity Framework. I was using a custom Role Provider, and everything was running through stored procedures. All in all, it was pretty snappy. The major reason for switching over to the MVC 5 is the better handling of OAuth. Right now it's being handled by a third-party plugin, and I'm not terribly fond of the idea. After reading about ASP.Net Identity 2.0, I thought it sounded pretty good.

From other sources that I've come across, if I do go the route of implementing my own storage provider, I'll also likely be implementing the IUser, IUserStore, and the other related Identity classes as well. Basically, I really like the way that ASP.Net Identity is structured, but I'm not a fan of how it was implemented (with it's reliance on the Entity Framework). Yes, that means I'll have to write a lot more code, but it also gives me a lot more control over what's going in the back-end - like avoiding the 5 database calls to add a role.
Sep 6, 2014 at 3:11 PM
Edited Sep 8, 2014 at 3:46 PM
(comment removed)
Sep 17, 2014 at 3:26 PM
With Dapper, DB transactions to SQL Server are much faster.

I too, wanted to get rid of EntityFramework to take advantage of Dapper.

I needed to roll my own Membership, Role, and Profile Providers, so it was a perfect opportunity to drop EF, EntityModels, etc, etc.

It's a bit painful to write all the interface members, but the performance gain is worth it.

Thanks to Matt's comments, I'll be able to watch out for those query and caching issues.
Apr 12, 2015 at 6:51 PM
I too need to get rid of EF and want to mkae use of Dapper. I already created an application with Dapper and running excellent now.
Now decided to replace my own identity mechanism with Asp.Net Identity that supports google claims as well.

This is the only reason why I am interested in Asp.Net Identity, bet never interested to replace any of my Dapper/Data Access code.

Please suggest is there any article that covers plain interface implementation of this without using EF.
Apr 13, 2015 at 2:51 PM
I was able to eventually get it working. Basically, I started off with a regular MVC project and started hacking bits out. I'd start by removing the EntityFramework Nuget packages. I then added in my own User class, UserManager class, storage provider, and a few other bits. It took a little bit of figuring things out on how the existing MVC code was calling the UserManager, but I was eventually able to get it working.

I found the following article particularly helpful: http://www.asp.net/identity/overview/extensibility/overview-of-custom-storage-providers-for-aspnet-identity

The only thing it didn't cover was how to get the existing MVC pages working with the new UserManager. I had some odd issues just because the way that the calls were doing things. For example, it looked like it was attempting to update the username of a user who hadn't yet been used. This is because my own custom storage provider initially required that a user exist in the database in order to update the username on it. The original MVC/Entity Framework code didn't actually make a call to the database, it just updated the object without actually making a call to the database. Changing mine so that it behaved the same way made the existing MVC pages work.