How to Update database table row when the foreign key has the same value ASP.NET mvc - entity-framework

I have a application where stores can complete a questionnaire. within this application i have two tables db.StoreAud(pk:AuditId) which contains all the stores information, and db.storequests(pk:ReviewId) which holds the all questions information.
AuditId is a foreign key in db.storequests table. Now here is the issue if a store complete the questionnaire the data saves perfectly in the database, however is the same store does the questionnaire again the db.storequests creates a new row in the database with a new primary key value instead of updating the previous row. Question is how can i update the previous row if the same store does the same questionnaire again. hope this made since.
db.StoreAUD
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
[Column(Order = 1)]
public int AuditId { get; set; }
public string Date { get; set; }
public int StoreNumber { get; set; }
public string StoreName { get; set; }
db.storequests
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public int ReviewId { get; set; }
public int AuditId { get; set; }
public int QuestionOne { get; set; }
public string QuestionTwo { get; set; }
public string QuestionThree { get; set; }
public string QuestionFour { get; set; }
controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(StoreQuestions storequestions)
{
if (ModelState.IsValid)
{
StoreAudit findingAudit = db.StoreAudit.Find(storequestions.AuditId); // grabbing the id from th store audit table
findingAudit.ReadOnlyTickBox = true;
db.StoreQuestions.Add(storequestions);
db.SaveChanges();
return RedirectToAction("Details", "Audit", new { id = storequestions.AuditId });
}
return View(storequestions);
}

I would seperate your update logic into a new Update action, following the Single Responsibility Principle:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Update(StoreQuestions storequestions)
{
if (ModelState.IsValid)
{
StoreAudit findingAudit = db.StoreAudit.Find(storequestions.AuditId);
findingAudit.ReadOnlyTickBox = true;
// update objects here
db.SaveChanges();
return RedirectToAction("Details", "Audit", new { id = storequestions.AuditId });
}
return View(storequestions);
}
}

Related

Why I am getting an error when i try to save form into quiz model with entity framework?

