I am trying to run cleanup jobs from my azure Webjob for my mvc application. I can do standard database updates no problem but am unable to cleanup the aspnetuser accounts as I cannot get an ApplicationUser context as no startup class for Owin.
Anyone got any ideas on how this can be done or some dummy code? My google searches have come up blank thus far.
Thanks.
I am running ASP.net MVC 5 with VS 2015
Actually even if the ApplicationUserManager requires an OwinContext, you can get rid of the OwinContext.
The OwinContext is only useful for accessing the ApplicationDbConbtext that is bound to the OWinContext.
So I refactored the Create factory methods of ApplicationUserManager in order to be able to manage the users, at startup (or in a WebJob), without any OwinContext :
public static ApplicationUserManager Create(
IdentityFactoryOptions<ApplicationUserManager> options,
IOwinContext context)
{
ApplicationDbContext dbContext = context.Get<ApplicationDbContext>();
return Create(options, dbContext);
}
internal static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, Applicatio
{
// Create ApplicationUserManager myself instead of finding it on OwinContext
var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context));
// old code already generated by VS wizard
}
Then I use the ApplicationUserManager :
using (ApplicationDbContext applicationDbContext = new ApplicationDbContext())
{
var options = new IdentityFactoryOptions<ApplicationUserManager>
{
//DataProtectionProvider = app.GetDataProtectionProvider(),
};
ApplicationUserManager userManager = ApplicationUserManager.Create(options, applicationDbContext);
// ApplicationRoleManager has been changed the same way to be usable without OwinContext
ApplicationRoleManager appRoleManager = ApplicationRoleManager.Create(applicationDbContext);
// ... Code using ApplicationUserManager and ApplicationRoleManager
ApplicationUser appUser = userManager.FindByEmailAsync(email).Result;
// ...
IdentityResult result = userManager.CreateAsync(appUser, password).Result;
}
}
Hope it helps
Related
SignInManager.PasswordSignInAsync checks user if present in database and signs in. await UserManager.CreateAsync creates new user in database. await SignInManager.SignInAsync signs in the user.
Correct me if I'm wrong but it trivial for this question. How to implement those functionalities when we are not using Entity Framework in ASP.NET MVC?
You need not to implement those method even if you're not using EF
You can rely to asp.net identity by registering a custom class with a IUserStore and call them to use your own database access
I've followed this article and the membership works even with my own written data access layer:
https://markjohnson.io/articles/asp-net-core-identity-without-entity-framework/
Basically you have to :
write your db access (of course)
create a model for the user (and for the role, etc.)
create a UserStore that implements IUserStore, handling YourCustomModel methods
add the UserStore as a service in app's Startup
You can then refer to identity managers in your Controller thanks to dependency injection
private readonly UserManager<YourCustomModel> _userManager;
private readonly SignInManager<YourCustomModel> _signInManager;
public YourCustomController(
UserManager<YourCustomModel> userManager,
SignInManager<YourCustomModel> signInManager
)
{
_userManager = userManager;
_signInManager = signInManager;
}
and in your actions use them to invoke identity methods :
var uModel = YourCustomModel.Static.SelectByEmail(vModel.Email);
var result = await _signInManager.PasswordSignInAsync(uModel, vModel.Password,
vModel.RememberMe, lockoutOnFailure: false);
if (result.Succeeded) ...
SignInAsync method instead and create a user object in memory.
var userPrincipal = await _signInManager.CreateUserPrincipalAsync(new
ApplicationUser
{
Id = 1,
UserName = viewModel.UserName,
Email = viewModel.UserName,
SecurityStamp = Guid.NewGuid().ToString()
});
foreach(var claim in userPrincipal.Claims)
{
appUser.Claims.Add(new ApplicationUserClaim { UserId = appUser.Id,
ClaimType = claim.Type, ClaimValue = claim.Value });
}
await _signInManager.SignInAsync(appUser, viewModel.RememberMe);
This happens after I customized the asp.net core identity services to support multi-tenancy based on This article. I simplified it to suite my needs.
Here is my basic setup.
1) Custom application user
public class ApplicationUser : IdentityUser<int>, IEntityBase{}
2) Custom Role
public class ApplicationRole : IdentityRole<int>, IEntityBase{}
3) Custom Role store
public class RoleStoreMultiTenant<TRole> : RoleStore<TRole, ApplicationDbContext, int>{}
4) Custom User Store
public class UserStoreMultiTenant<TUser, TRole, TKey> : UserStore<TUser, TRole, ApplicationDbContext, int>{}
5) My role service inheriting from above (3). This is just to separate my code from RoleStore overridden code.
public class ApplicationRoleStore : RoleStoreMultiTenant<ApplicationRole>{}
6) My User service inheriting from above (4). This is just to separate my code from UserStore overridden code.
public class ApplicationUserStore : UserStoreMultiTenant<ApplicationUser, ApplicationRole, int>{}
7) My ApplicationDbContext is;
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, int>{}
8) My startup configurations related to identity (in ConfigureServices).
services.AddScoped<IRoleStore<ApplicationRole>, ApplicationRoleStore>();
services.AddScoped<IUserStore<ApplicationUser>, ApplicationUserStore>();
services.AddIdentity<ApplicationUser, ApplicationRole>(o =>
{
o.User.RequireUniqueEmail = true;
//options code
}).AddUserStore<ApplicationUserStore>()
.AddEntityFrameworkStores<ApplicationDbContext, int>();
9) In Startup Configure method I have;
//other code
app.UseIdentity();
//other code
10) I have a basecontroller expecting below via the constructor injection
public BaseController(ApplicationDbContext dbContext,
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager,
IMessageServices messageServices, ILoggerFactory loggerFactory,
AppTenant currentTenant, IMapper mapper)
{
_dbContext = dbContext;
_signInManager = signInManager;
_userManager = userManager;
_messageServices = messageServices;
_logger = loggerFactory.CreateLogger<BaseController>();
_currentTenant = currentTenant;
_mapper = mapper;
}
All other controllers are inheriting from this base.
My Database migrations works fine and identity db structures getting created with my custom properties without any issues. However when I run the application I get the error shown in the topic. Which is;
InvalidOperationException: A circular dependency was detected for the service of type 'Microsoft.AspNetCore.Identity.UserManager`1[Registrar.Data.MultitenantIdentity.Models.ApplicationUser]'.
Stack trace shows all framework code and I am finding it difficult to figure out the circular reference .
Can anyone point me to the right direction?
Found the problem. In the class;
public class ApplicationUserStore : UserStoreMultiTenant<ApplicationUser, ApplicationRole, int>{}
I was requesting UserManager in the constructor which was causing the issue. It would have been nice if the framework shows at which line in my code made it to fail.
I've read and Googled everything on this, but can't seem to get it to work. I created a custom LifetimeManager for Unity in my MVC5 application based on these posts:
MVC3 Unity Framework and Per Session Lifetime Manager
This may be the issue I am experiencing
Here is my SessionLifetimeManager
public class SessionLifetimeManager : LifetimeManager
{
private string key = Guid.NewGuid().ToString();
public override object GetValue()
{
return HttpContext.Current.Session[key];
}
public override void RemoveValue()
{
HttpContext.Current.Session.Remove(key);
}
public override void SetValue(object newValue)
{
HttpContext.Current.Session[key] = newValue;
}
}
I only have a few types I'm playing with, here is the relevant registrations in UnityConfig.cs:
container.RegisterType<IEpiSession, EpiSession>(new SessionLifetimeManager(),
new InjectionConstructor(config.AppServerURI, config.PathToSysConfig));
container.RegisterType<IReportRepository, EpicorReportRepository>(new TransientLifetimeManager());
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
Note that the EpicorReportRepository has a dependency on IEpiSession via constructor injection.
public class EpicorReportRepository : IReportRepository
{
private IEpiSession session;
// DI constructor
public EpicorReportRepository(IEpiSession session) {
this.session = session;
}
// ...
}
My Problem: After the first user / session connects to the application, every new user / session after that seems to still be using the EpiSession object and credentials that the first user had create/injected for him. This seems to be a common pattern used on the interwebs, so I'm wondering what I am missing.
How did you test that IEpiSession is the same in different Sessions?
Try to open you application from different browsers. If you open several tabs in the same browser then the same session is used.
I checked your code and it works for me.
There is the only one difference in SetResolver():
DependencyResolver.SetResolver(
type => container.Resolve(type),
types => container.ResolveAll(types));
The full registration code is the following:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
...
var container = new UnityContainer();
container.RegisterType<IEpiSession, EpiSession>(
new SessionLifetimeManager(),
new InjectionConstructor("config.AppServerURI", "config.PathToSysConfig"));
container.RegisterType<IReportRepository, EpicorReportRepository>(new TransientLifetimeManager());
DependencyResolver.SetResolver(
type => container.Resolve(type),
types => container.ResolveAll(types));
}
}
I'm trying to get the DbContext from the current Owin context, so I can use a single context on my application, however, I'm getting a NullReferenceException.
I can access UserManager and RoleManager:
private ApplicationUserManager _userManager;
public ApplicationUserManager UserManager
{
get
{
return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
They're configured how they came by default in the Identity sample project:
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
But trying to get the context to use it directly:
ApplicationDbContext context = HttpContext.GetOwinContext().Get<ApplicationDbContext>();
It always returns null on my controller. How can I get the current DbContext from the Owin context?
EDIT:
I was creating a new context to use with my generic repository
public AdminController()
{
AppContext = new ApplicationDbContext();
this.repoProyects = new GenericRepository<Proyect>(AppContext);
}
But it was creating a problem with entities being referenced from multiple contexts, so I'm trying to get the current Owin context like this:
public AdminController()
{
this.AppContext = HttpContext.GetOwinContext().Get<ApplicationDbContext>();
this.repoProyects = new GenericRepository<Proyect>(AppContext);
}
HttpContext is always null from here, so I don't know how to get the context to pass it to my class.
I had the same issue using Identity Framework when I added some extra navigation properties to the ApplicationUser. If you set
appContext = HttpContext.Current.GetOwinContext().Get<ApplicationDbContext>();
in OnActionExecuting instead of in the constructor, then OWIN should be ready to return the DbContext that's in use at that point. OnActionExecuting kicks in before any action methods fire, so this should be early enough to be useful. Hope that helps.
I was missing the initialization of my entities in the Startup method ConfigureOauth...
app.CreatePerOwinContext<OwinAuthDbContext>(() => new OwinAuthDbContext());
Found the answer here: http://www.c-sharpcorner.com/UploadFile/ff2f08/token-based-authentication-using-Asp-Net-web-api-owin-and-i/
When I create default SPA template progect VS2013 creates MeController. Calling Get I have a user information. But when I create almost the same Controller, for example UserController, and copy-paste all from Me to User I do not have necessary info about user on breakpoint in the method:
// GET api/Me
public GetViewModel Get()
{
var user = UserManager.FindById(User.Identity.GetUserId());
return new GetViewModel() { Hometown = user.Hometown };
}
I don't uderstand this magic! I also do not see a caller of constractor and sender of userManager. Where is it?
public MeController(ApplicationUserManager userManager)
{
UserManager = userManager;
}
There is a default constructor in the MeController which gets called by default. In this case the UserManager, is got from the Owin contect as below
public ApplicationUserManager UserManager
{
get
{
return _userManager ?? HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
Even if you copy this over to a new class like UserController, this should happen. Are you using any DI library like StrucuteMap/Unity etc? Certain DI controllers choose the 'greediest' constructor(the one with the maximum arguments). You could override that by using appropriate methods for that DI library(like this if you are using StructureMap)
In case you would want to use your own UserManager you could use your own implementation of UserManager and have it used. But if you are planning to stick with the default, this is already added in Startup.Auth.cs into the Owin pipeline.