Entity Framework - not updating referenced model - entity-framework

I have a model, 'Person', that references another model, 'Salutation'.
public class Person
{
public int Id { get; set; }
public Boolean Active { get; set; }
// fk
public virtual Salutation Salutation { get; set; }
public virtual PersonName Name { get; set; }
}
public class Salutation
{
public int Id { get; set; }
public string Name { get; set; }
}
when I try and UPDATE the 'Person' with a different 'Salutation' it doesn't update. Though if I change the actual data within the 'salutation' it does update. This is the UPDATE code in my controller, the data coming in to the function is correct but just doesn't save in the DB.
For example if the current Salutation has ID: 1 and Name: "Mr" then if I try and pass in a different existing record with ID: 2 and Name: "Mrs" it doesn't change. But if I pass in ID:2 and Name:"RandomAlienString" then it DOES change to the new model and updates the Salutation.
In the controller - UPDATE Method:
public void PutPerson(int id, [FromBody]Person person)
{
if (!ModelState.IsValid)
{
throw new HttpResponseException(HttpStatusCode.BadRequest);
}
if (id != person.Id)
{
throw new HttpResponseException(HttpStatusCode.BadRequest);
}
db.Entry(person).State = EntityState.Modified;
db.Entry(person.Name).State = EntityState.Modified;
db.Entry(person.Salutation).State = EntityState.Modified;
try
{
db.SaveChanges();
}
catch (DbUpdateConcurrencyException)
{
if (!PersonExists(id))
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
else
{
throw;
}
}
}
Any help would be most appreciated.

Related

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();

Reset ID of rows after deleting or adding data in Entity framework

