EF Core2.0 dbvalidation errors not displaying all errors - asp.net-core

I've Created an EF core 2.0 application and trying to validate the model on Savechanges but its only returning the first validation error.
Here are my Dbcontext and controller
public partial class ProductWarehouseContext : DbContext
{ public List<string> ErrorList=new List<string>();
public ProductWarehouseContext(DbContextOptions<ProductWarehouseContext> options)
: base(options)
{
}
public virtual DbSet<Customer> Customer { get; set; }
public virtual DbSet<Order> Order { get; set; }
public virtual DbSet<OrderItem> OrderItem { get; set; }
public virtual DbSet<Product> Product { get; set; }
public virtual DbSet<Supplier> Supplier { get; set; }
public override int SaveChanges()
{
var entities = from e in ChangeTracker.Entries()
where e.State == EntityState.Added
|| e.State == EntityState.Modified
select e.Entity;
foreach (var entity in entities)
{
var validationContext = new ValidationContext(entity);
Validator.ValidateObject(
entity,
validationContext,
validateAllProperties: true);
}
return base.SaveChanges();}
}
Controller
[HttpPost]
public IActionResult Save([FromBody]CustomerViewModel customer)
{
using (var cont = _context.Database.BeginTransaction())
{
try
{
var cust = new Customer()
{
FirstName = customer.FirstName,
LastName = customer.LastName,
City = customer.City,
Country = customer.Country,
Phone = customer.Phone,
IsSubscribedforAlerts = customer.IsSubscribedforAlerts
};
_context.Customer.Add(cust);
_context.SaveChanges();
cont.Commit();
}
catch (Exception e)
{
Errors.Add(e.Message);
cont.Rollback();
foreach (var err in Errors)
{
ModelState.AddModelError("errors", err);
}
return Ok(ModelState);
}
}
return Ok();
}
Class
public partial class Customer
{
public Customer()
{
Order = new HashSet<Order>();
}
public int Id { get; set; }
[Required(ErrorMessage = "FirstName is required to save a new customer")]
public string FirstName { get; set; }
[Required(ErrorMessage = "LastName is required to save a new customer")]
public string LastName { get; set; }
public string City { get; set; }
public string Country { get; set; }
[Required(ErrorMessage = "PhoneNumber is required to save a new customer")]
public string Phone { get; set; }
public bool? IsSubscribedforAlerts { get; set; }
public ICollection<Order> Order { get; set; }
}
and error is only returnig ""firstname" is required and if I pass the firstname in request object then its returning "lastname" is required.
What should I do to return all the errors how we do it in EF6 using DbEntityValidationException ?

That's because ValidateObject() throws upon first encountering an error. Try using TryValidateObject() instead, and pass it a List<ValidationResult> that accumulate all errors.
Something like:
public class EntityValidationException : Exception
{
public EntityValidationException(IEnumerable<ValidationException> exceptions)
{
this.ValidationErrors = exceptions;
}
public IEnumerable<ValidationException> ValidationErrors { get; }
}
Then in your SaveChanges():
foreach (var entity in entities)
{
var validationContext = new ValidationContext(entity);
var validationResults = new List<ValidationResult>();
Validator.TryValidateObject(entity, validationContext, validationResults);
if (validationResults.Any())
throw new EntityValidationException(validationResults.Select(x => new ValidationException(x, null, null)));
}
Then in your controller/action, you can explicitly handle EntityValidationException:
catch (EntityValidationException validationException)
{
foreach (var err in validationException.ValidationErrors)
{
var validationResult = err.ValidationResult;
ModelState.AddModelError(validationResult.MemberNames.First(), validationResult.ErrorMessage);
}
}

Related

ASP.NET Core Web API - How to display users with respective role

