ASP.NET MVC 5 page hangs on db call - c#

I'm trying to get up and running with an MVC 5 project using ASP.NET Identity 2.0. My starting point is the example app in this tutorial. My initial page is Home/Index. When I try to do a search (expecting a null return value) the app simply hangs and I can't figure out why. Instantiation of the db context is causing the Seed() method to be called, which seems normal, but then it hangs on the roleManager.FindByName(roleName) call (I've commented it in the code below). Pausing the debugger showed where it was stuck but I don't know where to go from there. Relevant classes are as follows.
Controller:
public class HomeController : ApplicationController
{
public ActionResult Index()
{
var user = db.Users.Find("dummyVal");
return View();
}
[Authorize]
public ActionResult About()
{
ViewBag.Message = "Your app description page.";
return View();
}
public ActionResult Contact()
{
ViewBag.Message = "Your contact page.";
return View();
}
}
Base Controller:
public abstract class ApplicationController : Controller
{
protected ApplicationDbContext db;
public ApplicationController()
{
db = new ApplicationDbContext();
}
}
DB Initializer:
public class ApplicationDbInitializer : DropCreateDatabaseAlways<ApplicationDbContext>
{
protected override void Seed(ApplicationDbContext context) {
InitializeIdentityForEF(context);
base.Seed(context);
}
//Create User=Admin#Admin.com with password=Admin#123456 in the Admin role
public static void InitializeIdentityForEF(ApplicationDbContext db) {
var userManager = HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>();
var roleManager = HttpContext.Current.GetOwinContext().Get<ApplicationRoleManager>();
const string name = "admin#example.com";
const string password = "Admin#123456";
const string roleName = "Admin";
//Create Role Admin if it does not exist
// EXECUTION HANGS ON THIS CALL...
var role = roleManager.FindByName(roleName);
if (role == null) {
role = new IdentityRole(roleName);
var roleresult = roleManager.Create(role);
}
var user = userManager.FindByName(name);
if (user == null) {
user = new ApplicationUser { UserName = name, Email = name };
var result = userManager.Create(user, password);
result = userManager.SetLockoutEnabled(user.Id, false);
}
// Add user admin to Role Admin if not already added
var rolesForUser = userManager.GetRoles(user.Id);
if (!rolesForUser.Contains(role.Name)) {
var result = userManager.AddToRole(user.Id, role.Name);
}
// Create dummy user
const string dummyUserName = "PeterVenkman";
const string dummyUserPwd = "gf%^^ftf83X";
var dummyUser = userManager.FindByName(dummyUserName);
if (dummyUser == null) {
dummyUser = new ApplicationUser { UserName = dummyUserName, Email = dummyUserName };
var result = userManager.Create(dummyUser, dummyUserPwd);
result = userManager.SetLockoutEnabled(dummyUser.Id, false);
}
}
}

The error is because you are instantiating the dbContext object in the controller and not getting it from the owin context. The presence of two different context objects one in the controller and one in the startup working on the same db is causing this deadlock. Simplest way to resolve this is replace the code
db = new ApplicationDbContext();
with
db = System.Web.HttpContext.Current.GetOwinContext().Get<ApplicationDbContext>();
fixes the issue

I had the same problem and this is how I fixed it.
Delete your current database (I was using .mdf)
Create a new database
Clean and rebuild
Update database - if needed -
Unfortunately, I do not know the source of the problem. Please edit the answer/post a comment if you know :)
For other people with the same issue, make sure the username/password in ApplicationDbInitializer are valid. It will hang if you set the admin password to 123 for example.
Edit: This post provides an explanation + answer
http://forums.asp.net/post/5802779.aspx

Suhas Joshi has the correct answer. The ApplicationDbContext object should be in essence a singleton that is managed by the "Microsoft ASPNET Identity Owin" package (installed using NuGet). When you manually create a new instance of it in your ApplicationController there is contention for the same resource causing this deadlock.
Note that the "Get()" extension used comes from the following library which you must have a reference to in your ApplicationContext class.
using Microsoft.AspNet.Identity.Owin;
Thanks Suhas Joshi for pointing this out as it saved me greatly. I would have just up-voted your answer, but unfortunately I don't have a strong enough reputation yet :).

