MVC 5 Create Validation error but valid ModelState - c#

I am trying to create within MVC 5 and am getting a validation error even though the ModelState is coming back valid.
Error message
Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.
and when I look at the message, it shows....
The name 'e' does not exist in the current context
When I look at the POST data, the model that was created has all required fields filled in. I did notice that the model ID was assigned 0. I'm not sure if that is the error or if it is supposed to pass a zero for the ID.
What might the problem be?
WosController.cs
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Create([Bind(Include = "id,woNumber,woDescription,dueDate,qty,item_id,releaseDate,parentWO_id,wip_id")] Wo wo)
{
if (ModelState.IsValid)
{
db.Wos.Add(wo);
await db.SaveChangesAsync();
return RedirectToAction("Index");
}
return View(wo);
}
Wo.cs
public partial class Wo
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Wo()
{
this.WoParts = new HashSet<WoPart>();
this.WoStatuses = new HashSet<WoStatus>();
}
public int id { get; set; }
public string woNumber { get; set; }
public string woDescription { get; set; }
public Nullable<System.DateTime> dueDate { get; set; }
public string qty { get; set; }
public Nullable<int> item_id { get; set; }
public Nullable<System.DateTime> releaseDate { get; set; }
public string status { get; set; }
public Nullable<int> parentWO_id { get; set; }
public int wip_id { get; set; }
public Nullable<int> part_id { get; set; }
public virtual Item Item { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<WoPart> WoParts { get; set; }
public virtual Wo woParentWO { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<WoStatus> WoStatuses { get; set; }
public virtual Part Part { get; set; }
public virtual Wip Wip { get; set; }
}

Wrap your call to SaveChangesAsync in a try...catch like so:
try
{
await db.SaveChangesAsync();
}
catch (DbEntityValidationException e)
{
var errorMessages = e.EntityValidationErrors
.SelectMany(x => x.ValidationErrors)
.Select(x => x.ErrorMessage);
var fullErrorMessage = string.Join("; ", errorMessages);
var exceptionMessage = string.Concat(e.Message, " The validation errors are: ", fullErrorMessage);
throw new DbEntityValidationException(exceptionMessage, e.EntityValidationErrors);
}
That will show you the actual properties causing the validation issues. Then, update your question with the results, if you still need assistance.

Likely, your database is out of sync with your entities. The status property is not required on your entity, and by default properties of type string are nullable. That would explain why you're passing validation on post, but failing on actually saving the entity.
Generally, it's best not to rely on the database setting a default value in the first place. Instead, have the property itself have a default value, and then it will always be fine, regardless of what's going on at the database level:
private string _status;
public string status
{
get { return _status ?? "Default Value"; }
set { _status = value;
}
Short of that, if status is truly not required, then you should ensure that the status column on your table is nullable.

Related

Bad request (400) when using PutAsJsonAsync if Model has navigation properties

I am creating a simple API and I have a method that handles the update of a game:
[HttpPut("{Id}")]
public bool UpdateGame(Guid Id, [FromBody] Game game)
{
Game? oldGame = _unitOfWork.Games.GetById(Id);
if (oldGame != null)
{
oldGame.SeasonId = game.SeasonId;
oldGame.VenueId = game.VenueId;
oldGame.GameTypeId = game.GameTypeId;
oldGame.GameTitle = game.GameTitle;
oldGame.GameDateTime = game.GameDateTime;
oldGame.PublishResults = game.PublishResults;
oldGame.GameDetails = game.GameDetails;
oldGame.Buyin = game.Buyin;
oldGame.Fee = game.Fee;
_unitOfWork.Save();
return true;
}
else
{
return false;
}
}
I have tests against the repo and the controller and the update works fine.
I'm now implementing some CRUD pages using Blazor Server:
async Task OnSubmit()
{
if (Game != null)
{
try
{
var response = await _apiClient.httpClient.PutAsJsonAsync<Game>($"/api/Games/{Id}", Game);
bool updated = await response.Content.ReadFromJsonAsync<bool>();
if (updated)
{
_navManager.NavigateTo("/settings/games");
}
}
catch (Exception ex)
{
AlertIsVisible = true;
Message = ex.Message;
MessageType = AlertMessageType.Danger;
}
}
}
This is failing because of a bad request 400, but this is all the output is telling me. The put method in the controller doesn't hit as my breakpoint doesn't trigger.
Simpler models in my app post just fine using similar code.
The difference here I believe is this Game entity:
public class Game
{
public Guid Id { get; set; }
public Guid? SeasonId { get; set; }
public Guid? GameTypeId { get; set; }
public Guid? VenueId { get; set; }
public string? GameTitle { get; set; }
public DateTime GameDateTime { get; set; }
public bool PublishResults { get; set; }
public string? GameDetails { get; set; }
public double Buyin { get; set; }
public double Fee { get; set; }
public virtual Season Season { get; set; } = default!;
public virtual GameType GameType { get; set; } = default!;
public virtual Venue Venue { get; set; } = default!;
public virtual ICollection<Result> Results { get; set; } = new HashSet<Result>();
}
I think there is a serialisation issue with the navigation properties (Season, GameType and Venue - not the Results collection). If I remove the navigation properties the update succeeds. I'm at a loss at this point on how to handle the put request so that it works with the navigation properties on my model.
You declare Season, GameType and Venue as not Nullable [no ?], and then forcibly set to default!, which for any object is null.
You normally use default! when you know an object will be set to a value before any attempt is made to use it, which is not the case here.

EF Core returns only first record of a list unless FK entities are reset

I am facing the same issue as described in this question. Problem: my method GetAllConferences() returns correctly all the conferences from the DB, but when I return the result to the View from the controller return Ok(tripListVm) inly the first collection item is returned to the client. On the otehr side, by setting to null all the FK references (as pointed out in the SO question above) I can return correctly all the entities to the client, however this does not seem to me the proper way of proceeding.
EDIT: the solution was much simpler than I though. In the code below (I leave it in its original form for others to see it) I was not mapping the FK entities inside the ViewModel to Dto objects, but returning the model entity itself. That was the reason why I needed to null those inner references to make it work. By returning all Dtos objects, it works properly.
I have three entities involved with 1-many relationships:
public class Conference
{
public int Id { get; set; }
[Required]
[MaxLength(50)]
public string Name { get; set; }
public ICollection<Venue> Venues { get; set; }
public int? LocationId { get; set; }
public Location Location { get; set; }
}
public class Venue
{
public int Id { get; set; }
[Required]
[MaxLength(50)]
public string Name { get; set; }
public int? ConferenceId { get; set; }
public Trip Conference { get; set; }
public int? LocationId { get; set; }
public City City { get; set; }
}
public class City
{
public int Id { get; set; }
[Required]
[MaxLength(50)]
public string Name { get; set; }
public ICollection<Conference> Conferences { get; set; }
public ICollection<Venue> Venues { get; set; }
}
In the repository, I have a method that returns the conferences and the related entities (City and Venues):
public IEnumerable<Conference> GetAllConferences()
{
return _context.Conferences
.Include(t => t.Venues)
.Include(t => t.City)
.ToList();
}
In the controller I need to use the following code to return all the results:
var conferences = _repository.GetAllConferences();
if (conferences.Any())
{
var conferenceListVm = trips.ToConferenceVmList();
//Without setting the FK references to null, I can return only the first result of the collection
foreach (var vm in conferenceListVm)
{
foreach (var pm in vm.PoinOfInterests)
{
pm.Trip = null;
}
vm.Location.Conferences = null;
vm.Location.Venues = null;
}
return Ok(conferenceListVm);
}
public static ConferenceViewModel ToConferenceVm(this Conference conference)
{
var confVm = new ConferenceViewModel();
confVm.Name = conference.Name;
confVm.City = conference.City;
confVm.Venues = conference.Venues;
return tripVm;
}
public static IEnumerable<ConferenceViewModel> ToConferenceVmList(this IEnumerable<Conference> conferences)
{
return conferences.Select(c => c.ToConferenceVm()).ToList();
}

Under posted model state always valid

I have an interesting issue. I have following model with required data annotation for some of the properties.
public class Consumer : MongoEntity
{
public Consumer()
{
Products = new List<Product>();
}
[Required]
public string Email { get; set; }
[Required]
public string Password { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
public string Address { get; set; }
public string Phone { get; set; }
[Required]
public Location CurrentLocation { get; set; }
public List<Product> Products { get; set; }
}
Now in my web api controller I am accepting this as a parameter as show below:
[Route("")]
[ValidateModel]
public IHttpActionResult Post([FromBody]Consumer consumer)
{
try
{
if (ModelState.IsValid)
{
_consumerService.Create(consumer);
return Ok("User created sucessfully.");
}
return BadRequest("User object is not complete. It's missing mandatory fields");
}
catch
{
return InternalServerError(new Exception("Something went wrong while saving the data back to database."));
}
}
I am under the impression that if any of the required field of consumer model is null model state should be false, but it always return true. Web api only sets model state null when i send an empty body to the controller. Is there any logical explanation to this? Why web api don't take account of required properties of complex argument type?

How to generate and auto increment Id with Entity Framework

Revised entire post.
I'm trying to post the following JSON POST request via Fiddler:
{Username:"Bob", FirstName:"Foo", LastName:"Bar", Password:"123", Headline:"Tuna"}
However I'm getting this error:
Message "Cannot insert the value NULL into column 'Id', table 'xxx_f8dc97e46f8b49c2b825439607e89b59.dbo.User'; column does not allow nulls. INSERT fails.\r\nThe statement has been terminated." string
Though if I manually send a random Id along with the request then all is good. Like so:
{Id:"1", Username:"Bob", FirstName:"Foo", LastName:"Bar", Password:"123", Headline:"Tuna"}
Why does Entity Framework not generate and auto increment the Id's? My POCO class is as follows:
public class User
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public string Id { get; set; }
public string Username { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Password { get; set; }
public string Headline { get; set; }
public virtual ICollection<Connection> Connections { get; set; }
public virtual ICollection<Address> Addresses { get; set; }
public virtual ICollection<Phonenumber> Phonenumbers { get; set; }
public virtual ICollection<Email> Emails { get; set; }
public virtual ICollection<Position> Positions { get; set; }
}
public class Connection
{
public string ConnectionId { get; set; }
public int UserId { get; set; }
public virtual User User { get; set; }
}
public class Phonenumber
{
public string Id { get; set; }
public string Number { get; set; }
public int Cycle { get; set; }
public int UserId { get; set; }
public User User { get; set; }
}
Here is the controller method. When in debug mode and I send the request via Fiddler it breaks at db.SaveChanges(); and gives the error seen a bit above.
// POST api/xxx/create
[ActionName("create")]
public HttpResponseMessage PostUser(User user)
{
if (ModelState.IsValid)
{
db.Users.Add(user);
db.SaveChanges();
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Created, user);
response.Headers.Location = new Uri(Url.Link("DefaultApi", new { id = user.Id }));
return response;
}
else
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
}
}
What's wrong?
Solution
Change string Id to int instead and remove Data annotations. Renamed the Id to UserId, still following convention, and made changes where necessary in other POCO's to match up with the changes.
This is a guess :)
Is it because the ID is a string? What happens if you change it to int?
I mean:
public int Id { get; set; }
You have a bad table design. You can't autoincrement a string, that doesn't make any sense. You have basically two options:
1.) change type of ID to int instead of string
2.) not recommended!!! - handle autoincrement by yourself. You first need to get the latest value from the database, parse it to the integer, increment it and attach it to the entity as a string again. VERY BAD idea
First option requires to change every table that has a reference to this table, BUT it's worth it.

