I am attempting to use Asp.net identity and NHibernate.
I have created a new blank Asp.net MVC site using .NET framework 4.5.1 and I have installed and followed the instructions for using nuget package NHibernate.AspNet.Identity as described here:
https://github.com/milesibastos/NHibernate.AspNet.Identity
which involves making the following changes to the AccountController class default constructor:
var mapper = new ModelMapper();
mapper.AddMapping<IdentityUserMap>();
mapper.AddMapping<IdentityRoleMap>();
mapper.AddMapping<IdentityUserClaimMap>();
mapper.AddMapping<IdentityUserLoginMap>();
var mapping = mapper.CompileMappingForAllExplicitlyAddedEntities();
var configuration = new Configuration();
configuration.Configure(System.Web.HttpContext.Current.Server.MapPath(@"~\Models\hibernate.cfg.xml"));
configuration.AddDeserializedMapping(mapping, null);
var schema = new SchemaExport(configuration);
schema.Create(true, true);
var factory = configuration.BuildSessionFactory();
var session = factory.OpenSession();
UserManager = new UserManager<ApplicationUser>(
new UserStore<ApplicationUser>(session));
I am getting the following exception:
No persister for: IdentityTest.Models.ApplicationUser
The ApplicationUser class doesn't have any additional properties to IdentityUser (which works fine for a Entity Framework implementation of Asp.net Identity).
Can anyone offer suggestions as to how I can get Asp.net identity to work with this NuGet package?
I have struggled very much with this library, which is making me question why this is the recommended library for using OWIN with NHibernate.
Anyway, to answer your question, the code you provided that you got from the github website adds NHibernate mappings for the library's classes. NHibernate doesn't have a mapping for ApplicationUser
, it only has a mapping for it's base class. NHibernate needs a mapping for the instantiated class. This is problematic because you don't have access to the mapping code in the library's assembly, so you can't change it to use the ApplicationUser
class instead. So the only way to get past this using the library as it is, is to remove the ApplicationUser
class and use the library's IdentityUser
class. Or, you could copy the mapping code from github and try using the same mapping for ApplicationUser
.
Also, the library code and the code he gives for the AccountController
does not ever open an NHibernate transaction, so even though the library calls Session.Save
and Session.Update
the data won't ultimately be saved in the database. After you open the session you need to open a transaction and save it as a private field on the class:
transaction = session.BeginTransaction(IsolationLevel.ReadCommitted);
Then you need to call transaction.Commit()
after your action in the AccountController
finishes executing, so you will need to override OnResultExecuted
:
protected override void OnResultExecuted(ResultExecutedContext filterContext)
{
transaction.Commit();
}
Keep in mind this example is oversimplified, and in a production application you need to have error checking where you will Rollback instead of Commit if there are errors, and you need to properly close/dispose of everything, etc.
Furthermore, even after you solve those problems, there are other issues with the library. I ended up having to download the source from github so I could modify the library in order to use it. There are at least 3 other blatant errors in the library's code:
1) In NHibernate.AspNet.Identity.UserStore:
public virtual async Task<TUser> FindAsync(UserLoginInfo login)
{
this.ThrowIfDisposed();
if (login == null)
throw new ArgumentNullException("login");
IdentityUser entity = await Task.FromResult(Queryable
.FirstOrDefault<IdentityUser>(
(IQueryable<IdentityUser>)Queryable.Select<IdentityUserLogin, IdentityUser>(
Queryable.Where<IdentityUserLogin>(
// This line attempts to query nhibernate for the built in asp.net
// UserLoginInfo class and then cast it to the NHibernate version IdentityUserLogin,
// which always causes a runtime error. UserLoginInfo needs to be replaced
// with IdentityUserLogin
(IQueryable<IdentityUserLogin>)this.Context.Query<UserLoginInfo>(), (Expression<Func<IdentityUserLogin, bool>>)(l => l.LoginProvider == login.LoginProvider && l.ProviderKey == login.ProviderKey)),
(Expression<Func<IdentityUserLogin, IdentityUser>>)(l => l.User))));
return entity as TUser;
}
2) In NHibernate.AspNet.Identity.DomainModel.ValueObject:
protected override IEnumerable<PropertyInfo> GetTypeSpecificSignatureProperties()
{
var invalidlyDecoratedProperties =
this.GetType().GetProperties().Where(
p => Attribute.IsDefined(p, typeof(DomainSignatureAttribute), true));
string message = "Properties were found within " + this.GetType() +
@" having the
[DomainSignature] attribute. The domain signature of a value object includes all
of the properties of the object by convention; consequently, adding [DomainSignature]
to the properties of a value object's properties is misleading and should be removed.
Alternatively, you can inherit from Entity if that fits your needs better.";
// This line is saying, 'If there are no invalidly decorated properties,
// throw an exception'..... which obviously should be the opposite,
// remove the negation (!)
if (!invalidlyDecoratedProperties.Any())
throw new InvalidOperationException(message);
return this.GetType().GetProperties();
}
3) In NHibernate.AspNet.Identity.UserStore: For some reason, at least when creating a user/user login using an external provider like facebook, when the user/user login is initially created, the Update method is called instead of the Add/Create causing NHibernate to try to update an entity that doesn't exist. For now, without looking more into it, in the UserStore
update methods I changed the library's code to call SaveOrUpdate
on the NHibernate session instead of Update
which fixed the problem.
I have only ran simple tests with the library that have worked after my changes, so there is no telling how many other runtime / logic errors are in this library. After finding those errors, it makes me really nervous using it now. It seems there was absolutely no testing done with even simple scenarios. Take caution using this library.
I also struggled to use NHibernate.AspNet.Identity. I found it was much easier just to make my own implementation using NHibernate, which I've turned into a minimal worked example here:
https://github.com/MartinEden/NHibernate.AspNet.Identity.Example
They key parts are a simple implementation of IUserStore<TUser, TKey>
and IUserPasswordStore<TUser, TKey>
using an NHibernate session for persistence. Then it's just a matter of writing a bit of glue to tell Owin to use that code.