.NET CORE 3.1, MVC Async method not updating DB - asp.net-mvc

AI am just moving to ASYNC methods and trying to get my data to update. I can select just find so I know the repository is working.
Action
[HttpPost]
public async Task<IActionResult> EditTeam(EmployeeVm empVm)
{
if (!ModelState.IsValid)
{
ModelState.AddModelError("", _errorUpdateMsg);
}
else
{
if (await _teamRepository.UpdateEmployee(empVm.Employee))
{
return RedirectToAction("Index");
}
ModelState.AddModelError("", _errorUpdateMsg);
}
return View(empVm);
}
My Constructor in repo
public TeamRepository(EnvisionDbContext envisionDbContext)
{
_envisonDbContext = envisionDbContext;
}
Here is my Update that does not save
public async Task<bool> UpdateEmployee(Employee employee)
{
var result = await _envisonDbContext.Employees.FirstOrDefaultAsync<Employee>(e => e.Id == employee.Id);
if (result != null)
{
result.FirstName = employee.FirstName;
result.LastName = employee.LastName;
result.Phone = employee.Phone;
result.IsActive = employee.IsActive;
await _envisonDbContext.SaveChangesAsync();
return true;
}
return false;
}
Thanks in advance for the help.
UPDATED: If I add this, it works. Is this because the two await calls are disconnected?
result.IsActive = employee.IsActive;
_envisonDbContext.Entry(result).State = EntityState.Modified;

Seems like you forgot update-method before savingchanges
if (result != null)
{
result.FirstName = employee.FirstName;
result.LastName = employee.LastName;
result.Phone = employee.Phone;
result.IsActive = employee.IsActive;
_envisionDbContext.Update(result); //paste it before you save changes
await _envisonDbContext.SaveChangesAsync();
return true;
}

Related

How can I return Id when model state cannot be work?

I am in "User Add" page. I have an Id which coming from "Visa Type" page.
When my page cannot be pair with model state, the Id doesn't exist in the URL. I need this Id. How can I return again?
Controller
[HttpGet]
public IActionResult AddUser(int visaTypeId)
{
ViewBag.Id = visaTypeId;
Guid g = Guid.NewGuid();
ViewBag.UniqueUser = g;
return View();
}
[HttpPost]
public async Task<IActionResult> AddUser(UserModel userModel, string uniqueUser, int visaTypeId)
{
if (ModelState.IsValid)
{
var result = await _client.PostAsJsonAsync<UserModel>("users/adduser", userModel);
var response = await result.Content.ReadAsStringAsync();
var responseModel = JsonConvert.DeserializeObject<ResponseModel>(response);
if (responseModel.Success == false)
{
//ViewBag.Message = responseModel.Message;
ModelState.AddModelError("", responseModel.Message);
}
if (result.IsSuccessStatusCode)
{
return RedirectToAction("AddApplicant", "Applicant", new { uniqueUser = uniqueUser });
}
}
return View();
}
You can pass back whatever you obtain from your view.
[HttpPost]
public async Task<IActionResult> AddUser(UserModel userModel, string uniqueUser, int visaTypeId)
{
if (ModelState.IsValid)
{
var result = await _client.PostAsJsonAsync<UserModel>("users/adduser", userModel);
var response = await result.Content.ReadAsStringAsync();
var responseModel = JsonConvert.DeserializeObject<ResponseModel>(response);
if (responseModel.Success == false)
{
//ViewBag.Message = responseModel.Message;
ModelState.AddModelError("", responseModel.Message);
}
if (result.IsSuccessStatusCode)
{
return RedirectToAction("AddApplicant", "Applicant", new { uniqueUser = uniqueUser });
}
}
ViewBag.VisaTypeId = VisaTypeId;
ViewBag.uniqueUser = uniqueUser;
return View(userModel);
}

ExecuteSqlRawAsync returns -1 in Entity Framework 3.1

