Update custom user profile fields with SimpleMembershipProvider? - c#

I added a custom field to the UserProfile table named ClassOfYear and I'm able to get the data into the profile during registration like this:
var confirmationToken = WebSecurity.CreateUserAndAccount(model.UserName,
model.Password,
propertyValues: new { ClassOfYear = model.ClassOfYear },
requireConfirmationToken: true);
However, now I want to be able to update the profile when I manage it but I can't seem to find a method to do so. Do I need to simply update the UserProfile table myself? If not, what is the appropriate way of doing this?
FYI, I'm using Dapper as my data access layer, just in case it matters. But, like stated, I can just update the UserProfile table via Dapper if that's what I'm supposed to do, I just figured that the WebSecurity class, or something similar, had a way already since the custom user profile fields are integrated with the CreateUserAndAccount method.
Thanks all!

There is nothing in the SimpleMembershipProvider code that does anything with additional fields except upon create.
Simply query the values yourself from your ORM.
You can use the WebSecurity.GetUserId(User.Identity.Name) to get the user's id and then Dapper to query the UserProfile table.

Just in case anyone facing the same problem. After fighting a lot with the SimpleMembership I got a solution that populates both the webpages_Membership and my custom Users table. For clarification follow my code:
public ActionResult Register(RegisterModel model)
{
if (ModelState.IsValid)
{
TUsuario userDTO= new TUSer()
{
Name = model.Name,
Login = model.Login,
Pass = model.Pass.ToString(CultureInfo.InvariantCulture),
Active = true,
IdCompany = model.IdCompany,
IdUserGroup = model.IdUserGroup,
};
try
{
WebSecurity.CreateUserAndAccount(model.Login, model.Pass, new { IdUser = new UserDAL().Seq.NextVal(), Name = userDTO.Name, Login = userDTO.Login, Active = userDTO.Active, Pass = userDTO.Pass, IdCompany = userDTO.IdCompany, IdUserGroup = userDTO.IdUserGroup });
WebSecurity.Login(model.Login, model.Pass);
After cursing the framework a lot, that gave me a bliss of fresh air :)
PS.: The users table is specified in the global.asax file using the WebSecurity.InitializeDatabaseConnection functon.

Related

Previously no-tracking entities get tracked upon calling Savechages(). Is this normal?

I have a simple controller in which I create and add a user and a profile to the database
public async ActionResult AddUserAndProfile(string id)
{
_context.Users.Add(new User { Id = id });
_context.SaveChanges(); // If this line is removed, error doesn't occur.
var profile = new Profile
{
Id = "id",
User = _context.Users.AsNoTracking().FirstOrDefault(u => u.Id.Equals(id))
}; // Exception given on this line.
_context.Profiles.Add(profile);
_context.SaveChanges();
return Ok();
}
When I call this controller with id = "0" I get the following exception:
The instance of entity type 'User' cannot be tracked because another instance with the key value '{Id: 0}' is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.
However, if I remove the first SaveChanges() call, this error is not shown.
1st question: Is it intended that entities get tracked after saving changes, wouldn't it make sense that they get tracked beforehand only? Also afaik, add actions don't mark entities as tracked.
2nd question: When is the best time to call SaveChages() in this situation? (It is important to note that add user and add profile actions are in different repo methods in the real project. I simplified the code here.)
3rd question: What is the best way to add foreign keys in situations like this?
You should be able to save whole hierarchy within one call to SaveChanges:
var profile = new Profile
{
Id = "id",
User = new User { Id = id }
};
_context.Profiles.Add(profile);
_context.SaveChanges();
It should answer 2nd and 3rd questions I think. As for the first one - there is a github issue with request to "Do not track after SaveChanges()".
Since you have different methods you can just set the UserId property on Profile (if you have explicitly added it to Profile entity) directly.
Save the user you create, and then specify that user for the profile.
var user = new User { Id = id };
_context.Users.Add(user);
_context.SaveChanges();
var profile = new Profile {Id = "id", User = user};

ASP Identity Role Does Not Exist Error

I am trying to create a page in my MVC .NET CORE application in which a user can change the roles of other users. At this point the view and model bindings all work great, but when actually trying to save the roles back to the database I am getting an error saying that the roles don't exists. My approach is this:
Get list of roles from the model.
Delete existing roles for specified user.
Add roles back using list from model.
Do this all in a transaction in case of error.
My code in the controller is as follows:
var user = await _userManager.FindByIdAsync(id);
if (user == null)
{
return View("Error");
}
using (_context.Database.BeginTransaction())
{
var removeResult = await _userManager.RemoveFromRolesAsync(user, await _userManager.GetRolesAsync(user));
if (!removeResult.Succeeded)
{
return View("Error");
}
var addResult = await _userManager.AddToRolesAsync(user, model.MemberRoles);
if (!addResult.Succeeded)
{
return View("Error");
}
}
Where model.MemberRoles as a List<string> of roles. The strange part is is that this process is failing on _userManager.RemoveFromRolesAsync, even though I am passing the existing roles of the user directly into the function. I have tried ditching the UserManager class and going with ef but had no luck. I also verified that the roles in the model match what is in the database. Is there anything obviously wrong that would cause this to fail? Thanks.
Edit
Here is the error I am running into:
InvalidOperationException: Role ADMINISTRATOR does not exist.
Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserStore`4.<AddToRoleAsync>d__32.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
And here are the roles defined in my database:
When you try to add a user to a role, it first tries to find the role. In Sql Profiler, you can see that it is actually searching by the NormalizedName field rather than the Name field.
exec sp_executesql N'
SELECT TOP(2) [r].[Id], [r].[ConcurrencyStamp], [r].[Name], [r].[NormalizedName]
FROM [AspNetRoles] AS [r]
WHERE [r].[NormalizedName] = #__normalizedRoleName_0',N'#__normalizedRoleName_0 nvarchar(256)',#__normalizedRoleName_0=N'ADMINISTRATOR'
The solution is to set the NormalizedName field to the Uppercase of the Name field when you create the Roles:
So in the SeedData class:
/* Create Roles */
IEnumerable<UserRole> allRoles = Enum.GetValues(typeof(UserRole)).Cast<UserRole>();
foreach (UserRole role in allRoles)
{
if (!await roleManager.RoleExistsAsync(role.ToString()))
{
await roleManager.CreateAsync(new IdentityRole {
Name = role.ToString(),
NormalizedName = role.ToString().ToUpper()
});
}
}
I'm actually surprised you're not getting an InvalidCastException instead, as it's not possible to directly cast string[] to List<string>. Not really sure why you're doing it that way in the first place, since you could just as easily just call .ToList(), instead of .ToArray(), and call it a day, if you want a list. Or, since the return type of GetRolesAsync is IList<string>, anyways, you don't even really need to do that. Right now, that's the only problem I see with your code, though.

Get all role names of a cube in say a string array

With help of AMO (Application Management Objects) was able to find a solution to clear members of a particular role in a cube with c# code.
Simple code i used was :
Role role = new Role();
role = db.Roles.FindByName("roleName1");
role.Members.Clear();
role.Update()
Now i seem to have a situation. I do not know before hand the roleName parameter that i pass in "FindByName" function..
I do not know RoleName before.
Any kind of idea so as to how to get role names for a cube?
You can use
foreach(Role role in db.Roles) {
if(<some condition, possibly using properties like role.Name>) {
role.Members.Clear();
role.Update();
}
}
You can find the properties and methods of the Role class here: http://msdn.microsoft.com/en-us/library/microsoft.analysisservices.role.aspx.
Got it finallyy :)
foreach (Microsoft.AnalysisServices.Role CubeDbRole in db.Roles)
{
string Rolename = CubeDbRole.Name;
MessageBox.Show(Rolename);
CubeDbRole.Members.Clear();
CubeDbRole.Update();
foreach (Microsoft.AnalysisServices.RoleMember CubeRoleMember in CubeDbRole.Members)
{
//In case you want to display members
MessageBox.Show(CubeRoleMember);
}
}
Thanks :)

ManyToMany insertion issue

The goal
Create a new market and add its manager successfully.
The problem
Entity Validation Failed - errors follow:
MyApp.Models.Data.Users failed validation
Email : The Email field is required.
PHash : The PHash field is required.
PSalt : The PSalt field is required.
The scenario
I'm creating a market on my application and I can set to it a manager. In other words, I'm attaching to a market a user that already exists. I'm trying to do this by the following code:
[HttpPost]
public ActionResult Add(Market market)
{
[...]
Market marketBasics = new Market
{
Name = market.Name,
Slug = market.Slug,
Manager = market.ManagerId
};
[...]
User user = new User
{
Id = market.ManagerId
};
db.Markets.Add(marketBasics); // Here I insert all the information
// into "markets" table.
marketBasics.User.Add(user); // Here I (attempts to) insert into
// "users_in_markets"
[...]
}
As you can see, I'm not setting the user's Email, PHash or PSalt — I'm not registering a new user, but I'm associating an existing (user) to a (new) market.
So I ask: How can I resolve this?
Technical details
I'm using Entity Framework 5 + C# + MySQL.
The user is not added because you add the user to the market after the market has been added to the database. Therefore the user is not present in the market in the database.
marketBasics.User.Add(user); //First add user to market
db.Markets.Add(marketBasics); //Then add market to database
I hope that helped!
With the answer of Davud and a little research, I discover that I need to attach the user's Id to the User entity.
Follow the final code:
[HttpPost]
public ActionResult Add(Market market)
{
[...]
Market marketBasics = new Market
{
Name = market.Name,
Slug = market.Slug,
Manager = market.ManagerId
};
[...]
User user = new User
{
Id = market.ManagerId
};
db.User.Attach(user);
marketBasics.User.Add(user);
db.Markets.Add(marketBasics);
[...]
}
To learn more about Attach method, take a look here, on MSDN.
Success for you all!

MVC 2.0 C# ModelUpdate wont update

I'm having trouble getting a ModelUpdate or TryModelUpdate to work in my code.
I'm using the default Role Manager and Login system created by MVC when running the ASP.Net configuration tool. What I'm trying to do is add another column to the Users table so I can record if my users are also customers. So I want to record their CustomerID there.
I used the ADO Entity Data Model to generate all my model code based off my database. The code it created for the field I want to update is here:
public string CustomerID
{
get
{
return this._CustomerID;
}
set
{
this.OnCustomerIDChanging(value);
this.ReportPropertyChanging("CustomerID");
this._CustomerID = global::System.Data.Objects.DataClasses.StructuralObject.SetValidValue(value, true);
this.ReportPropertyChanged("CustomerID");
this.OnCustomerIDChanged();
}
}
private string _CustomerID;
partial void OnCustomerIDChanging(string value);
partial void OnCustomerIDChanged();
In my controller Im trying to update the CustomerID field with this code:
var userToUpdate = dbu.aspnet_Users.First(u => u.UserName == User.Identity.Name);
UpdateModel(userToUpdate, new string[] { "CustomerID"}, txtID);
dbu.SaveChanges();
But I get an error saying the overload method has some invalid arguments.
I get that the issue is in assigning txtID to CustomerID based off the error, but whats the correct way to do it?
If I need to add more info please let me know.
I figured it out. Apparantly ModelUpdate won't let me pass in custom data and it needs to be passed in from the Form Collection. So using UpdateModel(userToUpdate, new string[] {"CustomerID"}, form.ToValueProvider()); worked.

Categories

Resources