This is truly one of the strangest issues I've run into.
I have a Web API which uses EF. I have an audit table which takes an ApplicationUser. I create the new object, add it to the collection and then call SaveChangesAsync(). The weird part is, I get "User name MyUserName is already taken." error.
using (var context = new ApplicationDbContext())
{
var user = context.Users.Single<ApplicationUser>(x => x.UserName == model.UserName);
var sid = context.SessionIds.FirstOrDefault(x => x.Id == model.SessionId);
var audit = new Audit
{
Data = model.Data,
User = user,
IpAddress = Helper.GetClientIp(Request),
Session = sid != null ? sid : ItsMyChance.Entities.Entities.SessionId.Create(scoreModel.UserName, scoreModel.GameId)
};
context.Audits.Add(audit);
await context.SaveChangesAsync();
}
Update
This code has been working for years. The difference is I upgrade from .NET 4.5 to .NET 4.61
Update 2
I also tried the following but still receive the same error
[ForeignKey("User")]
public string UserId { get; set; }
public ApplicationUser User { get; set; }
Update 3
Trying to track this issue down I call
var entries = context.ChangeTracker.Entries();
It returns several entries, 1 for each object, including User. User shows Added and another as Unchanged. I can't figure out how this is happening.
In addition, I added the following before making any changes but there's no effect.
context.Configuration.AutoDetectChangesEnabled = false;
Since You are adding the complete user object in Audit , so SaveChangesAsync will save a new Entry for Audit and User also and since a user with same username already exists that's why you are getting this error. What you should do is just assign just the UserId (Whatever is referral key in Audit table for User) in Audit object
var audit = new Audit
{
Data = model.Data,
UserId = user.Id,
IpAddress = Helper.GetClientIp(Request),
Session = sid != null ? sid : ItsMyChance.Entities.Entities.SessionId.Create(scoreModel.UserName, scoreModel.GameId)
};
Related
I have asp.net core web api with React client. I'm adding data through my user interface created in React. In my api, Db context is added as scoped service, and each time my request finishes and new one is started, all my data from previous request is lost.
This is how my Configure services looks like:
services.AddDbContext<TicketingContext>(o=>o.UseLazyLoadingProxies().UseSqlServer(connectionString));
Controller method for posting data looks like this:
[HttpPost("{id}/tickets")]
public IActionResult CreateNewTicket(string id,
[FromBody] TicketForCreationDto ticketForCreation)
{
if (ticketForCreation == null)
{
return BadRequest();
}
var ticketEntity = _mapper.Map<Ticket>(ticketForCreation);
_ticketRepository.AddNewTicket(ticketEntity);
_ticketRepository.AddTicketToClient(id, ticketEntity);
if (!_ticketRepository.Save())
{
throw new Exception("Creating ticket failed on save");
}
var ticketToReturn = _mapper.Map<TicketDto>(ticketEntity);
return CreatedAtRoute("GetTicket", new {id=id, ticketId = ticketToReturn.Id }, ticketToReturn);
}
and methods in repository like this:
AddNewTicket:
public void AddNewTicket(Ticket ticket)
{
if (ticket.Id == Guid.Empty)
{
ticket.Id = Guid.NewGuid();
}
var dispatcher = AssignTicketToDispatcher(ticket);
if (dispatcher == null)
{
throw new Exception("There are no dispatchers matching this ticket");
}
dispatcher.UserTickets.Add(new UserTicket()
{
IdentityUser = dispatcher,
Ticket = ticket,
UserId = dispatcher.Id,
TicketId = ticket.Id
});
_context.Tickets.Add(ticket);
}
AddTicketToClient:
public void AddTicketToClient(string id, Ticket ticket)
{
var client = _identityUserRepository.GetClient(id);
if (client == null)
{
client = _context.Users.Where(u => u.UserName == "username").FirstOrDefault();
}
client.UserTickets.Add(new UserTicket()
{
IdentityUser = client,
Ticket = ticket,
UserId = client.Id,
TicketId = ticket.Id
});
}
Save:
public bool Save()
{
return (_context.SaveChanges() >= 0);
}
I want to be able to store data gained through multiple requests.
Does anyone have idea how to do that?
Use the database as it's the best method you have for persisting your data.
So When you do a request - at the end of the request, after your latest data is saved - query for the data from previous requests that you need and return it.
e.g. retrieve the last 5 requests saved newest first (where id is example for your primary key field):
var latestSaved = _context.UserTickets.OrderByDescending(x => x.id).Take(5);
Or amend to return all relevant data for e.g. active user by passing a user id stored client side.
Pass through any params you need to request the relevant data.
Use joins / includes set up in your entities. Whatever you need to do - make use of your entity relationships to get what you need from you database. Why try and replicate what it already does? :)
I'm working on a feature that allows a user to submit a post via a form, it successfully obtains the user Id from AspNetUsers.
I have implemented a radio button that gives a user the option to become an anonymous submission. The code is as follows,
protected void AddPost(object sender, EventArgs e)
{
var User = System.Web.HttpContext.Current.User.Identity.GetUserId();
if (Convert.ToBoolean(Int32.Parse(inputAnonymous.SelectedValue)))
{
User = "anonymous";
}
Post newPost = new Post()
{
postTitle = inputTitle.Text,
postBody = inputBody.Text,
postDescription = inputDescription.Text,
postCategory = inputCategory.SelectedValue,
postAnonymous = Convert.ToBoolean(Int32.Parse(inputAnonymous.SelectedValue)),
Id = User
};
using (var _dbContext = new ApplicationDbContext())
{
_dbContext.Posts.Add(newPost);
_dbContext.SaveChanges();
}
}
When the option "No" is selected for user anonymousity, the post is submitted normally, and it posts the id into the database etc, but when I click on "Yes" as I follow the debugger, it says that it is recognizing that it is anonymous, and that it should change the userId to the string "anonymous" and submit that to as the Id in the database, but I keep getting this error that prevents me that is as follows
SqlException: The INSERT statement conflicted with the FOREIGN KEY constraint "FK_dbo.Posts_dbo.AspNetUsers_Id". The conflict occurred in database "aspnet-WebEnterprise-20190201040107", table "dbo.AspNetUsers", column 'Id'.
The statement has been terminated.
UPDATE:
I've added this bit of code to my configuration.cs
{
var passwordHash = new PasswordHasher();
string password = passwordHash.HashPassword("Anonymous#123");
context.Users.AddOrUpdate(u => u.UserName,
new ApplicationUser
{
UserName = "Anonymous#Anonymous.com",
PasswordHash = password,
PhoneNumber = "12345678911",
Email = "Anonymous#Anonymous.com"
});
context.Roles.AddOrUpdate(
new IdentityRole { Id = "a1f04ba5-5600-4a6e-be43-ae0d6360c0ab", Name = "Anonymous" }
);
}
but when I update-database on the nuget packet manager, I get the following error
"An error occurred while updating the entries. See the inner exception for details."
There is no user with ID "anonymous" and you get an error because of that. The AspNetUsers doesn't contain any user whose ID is "anonymous". There's nothing wrong with this code, apart from using an Id value that doesn't exist.
As far as that problem goes, my suggestion is to create an Anonymous user, get his ID and bind that ID to all posts that should be anonymous. All anonymous post would then have the same ID, but that should not matter because they are anonymous anyway. Check this and this link for more info how to insert users via code-first architecture
I have a lot of tables in my database that use a user's identity User.Id as a foreign key. Now, in a lot of the requests I need to do the below lines it seems just in case the User is null (even if I add [Authorise] filter to the function).
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
ViewBag.ErrorCode = "1201";
ViewBag.ErrorMsg = "User not found";
return View("HandledError");
}
var userId = user.Id;
Does anyone know the best way I can access this without copying this same code for each function?
I guess I could have a function that retrieves the Id... and throws an exception on Null.. but then I would still need to write it in a try catch everytime anyway....
Any ideas?
User id is a claim. You can get it via:
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
Note: You'll need to add a using for System.Security.Claims.
In WebApi 2 you can use RequestContext.Principal from within a method on ApiController
string id;
id = User.Identity.GetUserId();
id = RequestContext.Principal.Identity.GetUserId();
I'm trying to update my existing ApplicationUser, by adding new "non-core" properties e.g. FirstName, LastName. I've extented these properties already via ApplicationUser: IdentityUser, this works overall fine.
Now, what I try to do is create a new ApplicationUser in my controller, and then pass it to repository to update user existing in db, like:
public ActionResult Members_Update(MemberViewModel mvm){
ApplicationUser user = new ApplicationUser();
user.FirstName = mvm.FirstName;
user.LastName = mvm.LastName;
_repo.UpdateApplicationUser(user);
}
In repository ( constructed as _context = new ApplicationDbContext(); ) I do:
public void UpdateAppicationUser(ApplicationUser updatedUser){
_context.Users.Attach(updatedUser);
var entry = _context.Entry(updatedUser);
...
and then I continue via
entry.Property(x => x.AccessFailedCount).IsModified = false;
entry.Property(x => x.EmailConfirmed).IsModified = false;
entry.Property(x => x.LockoutEnabled).IsModified = false;
// same for rest of core identity properties
_context.SaveChanges();
}
I'm trying to do this based on this pattern, so that the db is only hit once (and overall looks like an optimal way), however there seems to be some Identity behaviour preventing this. After Attach the entry is just replacing the original user instead of modifying (so e.g. UserName ends up being null, which is naturally required).
Using asp.net mvc5, my user management systems seems to work. I can login with google or with name/password..
but now I am working on a user management interface in which I need to be able to delete existing users. And this is starting to expose to me just how confusing the user management system is. There's so many different ways to deal with users.. and some of them don't work.
Most everywhere I read, it is talking about using the Membership.DeleteUser().
But that isn't working...
The users were created with.
var user = new ApplicationUser()
{
UserName = model.UserName,
Email = model.Email,
ConfirmationToken = confirmationToken,
IsConfirmed = false
};
var result = await UserManager.CreateAsync(user, model.Password);
Now later on.. how do I delete such a user? (given its name or userid)
I have tried what comes up most on various searches.. comes up with Membership as the solution. But this surely isn't right for MVC5?
For example
var allusers = Membership.GetAllUsers(); // allusers is empty
bool success = Membership.DeleteUser(model.name); // <-- success = false
I can get all the users using this method..
ApplicationDbContext db = new ApplicationDbContext();
foreach (var user in db.Users) { ... }
And I can find an individual user with..
ApplicationDbContext db = new ApplicationDbContext();
var um = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(db));
ApplicationUser user = um.FindById(model.userId);
Now how do I delete one though? ....
Update
As of Microsoft.AspNet.Identity Version 2.0.0.0, you can now delete users with Identity using UserManager.Delete(user);.
For Posterity
You are referring to two different things, Identity and Membership. Newer versions of ASP.NET support Identity and Membership with Identity being the default, while older versions support only Membership (out of those two authentication systems).
When you create a user with UserManager.CreateAsync, you are doing so within the Microsoft.AspNet.Identity namespace. When you are attempting to delete a user with Membership.DeleteUser, you are doing so within the System.Web.Security namespace. They are living in two different worlds.
As another comment mentions, deleting users is not yet supported out of the box by Identity, but it is the first item on their roadmap for a Spring of 2014 release.
But why wait? Add another property to the ApplicationUser model like this:
public class ApplicationUser : IdentityUser
{
public string IsActive { get; set; }
}
Then, in your controller for deleting a user:
user.IsActive = false;
Do a check when the user logs in:
if (user.IsActive == false)
{
ModelState.AddModelError(String.Empty, "That user has been deleted.");
return View(model);
}
When an deleted user attempts to re-register, instead of UserManager.Create, use UserManager.Update with their new information on the registration page.
These steps will effectively delete the user. If you truly must clear their information from your database, you can use Entity Framework to do that more directly.
added to the previous response. If you have
public class ApplicationUser : IdentityUser
{
public string IsActive { get; set; }
}
Then, in your controller for deleting a user:
user.IsActive = false.ToString();
because your data type is a string and n ot a boolean