I want to change my connectionstring at runtime based on the code user enters at login screen. I did the following
ApplicationDbContext
public static ApplicationDbContext Create(string scCode){
return new ApplicationDbContext("name=GEContext_" + scCode);
}
And at login i change the connectionstring as follows
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (ModelState.IsValid)
{
try
{
System.Web.HttpContext.Current.Session["SchoolCode"] = model.SchoolCode;
var appDbContext = ApplicationDbContext.Create(model.SchoolCode);
HttpContext.GetOwinContext().Set<ApplicationDbContext>(appDbContext);
....
}
}
}
Now it is still referring to the original database...what am i missing?
P.S. For a history/detail consider this post
Hi I Got the Answer from here
The problem was in ApplicationDbContext where we need to specify a default database whereas in my scenario that default database had to change.
So i changed it using
var appDbContext = ApplicationDbContext.Create(System.Web.HttpContext.Current.Session["SchoolCode"].ToString());//new ApplicationDbContext("name=GEContext", System.Web.HttpContext.Current.Session["SchoolCode"].ToString());
HttpContext.GetOwinContext().Set<ApplicationDbContext>(appDbContext);
HttpContext.GetOwinContext().Set<ApplicationUserManager>(new ApplicationUserManager(new UserStore<ApplicationUser, Role, int, UserLogin, UserRole, UserClaim>(appDbContext)));
return HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
Related
When Posting the following code works fine until it reaches the _userManager.CreateAsync method. No data is saved to the database.
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
private readonly ApplicationDbContext _appDbContext;
private readonly UserManager<IdentityUser> _userManager;
public ValuesController(UserManager<IdentityUser> userManager, ApplicationDbContext appDbContext)
{
_userManager = userManager;
_appDbContext = appDbContext;
}
[HttpPost]
public async Task<IActionResult> Post()
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
AppUser user = new AppUser();
user.Email = "email#mail.co.uk";
user.FirstName = "mark";
user.LastName = "Macneill";
user.UserName = "Saints22";
await _userManager.CreateAsync(user, "P4$$word");
return new OkObjectResult("Account created");
}
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
return new string[] { "value1", "value2" };
}
}
You don't seem to be using the IdentityUser directly but instead a AppUser class. This might be a class extending IdentityUser<> or IdentityUser. I am not sure if you have the rest of your setup right, so here is the process:
If you have created a custom AppUser class, let's say you created it as follows:
class AppUser : IdentityUser<int>
This would mean you have assigned the primary key as in int. However, if you extended IdentityUser instead, note that IdentityUser extends IdentityUser<string>, meaning your key is a string. Going ahead I am going to assume your key is int. If not, you can make the changes accordingly (change all ints to your key type.
In your startup class, you need to be adding the following to register this as your user class used for Identity
services.AddIdentity<AppUser, IdentityRole<int>>()
.AddEntityFrameworkStores<ApplicationDbContext>();
Your ApplicationDbContext needs to be defined as follows:
public class ApplicationDbContext : IdentityDbContext<AppUser, IdentityRole<int>, int>
If you don't want to use IdentityRole<int> in the above two pieces of code, you can define a custom role class as follows:
public AppUserRole : IdentityRole<int>
Once you have these setup, you need to inject UserManager<AppUser> not UserManager<IdentityUser>.
Thank you neville-nazerane
I had IdentityUser in dependency injection and all I had to do was change IdentityUser to AppUser.
Startup.cs
services.AddIdentity<AppUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
I have this seeder class which is called at the end of the Startup.cs file in the Configure method:
public class UserSeeder
{
private readonly ApplicationDbContext _context;
private readonly UserManager<ApplicationUser> _userManager;
public UserSeeder(ApplicationDbContext context, UserManager<ApplicationUser> userManager)
{
_context = context;
_userManager = userManager;
}
public async Task Seed()
{
if (!await _context.Users.AnyAsync())
{
var user = new ApplicationUser()
{
UserName = "admin",
Email = "admin#test.com"
};
await _userManager.CreateAsync(user, "passwort4admin");
}
}
}
The code is executed and I even put a try/catch around the method call but no error happens AND no user is inserted into the database!
Why not?
The problem is the complexity of the password. Add capital and numbers and symbols the issue will solve
Behind the scenes the UserManager<> uses a IUserStore did you configure this userstore in the startup.cs in the IOC container?
services.AddIdentity<ApplicationUser, ApplicationRole>()
.AddEntityFrameworkStores<MyContext, Guid>()
.AddUserStore<ApplicationUserStore>() //this one provides data storage for user.
.AddRoleStore<ApplicationRoleStore>()
.AddUserManager<ApplicationUserManager>()
.AddRoleManager<ApplicationRoleManager>()
.AddDefaultTokenProviders();
In my case, it was a space in the user name. It's possible that you have other constraints set up that make the user creation operation illegal.
In most cases, you can obtain explicit information on the exact cause of the error by investigating the message in the returned object from the operation.
IdentityUser userToBe = ...
IdentityResult result = await UserManager.CreateAsync(userToBe);
if(!result.Succeeded)
foreach(IdentityError error in result.Errors)
Console.WriteLine($"Oops! {error.Description} ({error.Code}));
I forgot to set "Username", it cannot be empty.
I set UserName=userEmail and it works
I am using asp.net core mvc with asp.net identity.
My ApplicationUser has a property: Country => 'GB' or 'FR' etc...
I want to set the System.Threading.Thread.CurrentThread.CurrentCulture to the value read from the applicationUser.Country. to display all datetime/number values correctly in my views as set by the user.
Thus I created this action filter:
public class LanguageActionFilter : ActionFilterAttribute
{
private readonly ILogger _logger;
private UserManager<ApplicationUser> _userManager;
public LanguageActionFilter(ILoggerFactory loggerFactory, UserManager<ApplicationUser> userManager)
{
_logger = loggerFactory.CreateLogger("LanguageActionFilter");
_userManager = userManager;
}
public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var user = await _userManager.GetUserAsync(context.HttpContext.User);
Thread.CurrentThread.CurrentCulture = new CultureInfo(user.Country);
await base.OnActionExecutionAsync(context, next);
}
}
On the action/controller where this actionfilter is set I also run this code:
public async Task<IActionResult> Index()
{
var user = await this.userManager.GetUserAsync(User);
var todos = await service.GetTodosAsync(user.Id);
return View(todos);
}
At the moment I call the this.userManager.GetUserAsync(User);
TWO TIMES !
Then I thought about passing the user object to the
context.HttpContext.Items.Add("applicationUser", user);
and grab this user object again within the Index() action method:
var user = (ApplicationUser)base.HttpContext.Items["applicationUser"];
That worked, but is that a good idea? Do you see any problems with that approach?
What you did is fine...
I'm quoting from the definition of the HttpContext class :
/// <summary>
/// **Gets or sets a key/value collection that can be used to share data within the scope of this request.**
/// </summary>
public abstract IDictionary<object, object> Items { get; set; }
Edit : It seems ef core doesn't support first level cache. So my idea went for nothing
Your code works, but i think you won't gain considerable performance.
You probably use aspnet identity with entity framework. Since ef supports first level cache. , if you call this.userManager.GetUserAsync(User); more than one, ef retrieves user entity from database only once. As a result two ways approximately have same effect for performance. I wouldn't use HttpContext.Items for your case.
I am building a site that has Users that belong to an Account. The account is identified by an AccountId which is a foreign key for most data in the DB such as Charges (associated to an Account) or Receipts (associated to an Account).
Rather than hitting the DB every time the repository needs to be polled for data to get the user's AccountId, I wanted to add the AccountId as a claim. The goal being to do something like:
_repository.GetAllChargesByAccountId(User.Identity.GetAccountId());
I'm finding only tidbits and partial solutions for this and I haven't been able to resolve some differences between those examples and my specific environment (ASP.NET Core RC1, MVC 6, EF7).
I have derived a class from IdentityUser for adding attributes about the user:
public class UserIdentity : IdentityUser {
public static object Identity { get; internal set; }
public int AccountId { get; set; }
}
I have created a UserIdentityContext that derives from IdentityDbContext that I'm using for my EF user store.
And I have the following AuthController:
public class AuthController : Controller {
private SignInManager<UserIdentity> _signInManager;
public AuthController(SignInManager<UserIdentity> signInManager) {
_signInManager = signInManager;
}
public IActionResult Login() {
if (User.Identity.IsAuthenticated)
return RedirectToAction("Dashboard", "App");
return View();
}
[HttpPost]
public async Task<ActionResult> Login(LoginViewModel vm, string returnUrl) {
if (ModelState.IsValid) {
var signInResult = await _signInManager.PasswordSignInAsync(vm.Username, vm.Password, true, false);
if (signInResult.Succeeded) {
if (String.IsNullOrWhiteSpace(returnUrl))
return RedirectToAction("Dashboard", "App");
else return RedirectToAction(returnUrl);
} else {
ModelState.AddModelError("", "Username or password is incorrect.");
}
}
return View();
}
public async Task<IActionResult> Logout() {
if (User.Identity.IsAuthenticated) {
await _signInManager.SignOutAsync();
}
return RedirectToAction("Index", "App");
}
}
Looking at other posts, it sounds like I need to add an IdentityExtension in order to access the claim as User.Identity.GetAccountId() and generate a custom user identity as in this answer: How to extend available properties of User.Identity but obviously this is done in an older version and many of the method calls are not applicable anymore.
Thanks in advance for any answers or guidance.
if you have added a claim for AccountId you can then easily write an extension method to get it:
public static string GetAccountId(this ClaimsPrincipal principal)
{
if (principal == null)
{
throw new ArgumentNullException(nameof(principal));
}
var claim = principal.FindFirst("AccountId");
return claim != null ? claim.Value : null;
}
if you need help on how to add a custom claim see this question
How would you seed users? I am following their documents here, but they only show how to seed data that is inserted directly by the ApplicationDbContext.
In the Account controller, the UserManager is created through DI. How would I instantiate a UserManager in the Seed method?
public class SeedData
{
public static void Initialize(IServiceProvider serviceProvider)
{
var context = serviceProvider.GetService<ApplicationDbContext>();
var userManager = serviceProvider.GetService<UserManager<ApplicationUser>>();
Then in Startup.cs in the Configure method:
SeedData.Initialize(app.ApplicationServices);
In the startup.cs in the configure method, you can add the following code to get the UserManager and from there seed the users.
var userManager = app.ApplicationServices.GetService<UserManager<ApplicationUser>>();
You would create a class in the lines of
public class UserSeed
{
private ApplicationDbContext _context;
private UserManager<ApplicationUser> _mgr;
public UserSeed(ApplicationDbContext context,UserManager<ApplicationUser> userManager)
{
_context = context;
_mgr = users;
}
public void UserSeedData()
{
var user = new ApplicationUser { UserName = "foo#foo.com", Email = "foo#foo.com" };
var result = _mgr.CreateAsync(user,"fooPassword");
......
......
}
}
In the configure method of your startup.cs take userSeed through DI like
public async void Configure(...., UserSeed userSeedData)
and then inside the method you call
userSeedData.UserSeedData();
Don't use this code as is, you would probably want to check if the user already exists before you create it.
Note: This method will run once every time your application starts.