I am developing a MVC 5 internet application and have a question in regards to having a custom object in the ApplicationUser object.
I have a public virtual Account account object in the ApplicationUser object that holds many maximum count variables for objects in my MVC 5 application. This is set for each user when a user registers an account.
Before I create a model object, I check to see if the user has not exceeded their maximum count for the model object. This is done in a service class for the object.
Here is an example of the code used for a file object:
var userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
ApplicationUser user = userManager.FindByNameAsync(userName).Result;
int maxFiles = user.account.maxFiles;
I am using many service classes for many objects with similar code to retrieve the ApplicationUser object.
My question is this: Rather than retrieving the ApplicationUser object each time, for each service class, is it possible to store this object when the user logs on, and then refer to this object before a model object is created? If so, where can I store this object for the above purpose?
Also, how much memory/bandwidth is used when retrieving the ApplicationUser object? Is there minimal memory/bandwidth used such that I do not need to worry about memory/bandwidth when retrieving the ApplicationUser object each time before I create a model object?
Thanks in advance.
That is a typical case for adding a claim with a value on logged-in identity.
When user logs in you add a claim with this value. Value of this claim is then persisted in the cookie. And is available every time user comes back and this is very fast look-up that is happening anyway for cookie authentication.
To add this claim to the identity, you need to override CreateIdentityAsync method on ApplicationUserManager:
public override async Task<ClaimsIdentity> CreateIdentityAsync(ApplicationUser user, string authenticationType)
{
var identity = await base.CreateIdentityAsync(user, authenticationType);
identity.AddClaim(new Claim("MyApplication:maxFiles", user.account.maxFiles));
return identity;
}
Then to access this you can create an extension method:
public static int GetMaxFiles(this IPrincipal principal)
{
if (principal == null)
{
throw new ArgumentNullException("principal");
}
var claimsPrincipal = principal as ClaimsPrincipal;
if (claimsPrincipal == null)
{
throw new DomainException("User is not authenticated or IPrincipal is not ClaimsPrincipal");
}
var claim = claimsPrincipal.Claims.FirstOrDefault(c => c.Type == "MyApplication:maxFiles");
int maxfiles = 0;
if(int.TryParse(claim.Value, out maxfiles))
{
return maxfiles;
}
throw new Exception("Claim value is not an integer")
}
And in your application you can use it as var maxFiles = HttpContext.Current.User.GetMaxFiles(). Or if it is a Controller or a View, then you can do var maxFiles = User.GetMaxFiles().
This is very fast lookup and does not reach into your database.
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);
I've been looking at some of the answers on this site about saving model data to session but none seem to work for me, or most likely I am not understanding it correctly and not sure how to implement it.
This is the latest solution I've been trying.
c# - How to save object to session in ASP.NET
In the Index I get an error on declaring the model telling me User doesn't exist
Model
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public string Password { get; set; }
}
Controller
public ActionResult Index()
{
using (DefaultConnection db = new DefaultConnection())
{
var model = from u in db.Users select u;
var vm = (User)Session["User"];
return View(vm);
}
}
View
#model User
#Model.Username
I am not quite sure what you are trying to achieve here. But i can see some problems in your code
var vm = (User)Session["User"];
You are trying to access a session variable with key User and trying to cast it to a User instance. That means, Before executing this code, you should be setting a valid user object to Session["User"]. If you do not do that, your casting will fail(code will crash) because Session does not have any item for the key "User"
If you haven't set it yet, Before accessing this session object, you should set a valid User object to session.
var us = new User { Id=1, Username="test"};
Session["User"] = us;
Later, before accessing from session item, you should always check whether it is null or not
User u = null;
if(Session["User"]!=null)
{
u = Session["User"] as User;
// you may use u now.
}
Also, you are querying from the Users table and selecting the records to the variable model. But you are not using that anywhere in your code. I am not sure why you want to do that.
I am not sure why you are using session. If you are trying to pass data between your action method to view, there are other better solutions like using a viewmodel (preferred), ViewBag etc.
Remember Session data is available across the entire application for the current session., not just one page. Use it wisely.
In order to save your model to the Session, you need to set the session object first e.g.
using (DefaultConnection db = new DefaultConnection())
{
var model = from u in db.Users select u;
Session["User"] = model; //Part where you set / save into the session
var vm = Session["User"] as User; //Part where you retrieve into the session
return View(vm);
}
User user = Session["User"] as User;
if ( user == null) user = new User();
If you want to access your ViewModel on the view, you can do it in many ways one way is to associate your view to a model (known as strongly typed views). You can do this in your view:
#model type #*Associate your view to a ViewModel, where type is your ViewModel Class*#
<h1>#Model.Name</h1> #*access the properties of your view using #Model *#
#model is used to associate a model to a view while #Model is used to access the associated model of a view. I suggest you read first on how ASP.NET MVC works. You may visit the ASP.NET MVC tutorial on views
MVC 5 with asp.net identity 2.0
I understand how to get the currently logged in user id. I have a need to get the user id of a different user in the system. How is this done?
Ex. I need to get the user id of user names "fsmith" ...
Inside the AccountController you can call the following method to get user object and then get the User Id.
var user = await UserManager.FindByNameAsync(userName);
var userId = user.Id;
If you are not inside AccountController you can get the UserManager reference using the following code
var UserManager = HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
To add to Shashi's useful answer...
If you're inside the account controller you can use the existing UserManager:
var user = await UserManager.FindByNameAsync(userName);
var userId = user.Id;
In other controllers (and maybe select other places where HttpContext is available) you create a User Manager with:
var um = HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
But what it took me a while to realize is you can get at it just about anywhere with this last bit. For instance I used it in my seed method (in Configuration.cs) so I could seed the User property of another model:
var um = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(*insert-your-db-context-here*));
One way would to use the FindByNameAsync method of your user manager (or the generic version of it). It takes the name of the user (in your case "fsmith") and returns a task of a user object. This user object contains all the information available about that user.
A code snippet could look similar to the following one (if you're looking for the database ID):
var user = await UserManager.FindByNameAsync("fsmith");
var userId = user.Id;
EDIT
The UserManager instance is normally declared as follows (keep in mind that there are many ways of declaring it, for example when using Dependency Injection):
public AccountController()
: this(new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext())))
{
}
public AccountController(UserManager<ApplicationUser> userManager)
{
UserManager = userManager;
}
public UserManager<ApplicationUser> UserManager { get; private set; }
If you are using Bearing Token Auth, the above sample do not return an Application User.
Instead, use this:
ClaimsPrincipal currentUser = this.User;
var currentUserName = currentUser.FindFirst(ClaimTypes.NameIdentifier).Value;
ApplicationUser user = await _userManager.FindByNameAsync(currentUserName);
return user.Id;
This works in apsnetcore 2.0. Have not tried in earlier versions.
I've added custom fields to the ApplicationUser class
I've also created a form through which the user can enter/edit the fields.
However for some reason I'm not able to update the fields in the database.
[HttpPost]
[ActionName("Edit")]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Manage(EditProfileViewModel model)
{
if (ModelState.IsValid)
{
// Get the current application user
var user = User.Identity.GetApplicationUser();
// Update the details
user.Name = new Name { First = model.FirstName, Last = model.LastName, Nickname = model.NickName };
user.Birthday = model.Birthdate;
// This is the part that doesn't work
var result = await UserManager.UpdateAsync(user);
// However, it always succeeds inspite of not updating the database
if (!result.Succeeded)
{
AddErrors(result);
}
}
return RedirectToAction("Manage");
}
My problem is similar to MVC5 ApplicationUser custom properties, but that seems to use an older version of Identity because the IdentityManager class doesn't seem to exist.
Can someone guide me on how to update User info in the database?
UPDATE:
If I include all the fields in the register form, all the values are stored in the appropriate field in a new record of the Users table from the database.
I don't know to make changes to the fields of an existing user (row in the users table). UserManager.UpdateAsync(user) doesn't work.
Also note my issue is more Identity oriented than EntityFramework
OK... I spent hours trying to figure why userManager.updateAsync would not persist the user data that we edit ... until I reached the following conclusion:
The confusion arises from the fact that we create the UserManager in one line like this:
var manager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new MyDbContext()));
...then we use manager.UpdateAsync( user ); but that will update the user in the context, and then we will need to save changes to the dbcontext of the Identity. So, the question is how to get the Identity DBcontext in the easiest way.
To solve this, we should not create the UserManager in one line ... and here is how I do it:
var store = new UserStore<ApplicationUser>(new MyDbContext());
var manager = new UserManager(store);
then after updating the user by calling
manager.UpdateAsync(user);
then you go to the context
var ctx = store.context;
then
ctx.saveChanges();
wahooooooo...persisted :)
Hope this will help someone who pulled their hair for a few hours :P
If you leave any of the fields for ApplicationUser OR IdentityUser null the update will come back as successful but wont save the data in the database.
Example solution:
ApplicationUser model = UserManager.FindById(User.Identity.GetUserId())
Add the newly updated fields:
model.Email = AppUserViewModel.Email;
model.FName = AppUserViewModel.FName;
model.LName = AppUserViewModel.LName;
model.DOB = AppUserViewModel.DOB;
model.Gender = AppUserViewModel.Gender;
Call UpdateAsync
IdentityResult result = await UserManager.UpdateAsync(model);
I have tested this and it works.
The OWIN context allows you to get the db context. Seems to be working fine so far me, and after all, I got the idea from the ApplciationUserManager class which does the same thing.
internal void UpdateEmail(HttpContext context, string userName, string email)
{
var manager = context.GetOwinContext().GetUserManager<ApplicationUserManager>();
var user = manager.FindByName(userName);
user.Email = email;
user.EmailConfirmed = false;
manager.Update(user);
context.GetOwinContext().Get<ApplicationDbContext>().SaveChanges();
}
The UserManager did not work, and As #Kevin Junghans wrote,
UpdateAsync just commits the update to the context, you still need to save the context for it to commit to the database
Here is quick solution (prior to new features in ASP.net identity v2) I used in a web forms projetc. The
class AspNetUser :IdentityUser
Was migrated from SqlServerMembership aspnet_Users. And the context is defined:
public partial class MyContext : IdentityDbContext<AspNetUser>
I apologize for the reflection and synchronous code--if you put this in an async method, use await for the async calls and remove the Tasks and Wait()s. The arg, props, contains the names of properties to update.
public static void UpdateAspNetUser(AspNetUser user, string[] props)
{
MyContext context = new MyContext();
UserStore<AspNetUser> store = new UserStore<AspNetUser>(context);
Task<AspNetUser> cUser = store.FindByIdAsync(user.Id);
cUser.Wait();
AspNetUser oldUser = cUser.Result;
foreach (var prop in props)
{
PropertyInfo pi = typeof(AspNetUser).GetProperty(prop);
var val = pi.GetValue(user);
pi.SetValue(oldUser, val);
}
Task task = store.UpdateAsync(oldUser);
task.Wait();
context.SaveChanges();
}
I also had problems using UpdateAsync when developing a version of SimpleSecurity that uses ASP.NET Identity. For example, I added a feature to do a password reset that needed to add a password reset token to the user information. At first I tried using UpdateAsync and it got the same results as you did. I ended up wrapping the user entity in a repository pattern and got it to work. You can look at the SimpleSecurity project for an example. After working with ASP.NET Identity more (documentation is still non-existent) I think that UpdateAsync just commits the update to the context, you still need to save the context for it to commit to the database.
I have tried the functionality in the same way and when i call UserManager.Updateasync method it succeeds but there is no update in the database. After spending some time i found another solution to update the data in aspnetusers table which is following:
1) you need to create UserDbContext class inheriting from IdentityDbContext class like this:
public class UserDbContext:IdentityDbContext<UserInfo>
{
public UserDbContext():
base("DefaultConnection")
{
this.Configuration.ProxyCreationEnabled = false;
}
}
2) then in Account controller update user information like this:
UserDbContext userDbContext = new UserDbContext();
userDbContext.Entry(user).State = System.Data.Entity.EntityState.Modified;
await userDbContext.SaveChangesAsync();
where user is your updated entity.
hope this will help you.
Excellent!!!
IdentityResult result = await UserManager.UpdateAsync(user);
Based on your question and also noted in comment.
Can someone guide me on how to update User info in the database?
Yes, the code is correct for updating any ApplicationUser to the database.
IdentityResult result = await UserManager.UpdateAsync(user);
Check for constrains of all field's required values
Check for UserManager is created using ApplicationUser.
UserManager<ApplicationUser> UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
This works for me. I'm using Identity 2.0, it looks like GetApplicationUser isn't there anymore.
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
if (!string.IsNullOrEmpty(form["FirstName"]))
{
user.FirstName = form["FirstName"];
}
if (!string.IsNullOrEmpty(form["LastName"]))
{
user.LastName = form["LastName"];
}
IdentityResult result = await UserManager.UpdateAsync(user);
I am using the new EF & Identity Core and I have the same issue, with the addition that I've got this error:
The instance of entity type cannot be tracked because another instance
of this type with the same key is already being tracked.
With the new DI model I added the constructor's Controller the context to the DB.
I tried to see what are the conflict with _conext.ChangeTracker.Entries() and adding AsNoTracking() to my calls without success.
I only need to change the state of my object (in this case Identity)
_context.Entry(user).State = EntityState.Modified;
var result = await _userManager.UpdateAsync(user);
And worked without create another store or object and mapping.
I hope someone else is useful my two cents.
Add the following code to your Startup.Auth.cs file under the static constructor:
UserManagerFactory = () => new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId, UserManagerFactory),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
AllowInsecureHttp = true
};
The UserManagerFactory setting line of code is what you use to associate your custom DataContext with the UserManager. Once you have done that, then you can get an instance of the UserManager in your ApiController and the UserManager.UpdateAsync(user) method will work because it is using your DataContext to save the extra properties you've added to your custom application user.
I am using .Net Core 3.1 or higher version.Please follow the solution:
public class UpdateAssignUserRole
{
public string username { get; set; }
public string rolename { get; set; }
public bool IsEdit { get; set; }
}
private async Task UpdateSeedUsers(UserManager<IdentityUser> userManager, UpdateAssignUserRole updateassignUsername)
{
IList<Users> Users = await FindByUserName(updateassignUsername.username);
if (await userManager.FindByNameAsync(updateassignUsername.username) != null)
{
var user = new IdentityUser
{
UserName = updateassignUsername.username,
Email = Users[0].Email,
};
var result = await userManager.FindByNameAsync(updateassignUsername.username);
if (result != null)
{
IdentityResult deletionResult = await userManager.RemoveFromRolesAsync(result, await userManager.GetRolesAsync(result));
if (deletionResult != null)
{
await userManager.AddToRoleAsync(result, updateassignUsername.rolename);
}
}
}
}
I've implemented a base controller for my MVC 3 project to allow a common way of accessing a user entity from my db context :
public abstract class MyBaseController : Controller
{
protected DBEntitiesContainer db;
protected override void Initialize(System.Web.Routing.RequestContext requestContext)
{
base.Initialize(requestContext);
this.db = new DBEntitiesContainer();
}
public UserDetails GetActiveUserDetails()
{
UserDetails userDetails = GetObjects.GetActiveUserDetails(this.db);
return userDetails;
}
}
This works great and all my controllers have access to this.db.MyEntity and can retrieve a UserDetails object by calling this.GetActiveUserDetails()
However, the problem arises when I try to perform an update on the entity thus :
public class UpdateController : MyBaseController
{
public ActionResult Index()
{
UserDetails userDetails = this.GetActiveUserDetails();
userDetails.LastOnline = DateTime.Now;
UpdateModel(userDetails);
this.db.SaveChanges();
}
}
Any ideas why the UserDetails object is easily retrieved, but when I check my database after calling SaveChanges(), nothing has been updated ? I'm presuming I'm accessing my context in two different ways, but I can't see how I am...!
Edit: Here's the GetObjects.GetActiveUserDetails() method
public static UserDetails GetActiveUserDetails(DBEntitiesContainer db)
{
MembershipUser membershipUser = Membership.GetUser();
UserDetails userDetails;
try
{
if (membershipUser != null)
{
userDetails = (from u in db.UserDetails
where (u.UserId == (System.Guid)membershipUser.ProviderUserKey)
select u).First();
}
else
{
return GetGuestAccount();
}
}
catch
{
return GetGuestAccount();
}
return userDetails;
}
Not the cleanest method I know...
UpdateModel is helper method for Controller base class. it supports updating the properties of an object we pass it using the incoming form parameters during HttpPost action method.
It uses relfection to find out the property names of the object (Model object what we passed) and then automatically converts the assigns values to them based on the input values submitted by the form ( client form).
In your case you when u use update model it has no input value find associated model and it make the model default values as it is in database.
try to comment the updatemodel line and runt he code... it must work.
It's not a good idea to setup data access this way. One reason is that you should dispose of the db context after you use it. This means that using your method, this leaves the database connection open until garbage collection occurs, which could be minutes or hours later. As other web requests come in, new database connections are created, and again those are not disposed of either.. etc.. etc.. it's a pseudo-memory leak (not a true memory leak because it will eventually get collected, but it means resources are being used well after they are needed)