NHibernate session management in RESTful API with Tasks/Threads - c#

I'm building a set of APIs. One of them is a authentication API, that returns JWT tokens. I'm trying to implement a Session per Action approach, with ActionFiltersAttribute. My controller is decorated with this attribute:
public class NHibernateSessionAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
var session = NHibernateSessionManager.SessionFactory.OpenSession();
session.BeginTransaction();
CurrentSessionContext.Bind(session);
}
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
var session = CurrentSessionContext.Unbind(NHibernateSessionManager.SessionFactory)
if (session != null)
{
if (session.Transaction.IsActive)
{
try
{
session.Transaction.Commit();
}
catch
{
session.Transaction.Rollback();
}
}
session.Close();
}
}
}
Where is the problem? To manage the users with NHibernate istead of Entity Framework I've implemented all the needed ASP.NET Identity interfaces, and they all return a Task<T>. For example on the following action:
AccountController.cs
public async Task<IHttpActionResult> ChangePassword(ChangePasswordBindingModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
IdentityResult result = await UserManager.ChangePasswordAsync(User.Identity.GetUserId(), model.OldPassword,
model.NewPassword);
if (!result.Succeeded)
{
return GetErrorResult(result);
}
return Ok();
}
There ChangePasswordAsync calls internally several methods wich have code inside new tasks, where the SessionFactory.GetCurrentSession() causes a NullException. As far as I know, because that's another Thread and context.
In code, the first attempt to save executes with no fail, and the second not. The duplicated code is only to ilustrate the situation.
UserStore.cs
public System.Threading.Tasks.Task UpdateAsync(UserModel user)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
//Here the Session is found
DataProviderI<UserModel, int> prov = new DataProviderImplGeneric<UserModel, int>();
prov.Save(user);
return Task.Factory.StartNew(() =>
{
//Here the Session is NOT found
DataProviderI<UserModel, int> prov2 = new DataProviderImplGeneric<UserModel, int>();
prov.Save(user);
});
}
What's the best way to deal with this and get the same ISession during all the Action ?
As far as I know NHibernate doesn't support async calls and I could refactorize the methods with a return of type Task.FromResult(0) on void cases or Task.FromResult<T>(T) where T is an object, but I would like to know if there's another solution to take advantage of parallelism