I am implementing ASP.NET Identity in ASP.NET Core-6 Web API
Entities:
public class ApplicationUser : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public ICollection<ApplicationUserRole> UserRoles { get; set; }
}
public class ApplicationRole : IdentityRole
{
public ICollection<ApplicationUserRole> UserRoles { get; set; }
}
public class ApplicationUserRole : IdentityUserRole<string>
{
public virtual ApplicationUser User { get; set; }
public virtual ApplicationRole Role { get; set; }
}
Then I have this DTOs:
public class AllRoleListDto
{
public string Id { get; set; }
public string Name { get; set; }
}
public class AllUserListDto
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
public string Fullname
{
get { return FirstName + " " + LastName; }
}
}
I did the Mapping using AutoMapper as shown below:
public class AdminMapperProfile: Profile
{
public AdminMapperProfile()
{
CreateMap<ApplicationUser, AllUserListDto>().ReverseMap();
CreateMap<ApplicationRole, AllRoleListDto>().ReverseMap();
}
}
I want to display all the users with there respective roles using:
public async Task<PagedResponse<AllUserListDto>> GetAllUserAsync(int page, int limit)
{
var response = new PagedResponse<AllUserListDto>();
try
{
if (page >= 1 && limit >= 1)
{
var userQueryable = _context.ApplicationUsers.AsQueryable();
var pagedUsers = await userQueryable.ToPagedListAsync(page, limit);
response.Result = _mapper.Map<List<AllUserListDto>>(pagedUsers.ToList());
response.TotalPages = pagedUsers.PageCount;
response.Page = pagedUsers.PageNumber;
response.PerPage = pagedUsers.PageSize;
}
else
{
response.Error = new ErrorResponseDto()
{
ErrorCode = 400,
Message = "The page number and page size must be greater than 1!"
};
}
}
catch (Exception ex)
{
response.Error = new ErrorResponseDto()
{
ErrorCode = 500,
Message = ex.Message
};
}
return response;
}
What I have above only get all the users without the roles.
How do I get all the users with their respective roles as shown below?
UserName | FirstName | LastName | Role
Thanks
You can use _userManager.GetRolesAsync(user) to get the respective roles, here is a simple demo( I have used MVC here to demonstrate it more clearly):
First, Create a viewModel
public class UserRole
{
public string UserName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public List<string> roles { get; set; } = new List<string>();
}
Then in controller:
public class RegisterController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly RoleManager<ApplicationRole> _roleManager;
public RegisterController(UserManager<ApplicationUser> userManager, RoleManager<ApplicationRole> roleManager)
{
_userManager = userManager;
_roleManager = roleManager;
}
public async Task<IActionResult> show()
{
List<UserRole> userRoles = new List<UserRole>();
//get all users
var user = _userManager.Users.ToList();
foreach (var item in user)
{
UserRole userRole = new UserRole();
userRole.UserName = item.UserName;
userRole.FirstName = item.FirstName;
userRole.LastName = item.LastName;
userRole.Email = item.Email;
//get the user's roles
var roles = await _userManager.GetRolesAsync(item);
foreach (var roleName in roles)
{
userRole.roles.Add(roleName);
}
userRoles.Add(userRole);
}
return View(userRoles);
}
}
View:
#model List<UserRole>
#foreach (var item in Model)
{
<h2>#item.UserName</h2>
<h2>#item.FirstName</h2>
<h2>#item.LastName</h2>
<h2>#item.Email</h2>
#foreach (var role in item.roles)
{
<h3>#role</h3>
}
<h1>==================================</h1>
}
Demo:

blazor server (blazorise datagrid) with foreign key column (complex object)

I'm using blazor (blazorise datagrid) with asp.net core and entity framework. I'm facing a problem while trying to perform crud operation on data with
foreign key column (complex object).
Here is my code
Model Classes
public partial class ApproverSequence
{
public int Id { get; set; }
public int? TransactionTypeId { get; set; }
public bool? IsStart { get; set; }
public bool? IsArchived { get; set; }
public virtual TransactionType TransactionType { get; set; }
}
public partial class TransactionType
{
public TransactionType()
{
ApproverSequences = new HashSet<ApproverSequence>();
}
public int Id { get; set; }
public string Description { get; set; }
public bool? IsArchived { get; set; }
public virtual ICollection<ApproverSequence> ApproverSequences { get; set; }
}
Here is my data grid column
<DataGridSelectColumn TItem="ApproverSequence" Field="#nameof(ApproverSequence.TransactionType)" Caption="Description" Editable="true">
<DisplayTemplate>
#{
var transactionTypeDesc = (context as ApproverSequence).TransactionType?.Description;
#transactionTypeDesc
}
</DisplayTemplate>
<EditTemplate>
<Select TValue="int"
SelectedValue="#((int)((TransactionType)(((CellEditContext)context).CellValue)).Id)"
SelectedValueChanged="#( v => ((CellEditContext)context).CellValue = transactionTypes.First(x=> x.Id == v))">
#foreach (var item in transactionTypes)
{
<SelectItem TValue="int" Value="#(item.Id)">#item.Description</SelectItem>
}
</Select>
</EditTemplate>
</DataGridSelectColumn>
#code{
private List<ApproverSequence> approverSequences;
private List<TransactionType> transactionTypes;
protected override async Task OnInitializedAsync()
{
approverSequences = await ApproverSequenceService.GetAll();
transactionTypes = await TransactionTypeService.GetAll();
}
}
Services
public async Task<List<ApproverSequence>> GetAll()
{
try
{
return await _context.ApproverSequences.Include(x=>x.TransactionType).Where(x => x.IsArchived == false).ToListAsync();
}
catch
{
throw;
}
}
public async Task<List<TransactionType>> GetAll()
{
try
{
return await _context.TransactionTypes.Where(x => x.IsArchived == false).ToListAsync();
}
catch
{
throw;
}
}
while running this project it throws System.NullReferenceException: 'Object reference not set to an instance of an object.' exception.
Is there anything I have missed? Thanks in advance