Related

Unit Test - How to set controller user to Generic Principal object

Updated full solution:
WebApi Controller method which I'm going to test :
using Microsoft.AspNet.Identity;
using System.Web.Http;
[Authorize]
public class GigsController : ApiController
{
private readonly IUnitOfWork _unitOfWork;
public GigsController(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
[HttpDelete]
public IHttpActionResult Cancel(int id)
{
var userId = User.Identity.GetUserId();
var gig = _unitOfWork.Gigs.GetGigWithAttendees(id);
if (gig.IsCanceled)
return NotFound();
if (gig.ArtistId != userId)
return Unauthorized();
gig.Cancel();
_unitOfWork.Complete();
return Ok();
}
}
Unit Test class :
[TestClass]
public class GigsControllerTests
{
private GigsController _controller;
public GigsControllerTests()
{
var identity = new GenericIdentity("user1#domain.com");
identity.AddClaim(
new Claim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", "user1#domain.com"));
identity.AddClaim(
new Claim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier", "1"));
var principal = new GenericPrincipal(identity, null);
var mockUoW = new Mock<IUnitOfWork>();
_controller = new GigsController(mockUoW.Object);
_controller.User = principal;
}
I'm getting following error :
Error CS0200 Property or indexer 'ApiController.User' cannot be
assigned to -- it is read only
https://i.stack.imgur.com/YDQJS.png
You can assign it through ControllerContext
var user = new ClaimsPrincipal();
var context = new ControllerContext
{
HttpContext = new DefaultHttpContext
{
User = user
}
};
controllerUnderTest.ControllerContext = context;
I ran into this same issue while following Moshi's tutorials, and this stumped me for a good couple of hours before I was finally able to resolve this.
Ultimately, what fixed this for me was changing my using statement from:
using GigHub.Controllers;
to
using GigHub.Controllers.Api;
Your GigsController _controller is pointing to the Controller itself and not the ApiController. I kinda wish Moshi had put "Api" in the name of his API classes that way you'd know if you were looking at the pure controller or the API version of the controller without having to check namespaces or go to definition on your objects.
You're right that the User property for the Controller class does indeed only have a Get.
public abstract class Controller : ControllerBase, IActionFilter,
IAuthenticationFilter, IAuthorizationFilter, IDisposable, IExceptionFilter,
IResultFilter, IAsyncController, IController, IAsyncManagerContainer
{
public IPrincipal User { get; }
}
The User property for the ApiController class however has both a Get and Set.
public abstract class ApiController : IHttpController, IDisposable
{
public IPrincipal User { get; set; }
}
Hopefully this was your issue as well and helps you out!
Cheers!
Instead set the CurrentPrincipal of your executing thread
Thread.CurrentPrincipal = principal;
Came across this post when I'm trying to mock a user in a controller, rather than controller.apiā€¦ and struggled.
Anyway the way to do it is by overwriting ControllerContext in the controller with a mock object , and the mock object is set up to return the user you wish to mock.
var user = CreateLoggedInUser(targetUserID);
mockControllerContext = new Mock<ControllerContext>();
mockControllerContext.Setup(o => o.HttpContext.User).Returns(user);
myCaseController.ControllerContext = mockControllerContext.Object;
and
private ClaimsPrincipal CreateLoggedInUser(int userID)
{
GenericIdentity myIdentity = new GenericIdentity("apiTestUser");
myIdentity.AddClaims(new List<Claim> {
new Claim(ClaimTypes.Sid, userID.ToString()),
});
return new ClaimsPrincipal(myIdentity);
}

Mvc .net Password change

I am new with C# and MVC aplications.
I have weird issue with password change in MVC aplication.
The thing is next, when i fill my form and enter old and new password and hit enter, everything seams to went fine but the password is not updated in databse. Then when I do from second try it went fine.
[Authorize]
[HttpPost]
public ActionResult ChangePassword(ChangePasswordModel model)
{
if (ModelState.IsValid)
{
bool changePasswordSucceeded;
MembershipUser currentUser = Membership.GetUser(User.Identity.Name, true /* userIsOnline */);
changePasswordSucceeded = currentUser.ChangePassword(model.OldPassword, model.NewPassword);
if (changePasswordSucceeded)
{
var user = GetUser();
user.PasswordEntropyScore = model.PasswordEntropyScore;
var userRepo = new UserRepository(MvcApplication.DbSession);
userRepo.Update(user);
return this.RedirectToAction("ChangePasswordSuccess");
}
else
{
ModelState.AddModelError(string.Empty, "The current password is incorrect or the new password is invalid.");
}
}
return this.View(model);
}
UserRepository Update() method is here:
public class UserRepository : Repository<User>
{
private Logger logger;
public UserRepository(ISession sesh) : base(sesh)
{
this.logger = LogManager.GetCurrentClassLogger();
}
public new bool Update(User user)
{
if (string.IsNullOrWhiteSpace(user.UserName) || string.IsNullOrWhiteSpace(user.Email))
{
var ex = new ArgumentException("Username or Email cannot be blank");
this.logger.Error("User.Update - username or email was null/empty string.", LoggerHelper.GetErrorString(ex, user));
throw ex;
}
return base.Update(user);
}
}
And repository base.Update() method:
public class Repository<T> : IRepository<T>
where T : Entity
{
protected readonly ISession session;
protected static Logger logger;
public Repository()
{
throw new NotImplementedException("Must instantiate repositories with an ISession");
}
public Repository(ISession sesh)
{
this.session = sesh;
logger = new LogFactory().GetCurrentClassLogger();
}
public bool Update(T entity)
{
this.session.SaveOrUpdate(entity);
this.session.Flush();
return true;
}
}
So all this code went fine when first time I tried to change password but it doens't udapted it in database. Then when I try the second time to change the password then it update in database.
If I change the code to directly call base.Update() method instead to call first UserRepostirory.Update() method as wrapper then it is fine.
Does anyone has idea what the issue is.
there is not much comment in your code to understand what you really did but if you use entity framework you can simply do like below:
entitymodel model= new entitymodel();
var query= model.users.first(o=>userID==inputID);
query.password=inputPassword;
model.SaveChanges();
As per the code I do not see any issues. Is it possible to add try catch block to Repository class Update Method and check if any exception is occurred when you are trying to save for the first time.
Can you check if MvcApplication.DBSession is set before hitting Update method for the first time
There is a possibility of having entity framework local cache which may be updating password to older one. Perform below steps.
Remove below lines from your code.
Change password
Restart application and check if password is changed or not.
If password is changed then clear entity framework local cache(by code).
NOTE: There is no need of updating password in db again. ChangePassword function makes changes to DB directly.

EntityFramework context returning old data

I'm working on an ASP.NET web forms project that has an implementation of a user repository that is acting strangely. I'm experiencing the following behavior:
User logs in with a valid username/password. They get in successfully.
User logs out.
I change the password in the database (for argument's sake).
User logs in again using the new password. However, the login is rejected as invalid.
If I set a breakpoint at the "user" variable in the ValidateUser() method and inspect the password, it has the OLD password even though the database clearly has the new password saved. It seems to me like there's some problem with the architecture of the repository that is causing it to load an old context or something along those lines, but it creates a new PortalContext() each time the class is instantiated. Any ideas?
public class BaseRepository
{
protected readonly PortalContext Context = new PortalContext();
}
public class UserRepository : BaseRepository, IUserRepository
{
public bool ValidateUser(string username, string password)
{
var user = Context.Users.FirstOrDefault(x => x.LoginName == username);
if (user == null) return false;
return user.Password == password;
}
}
.NET CODE BEHIND
public partial class _default
{
private readonly IUserRepository _userRepository = new UserRepository();
protected void btnSubmit_Click(object sender, EventArgs e)
{
if (_userRepository.ValidateUser(txtUsername.Text, txtPassword.Text)
{
// Log user in
}
}
}
EDIT: If I update ValidateUser() to the following code, the problem goes away. This seems to confirm that it has something to do with it hanging on to the old context.
public bool ValidateUser(string username, string password)
{
using (var context = new PortalContext())
{
var user = context.Users.FirstOrDefault(x => x.LoginName == username);
if (user == null) return false;
return user.Password == password;
}
}
I think I had the same issue. The problem is that MembershipProvider is instantiated only once and you probably created context in its constructor and thats why it works when you moved your context initialization in method ValidateUser.
The entity framework only looks for changes if one of the entity key's has changed.
When I had a similar issue, I made the dateModified field (every table I make such a field to track when it was changed) as a entity key, since then all changes are coming in perfect and fast. in the same context instance.

Unit Testing ASP.NET MVC5 App

I'm extending the ApplicationUser class by adding a new property (as shown in the tutorial
Create an ASP.NET MVC 5 App with Facebook and Google OAuth2 and OpenID Sign-on (C#))
public class ApplicationUser : IdentityUser
{
public DateTime BirthDate { get; set; }
}
Now I want to create a Unit Test to verify that my AccountController is correctly saving the BirthDate.
I've created an in-memory user store named TestUserStore
[TestMethod]
public void Register()
{
// Arrange
var userManager = new UserManager<ApplicationUser>(new TestUserStore<ApplicationUser>());
var controller = new AccountController(userManager);
// This will setup a fake HttpContext using Moq
controller.SetFakeControllerContext();
// Act
var result =
controller.Register(new RegisterViewModel
{
BirthDate = TestBirthDate,
UserName = TestUser,
Password = TestUserPassword,
ConfirmPassword = TestUserPassword
}).Result;
// Assert
Assert.IsNotNull(result);
var addedUser = userManager.FindByName(TestUser);
Assert.IsNotNull(addedUser);
Assert.AreEqual(TestBirthDate, addedUser.BirthDate);
}
The controller.Register method is boilerplate code generated by MVC5 but for reference purposes I'm including it here.
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser() { UserName = model.UserName, BirthDate = model.BirthDate };
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await SignInAsync(user, isPersistent: false);
return RedirectToAction("Index", "Home");
}
else
{
AddErrors(result);
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
When I call Register, it calls SignInAsync which is where the trouble will occur.
private async Task SignInAsync(ApplicationUser user, bool isPersistent)
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
}
At the lowest layer, the boilerplate code includes this tidbit
private IAuthenticationManager AuthenticationManager
{
get
{
return HttpContext.GetOwinContext().Authentication;
}
}
This is where the root of the problm occurs. This call to GetOwinContext is an extension method which I cannot mock and I cannot replace with a stub (unless of course I change the boilerplate code).
When I run this test I get an exception
Test method MVCLabMigration.Tests.Controllers.AccountControllerTest.Register threw exception:
System.AggregateException: One or more errors occurred. ---> System.NullReferenceException: Object reference not set to an instance of an object.
at System.Web.HttpContextBaseExtensions.GetOwinEnvironment(HttpContextBase context)
at System.Web.HttpContextBaseExtensions.GetOwinContext(HttpContextBase context)
at MVCLabMigration.Controllers.AccountController.get_AuthenticationManager() in AccountController.cs: line 330
at MVCLabMigration.Controllers.AccountController.<SignInAsync>d__40.MoveNext() in AccountController.cs: line 336
In prior releases the ASP.NET MVC team worked very hard to make the code testable. It seems on the surface that now testing an AccountController is not going to be easy. I have some choices.
I can
Modify the boiler plate code so that it doesn't call an extension method and deal with this problem at that level
Setup the OWin pipeline for testing purposes
Avoid writing testing code that requires the AuthN / AuthZ infrastructure (not a reasonable option)
I'm not sure which road is better. Either one could solve this. My question boils down to which is the best strategy.
Note: Yes, I know that I don't need to test code that I didn't write. The UserManager infrastructure provided MVC5 is such a piece of infrastructure BUT if I want to write tests that verify my modifications to ApplicationUser or code that verifies behavior that depends upon user roles then I must test using UserManager.
I'm answering my own question so I can get a sense from the community if you think this is a good answer.
Step 1: Modify the generated AccountController to provide a property setter for the AuthenticationManager using a backing field.
// Add this private variable
private IAuthenticationManager _authnManager;
// Modified this from private to public and add the setter
public IAuthenticationManager AuthenticationManager
{
get
{
if (_authnManager == null)
_authnManager = HttpContext.GetOwinContext().Authentication;
return _authnManager;
}
set { _authnManager = value; }
}
Step 2:
Modify the unit test to add a mock for the Microsoft.OWin.IAuthenticationManager interface
[TestMethod]
public void Register()
{
// Arrange
var userManager = new UserManager<ApplicationUser>(new TestUserStore<ApplicationUser>());
var controller = new AccountController(userManager);
controller.SetFakeControllerContext();
// Modify the test to setup a mock IAuthenticationManager
var mockAuthenticationManager = new Mock<IAuthenticationManager>();
mockAuthenticationManager.Setup(am => am.SignOut());
mockAuthenticationManager.Setup(am => am.SignIn());
// Add it to the controller - this is why you have to make a public setter
controller.AuthenticationManager = mockAuthenticationManager.Object;
// Act
var result =
controller.Register(new RegisterViewModel
{
BirthDate = TestBirthDate,
UserName = TestUser,
Password = TestUserPassword,
ConfirmPassword = TestUserPassword
}).Result;
// Assert
Assert.IsNotNull(result);
var addedUser = userManager.FindByName(TestUser);
Assert.IsNotNull(addedUser);
Assert.AreEqual(TestBirthDate, addedUser.BirthDate);
}
Now the test passes.
Good idea? Bad idea?
My needs are similar, but I realized that I don't want a pure unit test of my AccountController. Rather I want to test it in an environment that is as close as possible to its natural habitat (integration test, if you want). So I don't want to mock the surrounding objects, but use the real ones, with as little of my own code as I can get away with.
The HttpContextBaseExtensions.GetOwinContext method also got in my way, so I was very happy with Blisco's hint.
Now the most important part of my solution looks like this:
/// <summary> Set up an account controller with just enough context to work through the tests. </summary>
/// <param name="userManager"> The user manager to be used </param>
/// <returns>A new account controller</returns>
private static AccountController SetupAccountController(ApplicationUserManager userManager)
{
AccountController controller = new AccountController(userManager);
Uri url = new Uri("https://localhost/Account/ForgotPassword"); // the real string appears to be irrelevant
RouteData routeData = new RouteData();
HttpRequest httpRequest = new HttpRequest("", url.AbsoluteUri, "");
HttpResponse httpResponse = new HttpResponse(null);
HttpContext httpContext = new HttpContext(httpRequest, httpResponse);
Dictionary<string, object> owinEnvironment = new Dictionary<string, object>()
{
{"owin.RequestBody", null}
};
httpContext.Items.Add("owin.Environment", owinEnvironment);
HttpContextWrapper contextWrapper = new HttpContextWrapper(httpContext);
ControllerContext controllerContext = new ControllerContext(contextWrapper, routeData, controller);
controller.ControllerContext = controllerContext;
controller.Url = new UrlHelper(new RequestContext(contextWrapper, routeData));
// We have not found out how to set up this UrlHelper so that we get a real callbackUrl in AccountController.ForgotPassword.
return controller;
}
I have not yet succeeded to get everything working (in particular, I could not get UrlHelper to produce a proper URL in the ForgotPassword method), but most of my needs are covered now.
I've used a solution similar to yours - mocking an IAuthenticationManager - but my login code is in a LoginManager class that takes the IAuthenticationManager via constructor injection.
public LoginHandler(HttpContextBase httpContext, IAuthenticationManager authManager)
{
_httpContext = httpContext;
_authManager = authManager;
}
I'm using Unity to register my dependencies:
public static void RegisterTypes(IUnityContainer container)
{
container.RegisterType<HttpContextBase>(
new InjectionFactory(_ => new HttpContextWrapper(HttpContext.Current)));
container.RegisterType<IOwinContext>(new InjectionFactory(c => c.Resolve<HttpContextBase>().GetOwinContext()));
container.RegisterType<IAuthenticationManager>(
new InjectionFactory(c => c.Resolve<IOwinContext>().Authentication));
container.RegisterType<ILoginHandler, LoginHandler>();
// Further registrations here...
}
However, I'd like to test my Unity registrations, and this has proved tricky without faking (a) HttpContext.Current (hard enough) and (b) GetOwinContext() - which, as you've found, is impossible to do directly.
I've found a solution in the form of Phil Haack's HttpSimulator and some manipulation of the HttpContext to create a basic Owin environment. So far I've found that setting a single dummy Owin variable is enough to make GetOwinContext() work, but YMMV.
public static class HttpSimulatorExtensions
{
public static void SimulateRequestAndOwinContext(this HttpSimulator simulator)
{
simulator.SimulateRequest();
Dictionary<string, object> owinEnvironment = new Dictionary<string, object>()
{
{"owin.RequestBody", null}
};
HttpContext.Current.Items.Add("owin.Environment", owinEnvironment);
}
}
[TestClass]
public class UnityConfigTests
{
[TestMethod]
public void RegisterTypes_RegistersAllDependenciesOfHomeController()
{
IUnityContainer container = UnityConfig.GetConfiguredContainer();
HomeController controller;
using (HttpSimulator simulator = new HttpSimulator())
{
simulator.SimulateRequestAndOwinContext();
controller = container.Resolve<HomeController>();
}
Assert.IsNotNull(controller);
}
}
HttpSimulator may be overkill if your SetFakeControllerContext() method does the job, but it looks like a useful tool for integration testing.

ASP.NET MVC 3 or 4 - how to handle many DBs

I have a case where each customer has his own DB - What i would like to do is for this to happen automatically eg when Autofac creates the controller it looks up the clientId and then gets the connection string
The following gets the string
public class ModuleContextProvider : IModuleContextProvider
{
IModuleContext context;
public IModuleContext GetContext(int moduleId)
{
if ( context == null)
{
//get module.
var rep = DependencyResolver.Current.GetService<IModuleRepository>();
var module = rep.GetById(moduleId);
context = GetContext(module);
}
return context;
}
public IModuleContext GetContext(Model.Module module)
{
if ( context == null)
{
//get module.
var rep = DependencyResolver.Current.GetService<IModuleRepository>();
context = new ModuleContext(module.DBConnection);
if ( context == null)
throw new InvalidOperationException("Could nto create DB COntext" );
}
return context;
}
}
but how do i get it into the Controllers so everyone does not need to worry about it each time , i have the following but obviously to create the repositry i need the context.
public CategoryController(ICommandBus commandBus, ICategoryRepository categoryRepository)
{
this.commandBus = commandBus;
this.categoryRepository = categoryRepository;
}
Is it possible to use a ROute eg //Controller/ModuleId/Method and then somehow get this from the Url ?
Thanks in advance for any help.
Edit: The real issue here I think is how to keep pushing the connection string around ( in this case in the form of the module) .
You may want to look into using the IDbContextFactory.
http://msdn.microsoft.com/en-us/library/hh506876(v=vs.103).aspx
what is the best practice to set up DbContext in StructureMap for console app?
It was added in the EF 4.1 Update 1.
Not sure if that will help your problem, but either way it's good to know.
If anyone needs this ..
I stored it in the cookie and retrieve in the controller as follows
IModuleWorldRepository moduleWorldRepository;
protected override void Initialize(System.Web.Routing.RequestContext requestContext)
{
base.Initialize(requestContext);
moduleWorldRepository = GetModuleWorldRepository();
}
IModuleWorldRepository GetModuleWorldRepository()
{
UserRunTimeInfo user = (UserRunTimeInfo)User.Identity;
var context = contextProvider.GetContext(user.CurrentModuleId);
return new ModuleWorldRepository(context);
}

Categories