I am to new both entity framework and dotnet core. Shortly i want to explain what i did and what kind of an error i got?
What i did?
First I created a few models below.
public class Quiz
{
public int QuizID { get; set; }
public int UserID { get; set; }
public string Text { get; set; }
public User User { get; set; }
public ICollection<Question> Questions { get; set; }
}
public class Question
{
public int QuestionID { get; set; }
public string Text { get; set; }
public int QuizID { get; set; }
public Quiz Quiz { get; set; }
public IList<Option> Options { get; set; }
}
public class User
{
public int ID { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public ICollection<Quiz> Quizzes { get; set; }
}
public class Option
{
public int OptionID { get; set; }
public string Choice { get; set; }
public int QuestionID { get; set; }
public bool? IsCorrect { get; set; }
public Question Question { get; set; }
}
Second I created IActionResult method for saving informations that comes from user.
public async Task<IActionResult> CreateQuiz()
{
Quiz quiz = new Quiz();
quiz.UserID = 0;
quiz.Text = Request.Form["content"].ToString();
_context.Add(quiz);
await _context.SaveChangesAsync();
for (int i = 1; i <= 4; i++)
{
Question question = new Question();
question.QuizID = quiz.QuizID;
question.Text = Request.Form["title_" + i].ToString();
_context.Add(question);
_context.SaveChanges();
for (int j = 1; j <= 4; j++)
{
Option option = new Option();
option.QuestionID = question.QuestionID;
option.Choice = Request.Form["option_a" + i].ToString();
option.IsCorrect = j == int.Parse(Request.Form["correct_answer_" + i].ToString());
_context.Add(option);
}
}
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
Finally i got this error when i save form.
An unhandled exception occurred while processing the request.
SqliteException: SQLite Error 19: 'FOREIGN KEY constraint failed'.
Microsoft.Data.Sqlite.SqliteException.ThrowExceptionForRC(int rc,
sqlite3 db)
DbUpdateException: An error occurred while updating the entries. See
the inner exception for details.
Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection
connection, CancellationToken cancellationToken)
I need your help guys.
*Sorry for spelling rules i am also new to english.
There are a few things you can do to help avoid issues like this. It's a bit hard to pin down from your description exactly what is wrong, but you can simplify your code considerably which should take care of it.
Firstly, follow a convention for your key names and use annotations to nominate your Keys rather than relying on convention. You have a mix of naming like "QuestionId" for the PK on Question, yet "Id" for the PK on User.
Next, I would remove all FK fields and use Shadow Properties for the FK fields. The issue with FK columns is they are a second source of truth for relationships when you use Navigation Properties. Is option.QuestionId the ID of the question, or option.Question.QuestionId? How do you guarantee these are always in sync?
Lastly for the entity definitions, declare the navigation properties as virtual. This serves both lazy-loading (as a failsafe) as well as change tracking via proxies.
So updating the entity definitions to something like:
public class Quiz
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int QuizID { get; set; }
public string Text { get; set; }
public virtual User User { get; set; }
public virtual ICollection<Question> Questions { get; set; } = new List<Question>();
}
public class Question
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int QuestionID { get; set; }
public string Text { get; set; }
[ForeignKey("QuizID")] // Creates a shadow property mapped to a QuizID column in table.
public virtual Quiz Quiz { get; set; }
public virtual ICollection<Option> Options { get; set; } = new List<Option>();
}
public class User
{
[Key]
public int UserID { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public virtual ICollection<Quiz> Quizzes { get; set; } = new List<Quiz>();
}
public class Option
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int OptionID { get; set; }
public string Choice { get; set; }
public bool? IsCorrect { get; set; }
[ForeignKey("QuestionID")]
public virtual Question Question { get; set; }
}
Now, when it comes to creating your question data you can leverage EF to manage the FKs by populating a complete set of related entities and saving them together.
public async Task<IActionResult> CreateQuiz()
{
var systemUser = _context.Users.Single(x => x.UserId == 0);
Quiz quiz = new Quiz();
quiz.User = systemUser;
quiz.Text = Request.Form["content"].ToString();
for (int i = 1; i <= 4; i++)
{
Question question = new Question();
question.Text = Request.Form["title_" + i].ToString();
quiz.Questions.Add(question);
for (int j = 1; j <= 4; j++)
{
Option option = new Option();
option.Choice = Request.Form["option_a" + i].ToString();
option.IsCorrect = j == int.Parse(Request.Form["correct_answer_" + i].ToString());
question.Options.Add(option);
}
}
_context.Quizes.Add(quiz);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
This can likely be simplified even more, but I kept it close to the original so it should be easier to follow. Rather that calling SaveChanges several times and trying to set FKs, it is far better to create the objects and associate them with each other. We create our quiz, then go through and create the questions, associating them to the Quiz by adding them to the quiz.Questions collection. Then go through and do the same for the Question Options. Once we are done, we tell the context to add the Quiz to it's Quizes DbSet, and call SaveChanges once at the very end. EF will save all of the entities and their relationships automatically, populating the appropriate FKs. The benefit here is that all of the changes are committed together in one transaction rather than separate saves where a quiz might be saved, but there was an issue with one question or one option, leaving the database in an incomplete state at the point of failure.

Entity Framework Core: How do I update a record with nested fields?

I've got a simple "ContactsList" ASP.Net Core Web (REST) application, .Net Core 3.0, an MSSQL LocalDB, using MSVS 2019.
My "Contact" entity contains a list of "Notes".
When I create a new contact that already contains one or more notes, everything works fine. EF automagically inserts the notes into the notes table.
But when I try to UPDATE a contact, EF seems to disregard "notes".
Q: For "Updates", do I need write code in my controller to explicitly update the notes myself?
Or am I doing something "wrong", such that EF can't "automagically" do the updates it's supposed to?
Models/Contact.cs:
public class Contact
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ContactId { get; set; }
public string Name { get; set; }
public string EMail { get; set; }
public string Phone1 { get; set; }
public string Phone2 { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
public List<Note> Notes { get; set; }
}
Models/Note.cs:
public class Note
{
public Note()
{
this.Date = DateTime.Now; // Default value: local "now"
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int NoteId { get; set; }
public string Text { get; set; }
public DateTime Date { get; set; }
[ForeignKey("Contact")]
public int ContactId { get; set; }
}
Controllers/ContactsController.cs (POST works: if there are notes in the contacts list, it adds them):
[HttpPost]
public async Task<ActionResult<Contact>> PostContact(Contact contact)
{
_context.Contacts.Add(contact);
await _context.SaveChangesAsync();
//return CreatedAtAction("GetContact", new { id = contact.ContactId }, contact);
return CreatedAtAction(nameof(GetContact), new { id = contact.ContactId }, contact);
}
Controllers/ContactsController.cs (PUT seems to completely disregard any assocated notes):
[HttpPut("{id}")]
public async Task<IActionResult> PutContact(int id, Contact contact)
{
if (id != contact.ContactId)
{
return BadRequest();
}
_context.Entry(contact).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!ContactExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return NoContent();
}
The SQL for POST shows four separate INSERTs: one for the contact, and one for each note.
The SQL for PUT only shows one UPDATE: just the contact; nothing else.
The debugger shows "notes" are clearly part of the "Contact" record that the controller received by PutContact().
Q: Should EF deal with "updating" notes automagically, or do I need to hand-code my updates in the controller?
Entity Framework Core ignores relationships unless you explicitly include them in queries.
_context.Entry(contact).State = EntityState.Modified;
The problem with the line above is that you did not specify that the related data has been modified, so it will be ignored in the query.
So you can either
attach all the related data
set the state of the related data to EntityState.Modified
or you can
query the object in the database and include the related data
and then assign the contact object to that queried object
var dbContactObj = _context.Contacts.Include(x => x.Notes).First(x => x.Id == contact.Id);
dbContactObj = contact;
_context.SaveChangesAsync();

update foreign key using entity framework

i am trying to update foreign key in a table named(Friendship).The foreign key is of the table named(FriendshipStatus) the problem is that all the values are updated except the foreign key. I m using code first approach.
Friendship Class
public class Friendship
{
public int Id { get; set; }
public User UserOne { get; set; }
public User UserTwo { get; set; }
public FriendshipStatus Status { get; set; }
public User ReqSB { get; set; }
public RelationType RelationType { get; set; }
public Relationship Relationship { get; set; }
public DateTime FriendshipDate { get; set; }
}
FriendshipStatus class
public class FriendshipStatus
{
public int Id { get; set; }
public string Name { get; set; }
}
Here is the code for update
using (context)
{
Friendship f = getFrienshipRecord(u1, u2); // get single record from db which is to be updated
if (f != null)
{
Friendship ff = new Friendship();
ff.Status = new FriendshipStatus() { Id = 2}; //actually wants to update this this field
ff.Id = f.Id;
ff.FriendshipDate = DateTime.Now;
context.Entry(ff).State = EntityState.Modified;
context.SaveChanges();
}
}
The above code changes datetime but it does not change foreign key.
This is the technique I use for updates that include a child. First, I like to expose the Foreign Key as part of the parent. If you name it FriendshipStatusId, EF will make the association automatically or you can add an annotation or fluent code if preferred:
public class Friendship
{
public int Id { get; set; }
public User UserOne { get; set; }
public User UserTwo { get; set; }
public int? FriendshipStatusId { get; set; } // optional FK
public FriendshipStatus Status { get; set; }
public User ReqSB { get; set; }
public RelationType RelationType { get; set; }
public Relationship Relationship { get; set; }
public DateTime FriendshipDate { get; set; }
}
Now you can do your update by simply fetching the entity (which puts it under tracking) and updating the FK:
using (context)
{
Friendship f = getFrienshipRecord(u1, u2); // get single record from db which is to be updated
if (f != null)
{
f.FriendshipDate = DateTime.Now;
f.FriendshipStatusId = 2;
context.SaveChanges();
}
}
Note that if you add the FK you may need to do a migration or regenerate your database because the EF default might be something like FriendshipStatus_Id.

Inserting data into multiple tables using Entity Framework

I have two tables in my database that I am filling via Web API:
Orders
________
OrderID
OrderDate
OrderStatusID
and
OrderItems
___________
OrderID
ItemID
ItemVersionID
ItemNote
ItemSortOrder
I need to insert Order and all the items for that order in two tables. OrderID is an identity field generated by the database that I will need for inserting data into OrderItems table.
Primary key for the OrderItems table is a composite key (OrderID, ItemID, ItemVersionID), it is important since the same order can contain multiple items with the same ID but different Version ID.
I was wondering if I will have to add Order and OrderItems data separately or can do so in a single controller function.
Below are my model classes:
[Table("SN_Orders")]
public class Order
{
[Key]
public int OrderID { get; set; }
public DateTime OrderDate { get; set; }
public int OrderStatusID { get; set; }
public List<OrderItem> Details { get; set; }
}
[Table("SN_OrderItems")]
public class OrderItem
{
[Column(Order = 0), Key]
public int ItemID { get; set; }
[Column(Order = 1), Key]
public int ItemVersionID { get; set; }
[Column(Order = 2), Key]
public int OrderID { get; set; }
public string ItemNote { get; set; }
public int ItemSortOrder { get; set; }
}
And below is my attempt to pass in a list of OrderItems with Order that did not work out:
[HttpPost]
public IHttpActionResult PostItemToOrder(myClass.Order ord1, List<myClass.OrderItem> itemList)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
using (MyContext ctx = new MyContext())
{
ctx.Orders.Add(ord1);
ctx.SaveChanges();
foreach (var item in itemList)
{
item.OrderID=ord1.OrderId;
ctx.OrderItems.Add(item);
ctx.SaveChanges();
}
}
}
Is that something that is possible to accomplish at once? Or will I have to insert order first, return the OrderID to the calling program and then insert Items?
Put it like this:
Your Order class should have:
public class Order
{
public int Id { get; set; }
// all rest
public virtual ICollection<OrderItem> Items { get; set; } // here is the trick
}
[HttpPost]
public IHttpActionResult PostItemToOrder(myClass.Order ord1, List<myClass.OrderItem> itemList)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
using (MyContext ctx = new MyContext())
{
var order = ord1;
order.Items = itemList;
ctx.Orders.Add(order);
ctx.SaveChanges();
}
}

Updating a relation between two Entity Framework entities?

I have two related Entity Framework 6 classes in my data layer.
public class Order
{
public int Id { get; set; }
public virtual SalesStatus SalesStatus { get; set; }
}
public class SalesStatus
{
public SalesStatus()
{
Orders = new List<Order>();
}
public int Id { get; set; }
public string Name { get; set; }
public virtual List<Order> Orders { get; set; }
}
public class OrderVM
{
public int Id { get; set; }
public SalesStatus SalesStatus { get; set; }
}
I am using Automapper to map these to my view models and back again.
cfg.CreateMap<Order, OrderVM>()
.MaxDepth(4)
.ReverseMap();
The status entity is used to populate a drop down list.
In my method I am taking the selected value and trying to update the order record to the new selected status.
private bool SaveOrderToDb(OrderVM orderVM)
{
using (var db = new MyContext())
{
var order = AutomapperConfig.MapperConfiguration.CreateMapper().Map<OrderVM, Order>(orderVM);
order.SalesStatus = db.SalesStatuses.Find(Convert.ToInt16(orderVM.SalesStatusSelectedValue));
db.Set<Order>().AddOrUpdate(order);
db.SaveChanges();
}
return true;
}
This does not update the relationship in the database. Why? What am I missing?