Can i add a parent record and a child record using the same _context.SaveChangesAsync()

I have the following 2 Parent/Child objects:-
public Submission()
{
SubmissionQuestionSubmission = new HashSet<SubmissionQuestionSubmission>();
}
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Npi { get; set; }
public bool Independent { get; set; }
public string Comment { get; set; }
public virtual ICollection<SubmissionQuestionSubmission> SubmissionQuestionSubmission { get; set; }
}
public partial class SubmissionQuestionSubmission
{
public int SubmissionQuestionId { get; set; }
public int SubmissionId { get; set; }
public string Answer { get; set; }
public virtual Submission Submission { get; set; }
}
and i created the following view model:-
public class SubmissionCreate
{
public Submission Submission {set; get;}
public IList<SubmissionQuestion> SubmissionQuestion { set; get; }
public IList<SubmissionQuestionSubmission> SubmissionQuestionSubmission { set; get; }
}
where i have the following action method to add a parent record (submission) and a child record (SubmissionQuestionSubmission ), but to do so, i have to issue 2 save requests to the database, one to save the parent and get its ID, while the other to save the child record and assign it the parent ID, as follow:-
public async Task<IActionResult> Create(SubmissionCreate sc)//Bind("Id,FirstName,LastName,Npi,Independent,Comment")]
{
if (ModelState.IsValid)
{
var newsubmission = _context.Submission.Add(sc.Submission);
await _context.SaveChangesAsync();
foreach (var v in sc.SubmissionQuestionSubmission)
{
v.SubmissionId = sc.Submission.Id;
_context.SubmissionQuestionSubmission.Add(v);
}
await _context.SaveChangesAsync();
TempData["message"] = "Thank You.. Your request has been submitted...";
return View("Confirmation");
}
return View(sc);
}
so my question is if i can do the above job, using one save statement instead of 2?
You don't need use two SaveChanges. You can assign newsubmission into Submission property instead of v.SubmissionId = sc.Submission.Id;.
In this case Id and ForeignKey created automatically by EF Core
var newsubmission = _context.Submission.Add(sc.Submission);
foreach (var v in sc.SubmissionQuestionSubmission)
{
v.Submission = newsubmission;
_context.SubmissionQuestionSubmission.Add(v);
}
await _context.SaveChangesAsync();
Another way
sc.Submission.SubmissionQuestionSubmission = new List<SubmissionQuestionSubmission>();
foreach (var v in sc.SubmissionQuestionSubmission)
{
sc.Submission.SubmissionQuestionSubmission.Add(v)
}
_context.Submission.Add(sc.Submission);
await _context.SaveChangesAsync();

How to avoid saving a value of property of form object when saving changes to db