Seems like the problem you are dealing with is stemming from the HttpContext being null while inside a task and therefore cannot access the NHibernate session stored inside of the context variable.
You could work around this by getting the ISession before you call into a task.
Add in a constructor to your DataProviderImplGeneric so you manually pass one in.
DataProviderImplGeneric(ISession session) {
this.session = session;
}
just retrieve the session before you call into this from inside a task.
var session = GetCurrentNHibernateSession();
return Task.Factory.StartNew(() => {
var dataProvider = DataProviderImplGeneric<UserModel, int>(session);
return dataProvider.Save(user);
}

Related

Why is HttpContext.Session status changing to disposed?

I'm trying to store token I get from external api on session.
code snippet concerning this;
[HttpPost]
public async void Post()
{
if (HttpContext.Session.GetValue<User>("Token") == null)
{
HttpContext.Session.SetValue("Token", "test");
var res = await _loginBusiness.GetToken();
HttpContext.Session.SetValue("Token", res);
}
}
HttpContext.Session.SetValue("Token", "test");
in this part, it doesn't occur any error but second the same code line give an error after GetToken().
related error
System.ObjectDisposedException: 'IFeatureCollection has been disposed.
Object name: 'Collection'.'
Also GetToken():
public async Task<User> GetToken()
{
String url = "login/login";
var client = httpClientFactory.CreateClient("VoiceScope");
var postRes = await client.PostAsync<User>(new UserLogin(), url);
return postRes;
}
The problem is that you are using async void. These promises can't be observed and their semantics end up a lot different from a normal Task. Your disposal is happening early because the infrastructure just assumes your Post method has completed (it has no way to tell otherwise).
Change the signature of Post to be:
public async Task Post()
Please note that async void should be limited to event handlers.
I am not sure about using HttpContext. You have IHttpContextAccessor in asp.net core.
I think for store token you can use this
public class UserContext
{
public UserContext(IHttpContextAccessor context)
{
Token = GetAccessToken(context);
}
private static string GetAccessToken(IHttpContextAccessor contextAccessor)
{
var identity = (ClaimsIdentity)contextAccessor?.HttpContext?.User?.Identity;
return identity?.Claims.FirstOrDefault(x => x.Type == "token")?.Value;
}
public string Token { get; }
}
And then, add this staff in your DI like scope object and use it in controllers via ServiceProvider.

Correct conversion from synchronous to async controller methods (when using void)

Using Entity Framework Core 2.0 and .NET Core 2.0 Web API controllers I am trying to rewrite them from synchronous to asynchronous methods.
This actually works easy for my controller methods just querying data.
Unfortunatly I wrapped the DbContext method SaveChanges into some helper methods for centralized logging of DB changes.
And here I start struggling how to correctly use combinations of void in an asynchronous context.
Explanation of the code:
MyController is a web api controller. The PostMethod is a (currently) synchronous post method receiving the request model, handling it, making changes and then saving the changes.
The SaveChangesWithLogs within the controller calls the extension method and prints the returned logs.
The extension method SaveChangesWithLogs generates the log entries (some before saving, some after saving), does the actual saving and returns the logs.
public class MyController : BaseController
{
[HttpPost]
public IActionResult PostMethod([FromBody]PostRequestModel request)
{
//do something
SaveChangesWithLogs();
//return created at
}
protected void SaveChangesWithLogs()
{
List logs = DbContext.SaveChangesWithLogs();
foreach (string log in logs)
{
LogInfo(log); //just prints the generated logs
}
}
}
public static class MyExtensionMethod
{
public static List SaveChangesWithLogs(this DbContext dbContext)
{
List logs = null;
//pre-save prepare logs
dbContext.SaveChanges();
//post-save modifications of logs
return logs;
}
}
In tutorials about async programming they mention the methods should be async all the way down.
So my question: How would the (method signatures) of the SaveChangesWithLogs methods look like?
public class MyController2 : BaseController
{
[HttpPost]
public async Task PostMethod([FromBody]PostRequestModel request)
{
//do something
await SaveChangesWithLogs();
//return created at
}
//does a correct implementation need async here too?
protected void SaveChangesWithLogs()
{
List logs = await DbContext.SaveChangesWithLogs();
foreach (string log in logs)
{
LogInfo(log); //just prints the generated logs
}
//return what???
}
}
public static class MyExtensionMethod2
{
public static async Task> SaveChangesWithLogs(this DbContext dbContext)
{
List logs = null;
//pre-save prepare logs
await dbContext.SaveChanges();
//post-save modifications of logs
return logs;
}
}
You just use the return type as before and wrap Task around it. There is no more magic than that involved.
I'm guessing that your List type is of some type and added that for display purposes.
protected async Task<bool> SaveChangesWithLogs()
{
List logs = await DbContext.SaveChangesWithLogs();
foreach (string log in logs)
{
LogInfo(log); //just prints the generated logs
}
return true;
}
public static async Task<List<myLogType>> SaveChangesWithLogs(this DbContext dbContext)
{
List<myLogType> logs = null;
//pre-save prepare logs
await dbContext.SaveChanges();
//post-save modifications of logs
return logs;
}

Hung returning Follows data from Relationships endpoint

I'm trying to return a list of followed users from the Instagram API. I'm on a sandbox account using the InstaSharp wrapper for .NET.
The action method is being called after user is authenticated.
public ActionResult Following()
{
var oAuthResponse = Session["InstaSharp.AuthInfo"] as OAuthResponse;
if (oAuthResponse == null)
{
return RedirectToAction("Login");
}
var info = new InstaSharp.Endpoints.Relationships(config_, oAuthResponse);
var following = info.Follows("10").Result;
return View(following.Data);
}
Try making the method async all the way through instead of making the blocking call .Result which runs the risk of causing a deadlock
public async Task<ActionResult> Following() {
var oAuthResponse = Session["InstaSharp.AuthInfo"] as OAuthResponse;
if (oAuthResponse == null) {
return RedirectToAction("Login");
}
var info = new InstaSharp.Endpoints.Relationships(config_, oAuthResponse);
var following = await info.Follows("10");
return View(following.Data);
}
depending on how info.Follows was implemented.
Looking at the Github repo, the API internally makes a call to a method defined like this
public static async Task<T> ExecuteAsync<T>(this HttpClient client, HttpRequestMessage request)
Which looks like your smoking gun as calling .Result higher up the call stack on this task would result in your experienced deadlock.
Reference Async/Await - Best Practices in Asynchronous Programming

Custom implementation of ASP.NET Identity

I'm using various resources to try and implement an Identity system with MS Access for an AngularJS app.
I created classes which implement the Identity interfaces I need, and I'm stuck at the stage of creating the Account controller (which will be the API for registeration, login, etc).
The class UserStore implements IUserStore and has the CreateAsync method:
public Task CreateAsync(TUser user)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
var result = userTable.Insert(user);
return Task.FromResult(result);
}
AccountController implements ApiController and has the Register method:
[AllowAnonymous]
[Route("register")]
public async Task<IHttpActionResult> Register(IdentityUser user)
{
var result = await _userStore.CreateAsync(user);
if (result == 0)
{
return InternalServerError();
}
return Ok();
}
userTable.Insert(user) returns an int indicating the number of rows affected in the DB table. The line var result = await _userStore.CreateAsync(user); throws an error, saying it actually returns void, and so void cannot be assigned to var (or to anything else).
I'm having a hard time understanding how to write the Register method and the CreateAsync method so that they will work together.
BTW, I thought I should give up the whole async thing and just make CreateAsync and Register return the int value as-is, but I can't do that since UserStore implements `IUserStore'.
The issue is that the return type cannot be passed from the CreateAsync as it is simply a Task return. It would need to be Task<int> but you cannot do that since it's implementing the IUserStore interface. Why do you need the result, I'm assuming you do not?
Try this instead:
[AllowAnonymous]
[Route("register")]
public async Task<IHttpActionResult> Register(IdentityUser user)
{
await _userStore.CreateAsync(user);
return Ok();
}
Additionally, consider making userTable.Insert(user) an async call if at all possible.
I would suggest not giving up on async/await. Especially for I/O bound operations on a web site like this, they really make your application usable.
If you're really concerned about whether or not the insert might be problematic, try this instead:
public async Task CreateAsync(TUser user)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
var existingUser = await this.FindByIdAsync(user.Id);
if (existingUser != null)
{
await this.UpdateAsync(user);
}
else
{
userTable.Insert(user);
}
}

