Having a problem with saving user changes to the database, like changing the person's name. I'm using the IdentityModel that is automatically created in a new VS2013 web project using individual authentication. Sadly, the template doesn't allow you to change any user information, other than changing roles. I'm looking around via google, I haven't found much. Anyone implement updating using the base identity code?
This is the closest thing I found:
Updating user data - Asp.net Identity
I haven't been successful at incorporating default template. I've just started using Identity this week, so it might be my lack of understanding that's the problem.
var updatedUser = new ApplicationUser
{
Id = model.UserId,
UserName = model.UserName,
CustomerId = model.CustomerId,
Email = model.EmailAddress,
FirstName = model.FirstName,
LastName = model.LastName,
PhoneNumber = model.PhoneNumber,
};
...
var result = await UserManager.UpdateAsync(updatedUser);
My UserManager is created like this:
return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
I get the following error in the browser:
Attaching an entity of type 'ApplicationUser' failed because another entity of the same type already has the same primary key value. This can happen when using the 'Attach' method or setting the state of an entity to 'Unchanged' or 'Modified' if any entities in the graph have conflicting key values. This may be because some entities are new and have not yet received database-generated key values. In this case use the 'Add' method or the 'Added' entity state to track the graph and then set the state of non-new entities to 'Unchanged' or 'Modified' as appropriate
Thanks
The problem I had was I creating an ApplicationUser and saved the object to the database. Since Identity uses Entity Framework under the covers, the entity state of the "updatedUser" object is Added. So Entity Framework tried to INSERT in to the Identity database, causing the conflict. So, you have to get the user and update the returned user object for Entity Framework to know that the entity state is Modified. Here's the working code:
var user = await UserManager.FindByIdAsync(model.UserId);
user.Email = model.EmailAddress;
user.CustomerId = model.CustomerId;
user.FirstName = model.FirstName;
user.PhoneNumber = model.PhoneNumber;
user.LastName = model.LastName;
var result = await UserManager.UpdateAsync(user);
You can also use the AuthContext and update the state to EntityState.Modified. Below is an example. This will allow you to only make one call to the DB instead of two.
AuthContext authContext = new AuthContext();
authContext.Entry(updatedUser).State = EntityState.Modified;
Related
I have previously worked with Membership through "System.Web.Security.Membership"
Here, you can do the following:
var currentUser = Membership.GetUser();
var otherUser = Membership.GetUser(username);
...giving you a MembershipUser.
Now, with Identity, I can find a load of ways to get the current logged in user.
But no way to get another user.
I can use:
var userStore = new UserStore<IdentityUser>();
var userManager = new UserManager<IdentityUser>(userStore);
var user = userManager.Find(username, password);
But that takes both username and password, with no overload for just username.
How do i get the IdentityUser from only a username?
Almost every answer I find is connected to MVC.
This is for a WCF service, where authorization is made using Identity. And in some cases the user is getting to the site from an other site with a generated "token" - an encrypted string, containing the username. From here, user is logged in and a session-cookie is set, depending on users settings.
Also, is there a shorter way to get UserInformation?
"var currentUser = Membership.GetUser(username);"
is much more convenient than
"var user2 = (new UserManager((new UserStore()))).Find(username, password);"
UserManager has UserManager<TUser>.FindByNameAsync method. You can try using it to find user by name.
Using ASP.NET membership, if I want to get information for the current user, I can call MembershipUser.GetUser()
So, somehow the system must know the ID of the current user.
If this is correct, and all I want is the ID of the current user, is there a way to get it without returning all the user information from the database?
I know I can get the username of the current user using User.Identity.Name, but I need the ID.
The short answer is no you can't get only userID without retrieve whole user info when you use built-in membership provider, only by this
MembershipUser user = Membership.GetUser();
string UserID = user.ProviderUserKey.ToString();
But if you want to have method or property which retrieve only userID, you must re-implement your own membership provider or(it's simply) to implement IPrincipal interface
To return the UserId, use the command bellow in your controller:
User.Identity.GetUserId();
To return the UserName, use:
User.Identity.Name;
To return the user:
var user = db.Users.Find(User.Identity.GetUserId());
Please refer to the post: How to get current user, and how to use User class in MVC5?
As you've guessed, the Membership API doesn't support what you want out of the box. In the past, I've used a helper class instead of creating my own provider. In this case it's pretty simple, maybe something like this:
public static object GetUserId() {
return GetUserId(HttpContext.Current.User.Identity.Name, true);
}
public static object GetUserId(string userName) {
return GetUserId(userName, true);
}
public static object GetUserId(string userName, bool UpdateLastActivity) {
using (SqlConnection c = new SqlConnection(CONNECTION_STRING)) {
string sql = #"
DECLARE #UserId uniqueidentifier
SELECT #UserId=u.UserId
FROM dbo.aspnet_Applications AS a
,dbo.aspnet_Users AS u
,dbo.aspnet_Membership AS m
WHERE
a.LoweredApplicationName=LOWER(#ApplicationName)
AND u.ApplicationId=a.ApplicationId
AND u.LoweredUserName=LOWER(#UserName)
AND u.UserId=m.UserId;
IF #UserId IS NOT NULL AND #UpdateLastActivity=1
UPDATE dbo.aspnet_Users
SET LastActivityDate=#CurrentTimeUtc
WHERE UserId=#UserId;
SELECT #UserId
";
using (SqlCommand cmd = new SqlCommand(sql, c)) {
cmd.Parameters.AddWithValue("#ApplicationName", Roles.ApplicationName);
cmd.Parameters.AddWithValue("#UserName", userName);
cmd.Parameters.AddWithValue("#UpdateLastActivity", UpdateLastActivity);
cmd.Parameters.AddWithValue("#CurrentTimeUtc", DateTime.UtcNow);
object id = null;
c.Open();
id = cmd.ExecuteScalar();
return id != DBNull.Value ? id : null;
}
}
}
Above is pretty similar to what's done in the Membership API when calling GetUser()
You can use MembershipUser.UserName to get the user id or try calling Membership.GetUser(User.Identity.Name) and see if that works for you.
After looking into this further, it seems that the ASP.NET Membership API does not track the user ID after all. It must track just the user name (User.Identity.Name). The ID is not required because Membership.GetUser() can find a user from an ID or user name.
In fact, Membership.GetUser() must simply translate to Membership.GetUser(User.Identity.Name). Since it can obtain the current user from the user name, there is no longer any reason to assume that the current user ID is cached anywhere.
So it appears the ID is not loaded into memory, and the only way to obtain the ID is to load the data from the database (which means loading the entire user record when using the ASP.NET Membership API).
Consider
int userId = WebSecurity.CurrentUserId;
Credit: https://stackoverflow.com/a/15382691/1268910
I tried the following:
public void UpdatePlayer(int id)
{
Player player = new Player() {ID = id};
player.Password = "12";
Entities.Players.Attach(player);
Entities.SaveChanges();
}
No change at the db.
What am I missing?
I think it might be because you're setting the values before you attach the object - the data context will not know what fields have changed. Try:
public void UpdatePlayer(int id)
{
Player player = new Player() {ID = id};
Entities.Players.Attach(player);
player.Password = "12";
Entities.SaveChanges();
}
attach is used for entities that already exist in the database, but you have to attach first, and then edit it, as another poster pointed out.
you should use .Add instead of .Attach if you are creating new items.
FYI Entity Framework 4 - AddObject vs Attach
As already mentioned when you attach entity it is set to Unchanged state so you have to manually set the state to Modified. But be aware that setting the state for whole entity can cause update of all fields. So if your Player entity has more than Id and Password fields all other fields will probably be set to default values. For such case try to use:
Entities.Players.Attach(player);
var objectState = Entities.ObjectStateManager.GetObjectStateEntry(player);
objectState.SetModifiedProperty("Password");
Entities.SaveChanges();
You can also try setting password after attaching the entity:
Entities.Players.Attach(player);
player.Password = "12";
Entities.SaveChanges();
When you attach an entity using Attach method, the entity will go into Unchanged EntityState, that is, it has not changed since it was attached to the context. Therefore, EF will not generate necessary update statement to update the database.
All you need to do is to give a hint to EF by changing the EntityState to Modified:
Entities.Players.Attach(player);
Entities.ObjectStateManager.ChangeObjectState(player, EntityState.Modified)
Entities.SaveChanges();
I have an ASP.NET Page that updates registered User Address Details for a selected record.
Below is the update method that I am calling from my controller.
When I am calling the ApplyPropertyChanges method, I am getting an error. Did anyone run into the same error while updating the record with Entity Framework?
Appreciate your responses.
Error message:
The existing object in the ObjectContext is in the Added state. Changes can only be applied when the existing object is in an unchanged or modified state.
My Update method:
[HttpPost]
public bool UpdateAddressDetail([Bind(Prefix = "RegUser")] AddressDetail regUserAddress, FormCollection formData)
{
regUserAddress.AD_Id = 3;
regUserAddress.LastUpdated = HttpContext.User.Identity.Name;
regUserAddress.UpdatedOn = DateTime.Now;
regUserAddress.AddressType = ((AddressDetail)Session["CurrentAddress"]).AddressType ?? "Primary";
regUserAddress.Phone = ((AddressDetail)Session["CurrentAddress"]).Phone;
regUserAddress.Country = ((AddressDetail)Session["CurrentAddress"]).AddressType ?? "USA";
miEntity.ApplyPropertyChanges(regUserAddress.EntityKey.EntitySetName, regUserAddress);
miEntity.SaveChanges();
return true;
}
The error is the object is detached from the context, and ApplyPropertyChanges thinks the object is added because it isn't attached. So you would need to query from the data context or get an attached form and then apply the changes then.
HTH.
What Dave Said
+
You need to Attach() the disconnected entity to your object context:
http://msdn.microsoft.com/en-us/library/system.data.objects.objectcontext.attach.aspx
miEntity.Attach(regUserAddress);
miEntity.SaveChanges();
Just add the following code before miEntity.SaveChanges():
miEntity.Entry(regUserAddress).State = EntityState.Modified;
First select the record (object entity), search by key through the ObjectContext. For example if the search ArticleSet EntitySet called for there to record, and once you get it modified its properties with new values and then call SaveChanges() of ObjectContext.
Example:
ObjectQuery<Article> myArt=Context.ArticleSet.Where myArt = (row => row.ArticleId == value);
myArt.Description=" new value ";
etc. ..
etc ...
Context.SaveChanges ();
I have an asp.net site which uses the ASP.net Membership provider. Each comment, entry etc in the DB is tracked by the userID.
Since MS doesn't provide a way to change the username, I've found the userNAME in the "users" table in the DB and there is only 1 place where the username appears.
My question is,
Is it safe to provide an "edit profile" page where the user is allowed to edit their own username. Of course i would handle this change in the background by directly changing the "username" value in the DB.
Are there any downsides to this ? I've created and modified some test accounts and it seems to be fine, i am just wondering if there is any known negatives to this before putting it into production.
cptScarlet's link was good, however I despise using stored procedures if I don't have to and I favor Entity Framework whenever possible. Here's what I did to change the user name, using EF 4.0 and .NET 4.0:
Right click project -> Add New Item -> ADO.NET Entity Data Model
Give it a proper name, I chose "MembershipModel.edmx" and click Add
Select Generate from database and click Next
Add the connection to your 'aspnetdb' database (the ASP.NET membership database)
Give it a proper name, I chose "MembershipEntities"
Click Next
Drill into Tables and select aspnet_Users
Change the Model Namespace to MembershipModel
Click Finish
Now you can add code to create the EF object context and modify the database:
public void ChangeUserName(string currentUserName, string newUserName)
{
using (var context = new MembershipEntities())
{
// Get the membership record from the database
var currentUserNameLowered = currentUserName.ToLower();
var membershipUser = context.aspnet_Users
.Where(u => u.LoweredUserName == currentUserNameLowered)
.FirstOrDefault();
if (membershipUser != null)
{
// Ensure that the new user name is not already being used
string newUserNameLowered = newUserName.ToLower();
if (!context.aspnet_Users.Any(u => u.LoweredUserName == newUserNameLowered))
{
membershipUser.UserName = newUserName;
membershipUser.LoweredUserName = newUserNameLowered;
context.SaveChanges();
}
}
}
}
Note: I did not account for application ID's in my code. I typically only ever have one application using the ASP.NET membership database, so if you have multiple apps, you'll need to account for that.