I have a DbSet of objects on which I preform CRUD operations. My question is how to reset the value of the ID upon deleting or adding a new item. My class structure is outlined below:
public class Guitar
{
[Key]
public int ID { get; set; }
public string Make { get; set; }
public string Model { get; set; }
public double Price { get; set; }
}
I add or delete objects like so:
public void Add(Guitar g)
{
try
{
lock (context)
{
context.Guitars.Add(g);
context.SaveChanges();
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
public void Delete(int id)
{
Guitar g = context.Guitars.Find(id);
context.Guitars.Remove(g);
context.SaveChanges();
}
If I delete the last row for example with an ID of 5 it works. But when I add another I expect it's ID to be 5 but instead it is 6.

Saving one entity having m-t-m relationship duplicate the other entity in the DB?

I have two entities with many to many relationship, I'm saving one entity that has a list of entities, but they get duplicated
ex:
class GENEntity
{
int Id { get; set; }
string Name { get; set; }
List<GENEntityTab> tabs { get; set; }
}
class GENEntityTab
{
int Id { get; set; }
string Name { get; set; }
List<GENEntity> entities { get; set; }
}
When I save object of GENEntity but as a view model as you'll see next, with a list of two GENEntityTab, those two tabs get inserted (duplicated) in the DB. I'm using Web API and angular
In the Repository, it's only called when I click submit (there are some extra properties):
public static JsonViewData AddOrUpdate(ModelDBContext context, GENEntityViewModel entityVM, string name, string id)
{
try
{
var entity = context.Entities.SingleOrDefault(x => x.Id == entityVM.Id);
if (entity == null)
{
entity = new GENEntity();
entity.InjectFrom(entityVM);
context.Entities.Add(entity);
}
else
{
entity.DateUpdated = DateTime.Now;
entity.InjectFrom(entityVM);
}
entity.CreatedById = new Guid(id);
entity.LastUpdatedById = new Guid(id);
context.SaveChanges();
return new JsonViewData { IsSuccess = true, Message = "Created Successfully" };
}
catch (Exception ex)
{
return new JsonViewData { IsSuccess = false, Message = ex.Message };
}
}
the view models:
public class GENEntityTabViewModel
{
public long Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public List<GENEntity> Entities { get; set; } = new List<GENEntity>();
public GENEntityTabViewModel()
{
}
public GENEntityTabViewModel(GENEntityTab entityTab)
{
Id = entityTab.Id;
Name = entityTab.Name;
Description = entityTab.Description;
Entities = entityTab.Entities;
}
}

EF 5.0 not removing optional one to one navigation property

Setting an optional one to one navigation property to null in Entity Framework 5 does not seem to make it to the database. Is this expected behavior?
In the example below, Person is a proxy object. I would expect setting the address to null will cause the address to be removed from the database.
The code below works if I lazy load the address before setting it null. But loading the address before
Any help will be greatly appreciated.
namespace ConsoleApplication2
{
using System;
using System.Data.Entity;
internal class Program
{
private static void Main(string[] args)
{
using (PersonContext context = new PersonContext())
{
// Make sure person with Id = 1 exists with an address.
Person person = context.People.Find(1) ?? context.People.Add(new Person { Id = 1 });
if (person.Address == null)
{
person.Address = new Address
{
Street = "123 Main Street",
City = "SomeCity",
State = new State
{
Code = "NY",
Name = "New York"
},
Zip = "11771"
};
}
context.SaveChanges();
}
// Setting address to null should remove relationship
using (PersonContext context = new PersonContext())
{
Person person = context.People.Find(1);
Console.WriteLine("Person is a " + person.GetType());
person.Address = null;
context.SaveChanges();
if (person.Address == null)
{
Console.WriteLine("Success: Person.Address is null.");
}
else
{
Console.WriteLine("Failure: Person.Address is not null.");
}
}
}
}
public class Person
{
public int Id { get; set; }
public virtual Address Address { get; set; }
}
public class Address
{
public int Person_Id { get; set; }
public string Street { get; set; }
public string City { get; set; }
public int StateId { get; set; }
public State State { get; set; }
public string Zip { get; set; }
}
public class State
{
public int Id { get; set; }
public string Code { get; set; }
public string Name { get; set; }
}
public class PersonContext : DbContext
{
public DbSet<Person> People { get; set; }
public DbSet<State> States { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Address>()
.HasKey(x => x.Person_Id);
modelBuilder.Entity<Person>()
.HasOptional<Address>(x => x.Address)
.WithRequired()
.WillCascadeOnDelete();
}
}
}
Where you are not using lazy loading, the related properties such as Address will not be loaded and so will already be null.
To ensure it is always loaded use eager loading :
Person person = context.People.Include(x => x.Address).Single(x => x.Id == 1);

Insert/update multiple objects

I have the following class:
public class Location
{
public int Id { get; set; }
public string Description { get; set; }
public string CarId { get; set; }
public Car Car { get; set; }
}
public class Car
{
public string Id { get; set; }
public string Color { get; set; }
}
And a view:
<div>#Html.LabelFor(location => location.Description)</div>
<div>#Html.EditorFor(location => location.Description)</div>
<div>#Html.LabelFor(location => location.Car.Id)</div>
<div>#Html.EditorFor(location => location.Car.Id)</div>
<div>#Html.LabelFor(location => location.Car.Color)</div>
<div>#Html.EditorFor(location => location.Car.Color)</div>
When i try this:
[HttpPost]
public ActionResult Create(Location location)
{
if (ModelState.IsValid)
{
Car car = db.Car.Find(location.Car.Id);
if (car != null)
db.Entry(car).CurrentValues.SetValues(location.Car);
else
db.Car.Add(location.Car);
db.Location.Add(locacao);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(locacao);
}
it breaks in 'db.SaveChanges' cause it says 'Cannot insert duplicate key in object 'dbo.Car'.'
If I remove 'db.Location.Add(locacao);', so it works pretty fine for the car(insert and update), but no location is added in database.
How can I do to insert a new car when there is not car's id in database, update when there is, and insert a new location?
Add method always adds all entities in the object graph so both db.Car.Add(location.Car) and db.Location.Add(location) are inserting both location and car.
Try this:
db.Location.Add(locacation);
// You can also use another way to find if you are working with a new Car
// like location.Car.Id == 0
if (db.Car.Any(c => c.Id == location.Car.Id))
{
db.Entry(location.Car).State = EntityState.Modified;
}
db.SaveChanges();