In my ASP.NET Core 3.1 web application, I am mostly using stored procedures. When using ExecuteSqlRawAsync in Entity Framework Core it always returns -1. Below given is my generalized method to execute stored procedures.
public async Task<int> ExecuteSqlNonQuery(string StoredProcName, params object[] parameters)
{
int iTotalRecordsAffected = 0;
List<TEntity> listOfObject = null;
try
{
if (!string.IsNullOrEmpty(StoredProcName))
{
StringBuilder sbStoredProc = new StringBuilder();
sbStoredProc.Append("Exec ");
sbStoredProc.Append(StoredProcName);
if (parameters != null)
{
foreach (SqlParameter item in parameters)
{
if (listOfObject == null)
{
sbStoredProc.Append(" #");
listOfObject = new List<TEntity>();
}
else
{
sbStoredProc.Append(", #");
}
sbStoredProc.Append(item.ParameterName.Replace("#", ""));
if (item.Direction == System.Data.ParameterDirection.Output)
{
sbStoredProc.Append(" OUT");
}
}
}
iTotalRecordsAffected = await _DBContext.Database.ExecuteSqlRawAsync(sbStoredProc.ToString(), parameters);
}
}
catch (Exception ex)
{
}
finally
{
if (_DBContext.Database.GetDbConnection().State == System.Data.ConnectionState.Open)
{
_DBContext.Database.GetDbConnection().Close();
}
}
return iTotalRecordsAffected;
}
Here is my controller method that calls a SP to update data.
public async Task<int> UpdateCustomerData(EditCustomerDetail editCustomerDetail)
{
int iTotalRecordsEffected = 0;
try
{
List<SqlParameter> sqlParamList = new List<SqlParameter>()
{
new SqlParameter("#CustomerID",editCustomerDetail.CorporateID),
new SqlParameter("#CustomerName",editCustomerDetail.CorporateName),
new SqlParameter("#CustomerAddress",editCustomerDetail.Address),
new SqlParameter("#City",editCustomerDetail.City),
new SqlParameter("#CountryID",editCustomerDetail.CountryID),
new SqlParameter("#StateID",editCustomerDetail.StateID),
new SqlParameter("#Description",editCustomerDetail.Description),
new SqlParameter("#Phone",editCustomerDetail.Phone),
new SqlParameter("#Fax",editCustomerDetail.Fax),
new SqlParameter("#ModifiedBy",editCustomerDetail.UserID)
};
iTotalRecordsEffected = await _unitOfWork.GetRepository<EditCustomerDetail>().ExecuteSqlNonQuery("UpdateCustomerDetails", sqlParamList.ToArray());
}
catch (Exception ex)
{
}
finally
{
}
return iTotalRecordsEffected;
}
Any suggestion what I am doing wrong?

Web ApI: HttpClient is not invoking my web api action