Identity UpdateAsync lose IdentityResult.Failed

I've wrote a custom UserStore for the ASP.NET Identity which I'm using it in an ASP.NET MVC 5.1 application. Everything is working as expected and I'm very happy with this new feature (Identity) of ASP.NET...
The issue is that I think is almost 2 days since I'm trying to return an error from the UpdateAsync method and somehow it seems that I'm not able to return anything.
LE: I'm using ASP.NET Identity Core 2.0.0-beta1
This is my code in few lines:
public Task UpdateAsync(IdentityUser user)
{
AdminUserEntity userEntity = new AdminUserEntity();
userEntity.IsNew = false;
userEntity.Id = user.UserModel.Id;
userEntity.UserCompleteName = user.UserModel.UserCompleteName;
userEntity.IsDisabled = user.UserModel.IsDisabled;
if (!String.IsNullOrEmpty(user.UserModel.PasswordHash))
userEntity.PasswordHash = user.UserModel.PasswordHash;
if (user.Claims != null && user.Claims.Count > 0)
{
foreach (Claim claim in user.Claims)
{
AdminUserClaimEntity claimEntity = userEntity.AdminUserClaims.AddNew();
claimEntity.AdminUserUniqueId = user.UserModel.UniqueId;
claimEntity.ClaimType = claim.Type;
claimEntity.ClaimValue = claim.Value;
}
}
try
{
byte[] timestamp = Convert.FromBase64String(user.UserModel.Timestamp);
AdminUserEntityManagement.UpdateCompleteAdminUserEntity(userEntity, timestamp);
}
catch (Exception exception)
{
List<string> errors = new List<string>() {exception.Message};
return Task.FromResult<IdentityResult>(IdentityResult.Failed(errors.ToArray()));
}
return Task.FromResult<object>(null);
}
Though there is an exception and the catch block is getting executed, the following line always returns success:
IdentityResult result = await UserManager.UpdateAsync(identityUser);
Can someone please tell me what am I missing?
I'm assuming this UpdateAsync is in your ApplicationUserManager class, shouldn't the signature be
public override Task<IdentityResult> UpdateAsync
if you are trying to change how the UserManager's UpdateAsync method works.
First of all, #hao-kung, thank you for your help. Now I can say that I've seen the big picture. Though I can't say that I like it, I understood how UserManager is working.
Also, if you think that I'm saying something wrong, please, correct me.
For the others, in order to customize UserManager behavior (even if you just want to surface an error which you are anyway able to catch in the UserStore custom class) you have to follow these steps (for better understanding I will exemplify by describing what I've did to catch a business exception for the UpdateAsync method and show it in the interface):
In the UserStore class (which should implement among other interfaces the IUserStore interface), don't catch any exception.
public Task UpdateAsync(IdentityUser user)
{
// here an exception will be thrown if there is a concurrency issue
byte[] timestamp = Convert.FromBase64String(user.UserModel.Timestamp);
AdminUserEntityManagement.UpdateCompleteAdminUserEntity(userEntity, timestamp);
return Task.FromResult<object>(null);
}
Extend UserManager class and override the method that you want to customize (catch the business exception thrown earlier and surface it to the interface):
public class AdminUserManager : UserManager<IdentityUser>
public override async Task<IdentityResult> UpdateAsync(IdentityUser user)
{
Task<IdentityResult> result = base.UpdateAsync(user);
try
{
IdentityResult identityResult = await result;
}
catch (Exception exception)
{
List<string> errors = new List<string>() { exception.Message };
return IdentityResult.Failed(errors.ToArray());
}
return result.Result;
}
In the MVC controller (for example) read the result of the UpdateAsync method from the extended UserManager class:
IdentityResult result = await UserManager.UpdateAsync(identityUser);
if (result.Succeeded)
{
this.SetNotification("The user has been updated.", EnumToastrNotificationType.Info);
return RedirectToAction("ShowUsers", "UserManagement");
}
else
{
this.AddErrors(result);
}

Categories