TryUpdateModel sets property to null on insert even though a value is specified, using MVC4 and Entity Framework

I hope this is an easy one.
Using MVC4 with Entity Framework 4.1.
I'am trying to update a model with some properties passed through from a View with a custom view model's properties.
I have this class.
public class SitePage
{
public System.Guid PageID { get; set; }
public int PageTypeID { get; set; }
public string PageTitle { get; set; }
public Nullable<int> RegionId { get; set; }
public Nullable<int> LockLevelId { get; set; }
public System.DateTime UpdatedDate { get; set; }
public System.Guid UpdatedById { get; set; }
public System.Guid CreatedById { get; set; }
public System.DateTime CreatedDate { get; set; }
public string Url { get; set; }
public int PageStateId { get; set; }
public string SummaryImage { get; set; }
}
My controller
public ActionResult Update(AddPageViewModel model)
{
if (ModelState.IsValid)
{
var g = new Guid(model.PageId);
SitePage UpdatePage = dbSite.SitePages.FirstOrDefault(m => m.PageID == g);
//This is a page update. Not updating all properties
UpdatePage.PageTitle = model.PageTitle;
UpdatePage.GetFirstSection.PageSectionText.PageText = model.PageText;
UpdatePage.PageTypeID = (int)SitePageType.PageTypes.CommunityPage;
UpdatePage.RegionId = model.Region
UpdatePage.CreatedById = userId;
UpdatePage.CreatedDate = DateTime.UtcNow;
UpdatePage.UpdatedById = userId;
UpdatePage.UpdatedDate = DateTime.UtcNow;
TryUpdateModel(UpdatePage);
dbSite.Save();
return RedirectToAction("Community_test", "Community", new { id = UpdatePage.Url });
}
else
{
return RedirectToAction("Community_test", "Community");
}
}
Passing this page view model to the controller
public class AddPageViewModel
{
public string PageTitle { get; set; }
public string PageText { get; set; }
public int Region { get; set; }
public string TagString { get; set; }
public string ParentPageId { get; set; }
public string PageId { get; set; }
}
If i debug I can see that the SitePage instance UpdatePage has all the correct values when TryUpdateModel is called. However all values except RegionId are updated correctly in the db. RegionId gets set as null. If i run a trace on the server the sql entity framework generates is indeed setting RegionId to null even though my model has a value. This problem is intermittent! Some times it works, other it doesnt. 1 in 4 will update correctly and i cant quite see where im going wrong. Im newish to this stuff really.
Our classes are generated from the database and we are using an edmx file. Im not sure if i can make any changes to these classes without them just being overridden the next time we update model from database.
Any ideas or more info needed, give me a shout. Cheers
TryUpdateModel takes the values from the form and populate the given model. This means that setting any values on UpdatePage isn't necessary.
if (ModelState.IsValid)
{
var updatePage = dbSite.SitePages.FirstOrDefault(m => m.PageID == model.PageId);
TryUpdateModel(updatePage);
dbSite.Save();
//...
}
It is good practice to also specify the properties that should be updated.
TryUpdateModel(updatePage, new string[] { "PageTitle", "RegionId", etc.. });
Regarding RegionId getting set to Null.
If i debug I can see that the SitePage instance UpdatePage has all the correct values when TryUpdateModel is called.
Before TryUpdateModel is called or right after?

Categories