See first how i design my web api action.
[System.Web.Http.RoutePrefix("api/Appointments")]
public class AppointmentsServiceController : ApiController
{
[System.Web.Http.HttpGet, System.Web.Http.Route("UserAppointments/{email}")]
public IHttpActionResult UserAppointments(string email)
{
if (!string.IsNullOrEmpty(email))
{
AppointmentsService _appservice = new AppointmentsService();
IEnumerable<Entities.Appointments> app = _appservice.GetUserWiseAppointments(email);
if (app.Count() <= 0)
{
return NotFound();
}
else
{
return Ok(app);
}
}
else
{
return BadRequest();
}
}
}
Now this way i am calling web api action from my asp.net mvc action by HttpClient.
public async Task<ActionResult> List()
{
var fullAddress = ConfigurationManager.AppSettings["baseAddress"] + "api/Appointments/UserAppointments/" + Session["useremail"];
IEnumerable<Entities.Appointments> app = null;
try
{
using (var client = new HttpClient())
{
using (var response = client.GetAsync(fullAddress).Result)
{
if (response.IsSuccessStatusCode)
{
var customerJsonString = await response.Content.ReadAsStringAsync();
app = JsonConvert.DeserializeObject<IEnumerable<Entities.Appointments>>(customerJsonString);
}
else
{
Console.WriteLine("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase);
var dict = JsonConvert.DeserializeObject<Dictionary<string, string>>(response.Content.ReadAsStringAsync().Result);
//MessageBox.Show(dict["Message"]);
}
}
}
}
catch (HttpRequestException ex)
{
// catch any exception here
}
return View();
}
}
i want capture the return IEnumerable and if not data return that also i have to capture. please show me the right direction.
Where i made the mistake. thanks
Mixing async with blocking calls like .Result
var response = client.GetAsync(fullAddress).Result
And
response.Content.ReadAsStringAsync().Result
can lead to deadlocks, which is possibly why it it not hitting your API.
Refactor the code to be async all the way.
That would mean updating the using to
var response = await client.GetAsync(fullAddress)
and the reading of the content in the else statement to
await response.Content.ReadAsStringAsync()
Reference Async/Await - Best Practices in Asynchronous Programming
It looks like you are not awaiting your GetAsync call, so in the following if (response.IsSuccessStatusCode) is probably always returning false. Try calling your method like this:
using (var response = (await client.GetAsync(fullAddress)).Result)
{

CRUD in MVC repository

I have created a repository data layer in my MVC web application, and want to use it for my CRUD methods. But I came to think of situations where I want to do something like:
If record does not exist
create record
else
update record
But how does this fit into CRUD? Is this two-in-one operation logic supposed to be kept in the controller?
I think the repository should take care of that, the controller should be as light as possible:
At repository level:
public bool CreateUpdate(Type model)
{
var record = db.FirstOrDefault(x=> x.Id == model.Id);
if(record == null)
{
Create(model);
}
else
{
Update(model);
}
}
public bool Create(Type model)
{
//create logic here
}
public bool Update(Type model)
{
//update logic here
}
This can be done with this code
var data = db.tableName.where(x=> x.Id == model.Id).FirstOrDefault();
if(data== null)
{
db.FAQCategories.Add(model);
db.SaveChanges();
}
else
{
db.Entry(model).State = EntityState.Modified;
db.SaveChanges();
}
public IActionResult Create()
{
return View();
}
// POST: AdminPanel/Students/Create
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(Student student)
{
if (!ModelState.IsValid)
{
return View();
}
student.Image = await student.Photo.SaveFileAsync(_environment.WebRootPath, "images");
await _context.Students.AddAsync(student);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
// GET: AdminPanel/Students/Edit/5
public async Task<IActionResult> Update(int? id)
{
if (id == null)
{
return BadRequest();
}
var student = await _context.Students.FindAsync(id);
if (student == null)
{
return NotFound();
}
return View(student);
}
// POST: AdminPanel/Students/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Update(int? id, Student newstudent)
{
if (id==null)
{
return BadRequest();
}
var oldstudent = _context.Students.Find(id);
if (oldstudent == null)
{
return NotFound();
}
if (!ModelState.IsValid)
{
return View();
}
var path = Helper.GetPath(_environment.WebRootPath, "images", oldstudent.Image);
if (System.IO.File.Exists(path))
{
System.IO.File.Delete(path);
}
newstudent.Image = await newstudent.Photo.SaveFileAsync(_env.WebRootPath, "images");
oldstudent.Image = newstudent.Image;
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
public async Task<IActionResult> Delete(int id)
{
if (id == null)
{
return BadRequest();
}
var student= _context.Students.Find(id);
if (student== null)
{
return NotFound();
}
_context.Students.Remove(student);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
}
}

ASP.NET MVC 5 how to delete a user and its related data in Identity 2.0

I'm following this article to delete a user in Identity 2.0
http://www.asp.net/mvc/tutorials/mvc-5/introduction/examining-the-details-and-delete-methods
However, I need to delete all related records in AspNetUserRoles first and then delete the user.
I found an example which is written in Identity 1.0 and some of methods used inside this example don't exist.
// POST: /Users/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<ActionResult> DeleteConfirmed(string id)
{
if (ModelState.IsValid)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var user = await context.Users.FindAsync(id);
var logins = user.Logins;
foreach (var login in logins)
{
context.UserLogins.Remove(login);
}
var rolesForUser = await IdentityManager.Roles.GetRolesForUserAsync(id, CancellationToken.None);
if (rolesForUser.Count() > 0)
{
foreach (var item in rolesForUser)
{
var result = await IdentityManager.Roles.RemoveUserFromRoleAsync(user.Id, item.Id, CancellationToken.None);
}
}
context.Users.Remove(user);
await context.SaveChangesAsync();
return RedirectToAction("Index");
}
else
{
return View();
}
}
I cannot find IdentityManager from anywhere, and context.Users doesn't have FindAsync() method either.
How can I properly delete a User and its related records in Identity 2.0?
I think the classes you're looking for are the UserManager and the RoleManager. In my opinion they are the better way instead of going against the context directly.
The UserManager defines a method RemoveFromRoleAsync which gives you the ability to remove the user (identified by his key) from a given role. It also defines several Find methods, such as FindAsync, FindByIdAsync, FindByNameAsync, or FindByEmailAsync. They all can be used to retrieve a user. To delete a user you should use the DeleteAsync method which accepts a user object as a parameter. To get the roles a user is member of Identity gives you the GetRolesAsync method where you pass in the ID of the user. Also I see that you're trying to remove a login from a user. For this purpose you should use the RemoveLoginAsync method.
All in all your code would look similar to the following one:
// POST: /Users/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<ActionResult> DeleteConfirmed(string id)
{
if (ModelState.IsValid)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var user = await _userManager.FindByIdAsync(id);
var logins = user.Logins;
var rolesForUser = await _userManager.GetRolesAsync(id);
using (var transaction = context.Database.BeginTransaction())
{
foreach (var login in logins.ToList())
{
await _userManager.RemoveLoginAsync(login.UserId, new UserLoginInfo(login.LoginProvider, login.ProviderKey));
}
if (rolesForUser.Count() > 0)
{
foreach (var item in rolesForUser.ToList())
{
// item should be the name of the role
var result = await _userManager.RemoveFromRoleAsync(user.Id, item);
}
}
await _userManager.DeleteAsync(user);
transaction.Commit();
}
return RedirectToAction("Index");
}
else
{
return View();
}
}
You'll need to adjust this snippet to your needs, because I don't have an idea how your IdentityUser implementation looks like. Remember to declare the UserManager as needed. An example how you could do this can be found when you create a new project in Visual Studio using Individual Accounts.
Update for ASP.NET Core 2.0 - hope this saves someone a bit of time
ApplicationDbContext context,
UserManager<ApplicationUser> userManager,
ApplicationUser user
var logins = await userManager.GetLoginsAsync(user);
var rolesForUser = await userManager.GetRolesAsync(user);
using (var transaction = context.Database.BeginTransaction())
{
IdentityResult result = IdentityResult.Success;
foreach (var login in logins)
{
result = await userManager.RemoveLoginAsync(user, login.LoginProvider, login.ProviderKey);
if (result != IdentityResult.Success)
break;
}
if (result == IdentityResult.Success)
{
foreach (var item in rolesForUser)
{
result = await userManager.RemoveFromRoleAsync(user, item);
if (result != IdentityResult.Success)
break;
}
}
if (result == IdentityResult.Success)
{
result = await userManager.DeleteAsync(user);
if (result == IdentityResult.Success)
transaction.Commit(); //only commit if user and all his logins/roles have been deleted
}
}
Brad's point about requiring #Html.AntiForgeryToken() in views is not necessary if you are using latest versions of ASP.NET - see AntiForgeryToken still required
Why not create a SQL trigger for AspNetUsers so deleting a user also deletes the corresponding records for user from AspNetUserRoles and AspNetUserLogins?
I need to invoke DeleteUser from a number of places so I added a static method to AccountController (see below). I'm still learning about MVC, so should be grateful for comments, in particular 1) use of IdentityResult as a return code 2) wisdom of extending AccountController in this way 3) approach for putting password (cleartext) into the Model to validate the action (see sample invocation).
public static async Task<IdentityResult> DeleteUserAccount(UserManager<ApplicationUser> userManager,
string userEmail, ApplicationDbContext context)
{
IdentityResult rc = new IdentityResult();
if ((userManager != null) && (userEmail != null) && (context != null) )
{
var user = await userManager.FindByEmailAsync(userEmail);
var logins = user.Logins;
var rolesForUser = await userManager.GetRolesAsync(user);
using (var transaction = context.Database.BeginTransaction())
{
foreach (var login in logins.ToList())
{
await userManager.RemoveLoginAsync(user, login.LoginProvider, login.ProviderKey);
}
if (rolesForUser.Count() > 0)
{
foreach (var item in rolesForUser.ToList())
{
// item should be the name of the role
var result = await userManager.RemoveFromRoleAsync(user, item);
}
}
rc = await userManager.DeleteAsync(user);
transaction.Commit();
}
}
return rc;
}
Sample invocation - form passes the user's password (cleartext) in Model:
// POST: /Manage/DeleteUser
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteUser(DeleteUserViewModel account)
{
var user = await GetCurrentUserAsync();
if ((user != null) && (user.PasswordHash != null) && (account != null) && (account.Password != null))
{
var hasher = new Microsoft.AspNetCore.Identity.PasswordHasher<ApplicationUser>();
if(hasher.VerifyHashedPassword(user,user.PasswordHash, account.Password) != PasswordVerificationResult.Failed)
{
IdentityResult rc = await AccountController.DeleteUserAccount( _userManager, user.Email, _Dbcontext);
if (rc.Succeeded)
{
await _signInManager.SignOutAsync();
_logger.LogInformation(4, "User logged out.");
return RedirectToAction(nameof(HomeController.Index), "Home");
}
}
}
return View(account);
}
I was looking also for the answer but finally this is what work well for me, even its old post but it may help for someone.
// GET: Users/Delete/5
public ActionResult Delete(string id)
{
using (SqlConnection sqlCon = new SqlConnection(connectionString))
{
sqlCon.Open();
string query = "DELETE FROM AspNetUsers WHERE Id = #Id";
SqlCommand sqlCmd = new SqlCommand(query, sqlCon);
sqlCmd.Parameters.AddWithValue("#Id", id);
sqlCmd.ExecuteNonQuery();
}
return RedirectToAction("Index");
}
// POST: Users/Delete/5
[HttpPost]
public ActionResult Delete(string id, FormCollection collection)
{
try
{
// TODO: Add delete logic here
return RedirectToAction("Index");
}
catch
{
return View();
}
}

Resources