public async Task<IActionResult> LeaveForm([Bind("ReqNo,ReqId,ActionTp,EmpNo,Remark,ActionDt,Ndays,ReqDt,Status,CreatedTm,CreatedBy,DeptId,ActionTaken,ActionRemark")] Request requests, Noti noti)
{
if (ModelState.IsValid)
{
try
{
var empno = _context.Users.Where(x => x.UserName == User.Identity.Name).Select(m => m.EmpNo).Single();
requests.ReqId = Convert.ToByte(1);
requests.ActionTp = 1;
requests.Status = "Pending";
requests.CreatedTm = DateTime.Now;
_context.Add(requests);
// Notification
var admin = _context.Users.Where(x => x.UserTp == 1).Select(x => x.EmpNo).ToListAsync();
noti.CreatedDate = DateTime.Now;
noti.FromUserId = empno;
noti.ToUserId = admin;
noti.NotiHeader = "Requested";
noti.NotiBody = "LeaveForm";
noti.IsRead = false;
noti.Url = "../HandleRequest/index";
_context.Add(noti);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
catch (Exception)
{
Notify("Something went wrong", notificationType: NotificationType.error);
return RedirectToAction("index", "Home");
throw;
}
}
Notify("Something went wrong", notificationType: NotificationType.error);
return View(requests);
}
here var admin is a list of items
And I need to add that list of items to which I had mentioned
Here my need is to sent the same request to different users in the list at a time
Related
In my ASP.NET Core-6 Web API Entity Framework Core, I have this code for user Registration:
public async Task<Response<string>> RegisterUserAsync(string userId)
{
var response = new Response<string>();
using (var transaction = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
try
{
var userDetail = _dataAccess.GetSaffUserProfile(userId);
adminAccess = _config.GetSection("DDMAdminCredentials").GetValue<string>("adminaccess");
if (string.IsNullOrEmpty(userDetail.user_logon_name))
{
_logger.Error("Failed Authentication");
transaction.Dispose();
response.Successful = false;
response.StatusCode = (int)HttpStatusCode.BadRequest;
response.Message = "Staff Record Not Found!";
return response;
}
var user = new ApplicationUser()
{
UserName = userDetail.user_logon_name.ToLower().Trim(),
Email = string.IsNullOrWhiteSpace(userDetail.email) ? null : userDetail.email.Trim().ToLower(),
FirstName = string.IsNullOrWhiteSpace(userDetail.firstname) ? null : userDetail.firstname.Trim(),
LastName = string.IsNullOrWhiteSpace(userDetail.lastname) ? null : userDetail.lastname.Trim(),
MobileNumber = string.IsNullOrWhiteSpace(userDetail.mobile_phone) ? null : userDetail.mobile_phone.Trim()
CreatedAt = DateTime.Now
};
var result = await _userManager.CreateAsync(user, adminAccess);
if (result.Succeeded)
{
// Insert User Role
await _userManager.AddToRoleAsync(user, userRole);
userBranchNo = userDetail.branch_code.TrimStart('0');
var bankBranchId = _dbContext.BankBranches.Where(u => u.BranchNumber.ToString() == userBranchNo.ToString()).Select(m => m.Id).FirstOrDefault();
var bankUser = new BankUser()
{
UserId = user.Id,
BankBranchId = bankBranchId,
CreatedBy = "System"
};
await _unitOfWork.AdminUsers.InsertAsync(bankUser);
await _unitOfWork.Save();
response.StatusCode = (int)HttpStatusCode.Created;
response.Successful = true;
response.Data = user.Id;
response.Message = "Bank User Created Successfully!";
transaction.Complete();
await _signInManager.SignInAsync(user, isPersistent: false);// user, true, false
return response;
}
response.Message = GetErrors(result);
response.StatusCode = (int)HttpStatusCode.BadRequest;
response.Successful = false;
transaction.Dispose();
return response;
}
catch (Exception ex)
{
response.Message = "An error occured :" + ex.Message;
response.Successful = false;
response.StatusCode = (int)HttpStatusCode.BadRequest;
return response;
}
};
}
The application should immediately log in as soon as the user is created.
However, I got this error in the log:
An Error occured The current TransactionScope is already complete.
How do I get this resolved?
Thanks
You complete the transaction scope and then SignInManager tries to use it. You should complete the scope after signing user in:
await _signInManager.SignInAsync(user, isPersistent: false);
transaction.Complete();
What is also very important is the maintainability of your code - it's a single method, long and imperative. Split it to improve readability. You can have small top-level method which just gets required for registration data and passes it further:
public async Task<Response<string>> RegisterUserAsync(string userId)
{
try
{
var userProfile = GetStaffUserProfile(userId);
if (userProfile == null)
return BadRequestResponse("Staff Record Not Found!");
var user = _mapper.Map<ApplicationUser>(userProfile);
return await RegisterUserAsync(user, userProfile.GetBranchNo());
}
catch(Exception ex)
{
return BadRequestResponse(ex);
}
}
A few things were extracted here. Getting user profile:
private UserDetail? GetStaffUserProfile(string userId)
{
var userDetail = _dataAccess.GetSaffUserProfile(userId);
if (!userDetail.IsAuthenticated())
{
_logger.Error("Failed Authentication");
return null;
}
return userDetail;
}
Mapping exceptions, strings, and IdentityResults to responses (Note that you should not wrap all exceptions into a bad request response, because not all exceptions will be caused by bad requests from clients. There could be also internal server errors):
private Response<string> BadRequestResponse(IdentityResult result) =>
BadRequestResponse(GetErrors(result)); // create this on your own
private Response<string> BadRequestResponse(Exception ex) =>
new() {
Message = "An error occured :" + ex.Message,
Successful = false,
StatusCode = (int)HttpStatusCode.BadRequest
};
Declarative access to user details data:
public static class UserDetailExtensions
{
public static bool IsAuthenticated(UserDetail userDetail) =>
!String.IsNullOrEmpty(userDetail.user_logon_name);
public static string GetBranchNo(this UserDetail userDetail) =>
userDetail.branch_code.TrimStart('0');
}
Mapping user details to ApplicationUser is done by something like AutoMapper but you can extract method for manual mapping. And now you have a relatively small method for registering user:
private async Task<Response<string>> RegisterUserAsync(
ApplicationUser user, string userBranchNo)
{
using (var transaction = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
var result = await _userManager.CreateAsync(user, adminAccessPassword);
if (!result.Succeeded)
return BadRequestResponse(result);
await _userManager.AddToRoleAsync(user, userRole);
await CreateBankUserAsync(user.Id, userBranchNo);
await _signInManager.SignInAsync(user, isPersistent: false);
transaction.Complete();
}
return new() {
StatusCode = (int)HttpStatusCode.Created,
Successful = true,
Data = user.Id,
Message = "Bank User Created Successfully!"
};
}
With a small helper method:
private async Task CreateBankUserAsync(string userId, string userBranchNo)
{
var bankBranchId = _dbContext.BankBranches
.Where(b => b.BranchNumber.ToString() == userBranchNo)
.Select(b => b.Id)
.FirstOrDefault();
var bankUser = new BankUser() {
UserId = userId,
BankBranchId = bankBranchId,
CreatedBy = "System"
};
await _unitOfWork.AdminUsers.InsertAsync(bankUser);
await _unitOfWork.Save();
}
Config reading of adminAccessPassword should be moved to the application startup logic.
I am trying to get the point and solve problem with SelectListItem. As Admin I want to be able to assign Client to register User
public IActionResult Edit(string Id)
{
var user = _db.ApplicationUsers.FirstOrDefault(u => u.Id == Id);
if (user == null)
{
return NotFound();
}
var userRole = _db.UserRoles.ToList();
var roles = _db.Roles.ToList();
var client = _db.Clients.ToList();
var role = userRole.FirstOrDefault(u => u.UserId == user.Id);
if (role != null)
{
user.Role = roles.FirstOrDefault(u => u.Id == role.RoleId).Id;
}
if(client != null)
{
}
user.RoleList = _db.Roles.Select(u => new Microsoft.AspNetCore.Mvc.Rendering.SelectListItem
{
Text = u.Name,
Value = u.Id
});
user.ClientList = _db.Clients.Select(u => new Microsoft.AspNetCore.Mvc.Rendering.SelectListItem
{
Text = u.Name,
Value = u.Id.ToString()
});
return View(user);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(ApplicationUser user)
{
if (ModelState.IsValid)
{
var objFromDb = _db.ApplicationUsers.FirstOrDefault(u => u.Id == user.Id);
if (objFromDb == null)
{
return NotFound();
}
var userRole = _db.UserRoles.FirstOrDefault(u => u.UserId == objFromDb.Id);
if (userRole != null)
{
var previousRoleName = _db.Roles.Where(u => u.Id == userRole.RoleId).Select(e => e.Name).FirstOrDefault();
await _userManager.RemoveFromRoleAsync(objFromDb, previousRoleName);
}
await _userManager.AddToRoleAsync(objFromDb, _db.Roles.FirstOrDefault(u => u.Id == user.Role).Name);
objFromDb.Name = user.Name;
objFromDb.Email = user.Email;
objFromDb.PhoneNumber = user.PhoneNumber;
objFromDb.StreetAddress = user.StreetAddress;
objFromDb.City = user.City;
objFromDb.PostalCode = user.PostalCode;
objFromDb.RoleList = user.RoleList;
objFromDb.ClientList = user.ClientList;
_db.SaveChanges();
return RedirectToAction(nameof(Index));
}
user.RoleList = _db.Roles.Select(u => new Microsoft.AspNetCore.Mvc.Rendering.SelectListItem
{
Text = u.Name,
Value = u.Id
});
user.ClientList = _db.Clients.Select(u => new Microsoft.AspNetCore.Mvc.Rendering.SelectListItem
{
Text = u.Name,
Value = u.Id.ToString()
});
return View(user);
}
Somehow everything works fine only I can not change ClientList and I try to debug application but I can not see anything wrong.
I would be very thankfull If someone could check code and tell me where I made mistake ? What is wrong with this code, why my ClientList cannot be Edited
The error message tell me that ModelState is false and I notice error here
objFromDb.ClientList = user.ClientList;
And I change to
objFromDb.ClientId= user.ClientId;
And now it works ! :)
In Chrome i get following error:
Not allowed to load local resource: C://...
Now i want to change the return from the Image in like 'localhost:44346/wwwroot/images/profileimages/img.jpg'
Can you tell my how i can do this?
This is my Controller for the Fileupload:
[HttpPut]
[Authorize]
[RequestSizeLimit(1_000_000)]
[Route("UpdateImage")]
public async Task<IActionResult> UpdateImage([FromForm]ApplicationUserModel model)
{
try
{
var user = await _userManager.FindByIdAsync(model.id);
if (user.ProfileImagePath != null)
{
var file = new FileInfo(user.ProfileImagePath);
file.Delete();
}
var uniqueFileName = GetUniqueFileName(model.ProfileImage.FileName);
var uploads = Path.Combine(hostingEnvironment.WebRootPath, "Images\\ProfileImages");
var filePath = Path.Combine(uploads, uniqueFileName);
await model.ProfileImage.CopyToAsync(new FileStream(filePath, FileMode.Create));
user.ProfileImagePath = filePath;
var result = await _userManager.UpdateAsync(user);
return Ok(result);
}
catch (Exception ex)
{
throw ex;
}
}
This is the Controller for getting the User Informations:
[HttpGet]
[Authorize]
[Route("GetCurrentUser")]
public async Task<IActionResult> GetCurrentUser()
{
try
{
var userId = User.FindFirst("UserID")?.Value;
var data = await _userManager.FindByIdAsync(userId);
var user = new UserStandardModel
{
id = userId,
LastName = data.LastName,
FirstName = data.FirstName,
ProfileImagePath = data.ProfileImagePath
};
return Ok(user);
}
catch (Exception ex)
{
throw ex;
}
}
So i have found a solution for my problem.
When i trigger my GetCurrentUser function than i will check i the FilePath starts with 'C'.
When this is true i will format the filepath with my localhost address. Like Bruno suggested.
But this works only if i have the UseStaticFiles() in my Startup.cs
app.UseStaticFiles();
This is not beautiful but it does the work and its only for testing.
var userId = User.FindFirst("UserID")?.Value;
var data = await _userManager.FindByIdAsync(userId);
var user = new UserStandardModel
{
id = userId,
LastName = data.LastName,
FirstName = data.FirstName,
ProfileImagePath = data.ProfileImagePath
};
if (user.ProfileImagePath.StartsWith('C'))
{
var url = "https://localhost:44356/";
user.ProfileImagePath = user.ProfileImagePath.Remove(0,58);
user.ProfileImagePath = url + user.ProfileImagePath;
}
return Ok(user);
So I'm currently building a Web Api with .NET, and using async calls with entity framework.
On my PUT endpoint for a controller, I'm trying to get whether the user already belongs to another level, or if he's in the DB at all, here's the controller code:
[HttpPut]
public async Task<IHttpActionResult> PutCommittee(CommitteeViewModel committee)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (!User.IsInRole("Dean") && !User.IsInRole("Chair"))
return Unauthorized();
var user = await db.Users.Where(u => u.Cn == User.Identity.Name).FirstOrDefaultAsync();
if (user == null) { return BadRequest("Your user does not exist"); }
if (User.IsInRole("Dean"))
{
var college = await db.Colleges.Where(c => c.Dean.Cn == user.Cn).FirstOrDefaultAsync();
if (college == null) { return BadRequest("You're not a Dean of any college"); }
committee.Name = _utils.getCollegeCommitteeName(college);
}
else
{
var department = await db.Departments.Where(d => d.Chair.Cn == user.Cn).FirstOrDefaultAsync();
if (department == null) { return BadRequest("You're not a Chair of any college"); }
committee.Name = _utils.getDepartmentCommitteeName(department);
}
var model = await db.Commitees.Where(c => c.Name == committee.Name).FirstOrDefaultAsync();
if (model == null)
return BadRequest("You have no committee");
var tuple = await getUsers(committee);
model.Users = tuple.Item1;
if (model.Users == null)
return BadRequest(tuple.Item2);
db.Entry(model).State = EntityState.Modified;
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
throw;
}
return StatusCode(HttpStatusCode.NoContent);
}
And Here's the function that checks for the users:
private async Task<Tuple<List<User>, string>> getUsers(CommitteeViewModel committee)
{
string error = "";
List<User> users = new List<User>();
var tuple = new Tuple<List<User>, string>(users, error);
var role = await db.Roles.Where(r => r.Name == "Committee").FirstOrDefaultAsync();
foreach (UserViewModel u in committee.Users)
{
var user = await db.Users.Where(us => us.Cn == u.Cn).FirstOrDefaultAsync();
if (user != null)
{
if (user.Role.Name == "Chair" || user.Role.Name == "Dean")
{
error = "User " + user.Name + " is a " + user.Role.Name + " and cannot be member of a review committee";
return tuple;
}
users.Add(user);
}
else
{
user = _loginProvider.generateUser(u.Cn, role);
db.Users.Add(user);
await db.SaveChangesAsync();
users.Add(user);
}
}
return tuple;
}
I'm using a tuple since async method don't support OUT parameters, in case there's an error.
So the problem is that when I delete a user in my front-end (and then send a put request with the updated array), and I debug step by step, it does delete it, but when I don't, if I put a breakpoint at the try block, the variable model.Users contains the previous array (the original from the model), and this only happens when I delete a user from an array, and the weird thing is that it also happened when I wrote the code synchronously
Thank you for your help.
Just remember... Async methods running back server, so if you have proccess that need run first you need to replace async method for a simple one. Trying it and I´m sure you can solve it.
For Update campaign I am using this Code
public async Task<List<long?>> updateCampaign(Campaign campaign,string status)
{
try
{
campaign.Status = (CampaignStatus)(int)Enum.Parse(typeof(CampaignStatus), status);
var request = new UpdateCampaignsRequest
{
Campaigns = new Campaign[] { campaign },
CustomerId = "xxxxxx",
UserName = "something#outlook.com",
Password = "something#123",
ApplicationToken = "myApplicationToken",
CustomerAccountId = "123456",
DeveloperToken = "1234567890"
};
CampaignService = new ServiceClient<ICampaignManagementService>(_authorizationData);
CampaignService.RefreshOAuthTokensAutomatically = false;
var result = (await CampaignService.CallAsync((s, r) => s.UpdateCampaignsAsync(r), request));
if (result.TrackingId != null)
{
return result.CampaignIds.ToList();
}
else
{
return new List<long?>();
}
}
catch (Exception ex)
{
ErrorLog.log(ex);
return new List<long?>();
}
}
When I run this code, I got this error "Invalid client data. Check the SOAP fault details for more information"
thanks.
For updating the Campaign we can use "BulkServiceManager" for bulk updating of the campaign,you can use this service single campaign update also.
public async Task<List<long?>> updateCampaign(List<Campaign> campaigns)
{
try
{
var listBulkCampaign = new List<BulkCampaign>();
foreach (var campaign in campaigns)
{
var _bulkCampaign = new BulkCampaign()
{
Campaign = campaign
};
listBulkCampaign.Add(_bulkCampaign);
}
BulkServiceManager bulkServiceManager = new BulkServiceManager(_authorizationData);
string fileName = bingCampaignUpdate.csv;
var campaigns = (await bulkServiceManager.UploadEntitiesAsync(new EntityUploadParameters
{
Entities = listBulkCampaign,
OverwriteResultFile = true,
ResultFileDirectory = FileDirectory,
ResultFileName = fileName,
ResponseMode = ResponseMode.ErrorsAndResults
})).OfType<BulkCampaign>().ToList();
return new List<long?>();
}
catch (Exception ex)
{
ErrorLog.log(ex);
return new List<long?>();
}
}
You have to download .csv report and update the Campaigns.
I hope it helps you