In a crud asp.net core 2.2 web app, I need to avoid saving a property of form object to db. How do I do that?
I've tried using [Editable(false)] data annotation on the ListBin property to prevent saving property value to db.
[Table("supply_lists")]
public partial class SupplyLists
{
[Column("id")]
public int Id { get; set; }
[Column("category_id")]
public int CategoryId { get; set; }
[Required]
[Column("coursecode")]
[StringLength(200)]
public string Coursecode { get; set; }
[Required]
[Column("title")]
[StringLength(200)]
public string Title { get; set; }
[Required]
[Column("filename")]
[StringLength(200)]
public string Filename { get; set; }
[Column("isactive")]
public bool Isactive { get; set; }
[Column("date", TypeName = "smalldatetime")]
public DateTime Date { get; set; }
[Column("list_bin")]
public byte[] ListBin { get; set; }
[ForeignKey("CategoryId")]
[InverseProperty("SupplyLists")]
public virtual SupplyListCategory Category { get; set; }
}
[ModelMetadataType(typeof(MetaDataTypeModel))]
public partial class SupplyLists
{
}
public class MetaDataTypeModel
{
[Editable(false)]
public byte[] ListBin { get; set; }
[Display(Name = "Is Active")]
public bool Isactive { get; set; }
[Display(Name ="Course Code")]
public string Coursecode { get; set; }
[Display(Name = "Category")]
public int CategoryId { get; set; }
[DataType(DataType.Date)]
public DateTime Date { get; set; }
}
public class EditModel : PageModel
{
private readonly SupplyListCore22.Models.SupplyListsContext _context;
private readonly IHostingEnvironment _env;
public EditModel(SupplyListCore22.Models.SupplyListsContext context, IHostingEnvironment env)
{
_context = context;
_env = env;
}
[BindProperty]
public SupplyLists SupplyLists { get; set; }
[BindProperty]
public FileUpload FileUpload { get; set; }
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
}
SupplyLists = await _context.SupplyLists
.Include(s => s.Category).FirstOrDefaultAsync(m => m.Id == id);
if (SupplyLists == null)
{
return NotFound();
}
ViewData["CategoryId"] = new SelectList(_context.SupplyListCategory, "Id", "Category");
return Page();
}
public async Task<IActionResult> OnPostAsync()
{
//if (!ModelState.IsValid)
//{
// return Page();
//}
_context.Attach(SupplyLists).State = EntityState.Modified;
await _context.SaveChangesAsync();
if (FileUpload.UploadSupplyList != null)
{
var fileUploadData = await utilities.utilities.ProcessFormFile(FileUpload.UploadSupplyList, ModelState);
if (ModelState.ErrorCount > 0)
{
ViewData["CategoryId"] = new SelectList(_context.SupplyListCategory, "Id", "Category");
return Page();
}
var sl = _context.SupplyLists.Find(SupplyLists.Id);
sl.ListBin = fileUploadData;
await _context.SaveChangesAsync();
}
return RedirectToPage("./Index");
}
It set the ListBin to null in db which is not what I wanted when saving changes (I wanted to preserve the old value of ListBin in db).

Entity Framework Core no duplicate entries with ErrorMessage

What is the best way to avoid duplicate entries with MVC-EFC and ErrorMessage return
Model Test.cs
public class Test
{
public int Id { get; set; }
[Required(ErrorMessage = "Select a Name")]
[StringLength(50, ErrorMessage = "Max 50 character")]
public string Name { get; set; }
[StringLength(100, ErrorMessage = "Max 100 character")]
public string Text { get; set; }
}
I use a ApiController with "ErrorMessage" return.
For column "Name" i do not want duplicates and a ErrorMessage return like "Entry already available!"
What is the best way?
Try to implement custom ValidationAttribute
Customer ValidationAttribute
public class UniqueValidation : ValidationAttribute
{
private readonly string _errorMessage;
public UniqueValidation(string ErrorMessage)
{
_errorMessage = ErrorMessage;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (validationContext != null && typeof(IName).IsAssignableFrom(validationContext.ObjectType))
{
ApplicationDbContext db = validationContext.GetService(typeof(ApplicationDbContext)) as ApplicationDbContext;
IQueryable<IName> result = db.GetType().GetMethod("Set").MakeGenericMethod(validationContext.ObjectType).Invoke(db, null) as IQueryable<IName>;
var v = result.FirstOrDefault(u => u.Name == ((IName)validationContext.ObjectInstance).Name);
if (v != null)
{
return new ValidationResult(_errorMessage);
}
}
return ValidationResult.Success;
}
}
Use
public class File: IName
{
public int Id { get; set; }
[UniqueValidation("FileName is exist")]
public string Name { get; set; }
[InverseProperty("Avatar")]
public ICollection<ApplicationUser> Users { get; set; }
}
public interface IName
{
string Name { get; set; }
}