Unable to get a link to update navigation properties in entity infrastructure

I am currently using EF4.3 and Code First. Creating my objects works (through my views - only using the automatically created Create), but when I try to edit the object, it does not save any changes that ultimately bind to my navigation properties. I was reading about relationships , but I don’t understand how to tell my context that relationships have changed.

Here is a sample code for my implementation.

@* Snippet from my view where I link into my ViewModel. *@ <div class="row"> <div class="editor-label"> @Html.LabelFor(model => model.ManagerID) </div> <div class="editor-field"> @Html.DropDownListFor(model => model.ManagerID, ViewBag.Manager as SelectList, String.Empty) @Html.ValidationMessageFor(model => model.ManagerID) </div> </div> 

Here is my controller implementation (POST of my editing):

  [HttpPost] public ActionResult Edit(ProjectViewModel projectViewModel) { if (ModelState.IsValid) { Project project = new Project(); project.ProjectID = projectViewModel.ProjectID; project.Name = projectViewModel.Name; project.ProjectManager = repository.GetUser(projectViewModel.ManagerID); repository.InsertOrUpdateProject(project); repository.Save(); return RedirectToAction("Index"); } ViewBag.Manager = new SelectList(repository.GetUsers(), "UserID", "FullName", projectViewModel.ManagerID); return View(projectViewModel); } 

Inside the Project object:

 public class Project { public int ProjectID { get; set; } [Required] public string Name { get; set; } // Navigation Properties public virtual User Manager { get; set; } } 

Here is the corresponding method from the repository (where is my context):

 public void InsertOrUpdateProject(Project project) { if (program.ProjectID == default(int)) { context.Projects.Add(project); } else { context.Entry(project).State = EntityState.Modified; } } 

To be clear, this works to update my properties, but it does not update my navigation properties (in this case, the dispatcher). Appreciate any help.

+6
source share
3 answers

Setting the state to Modified means only scanning properties as changed, not navigation properties. You have several options:

  • Hacking (you won't like it)

     //... else { var manager = project.Manager; project.Manager = null; context.Entry(project).State = EntityState.Modified; // the line before did attach the object to the context // with project.Manager == null project.Manager = manager; // this "fakes" a change of the relationship, EF will detect this // and update the relatonship } 
  • Reload the project from the database, including (loading) the current manager. Then set the properties. Tracking changes will detect the manager change again and write an update message.

  • Derive the foreign key property for the Manager navigation property in your model:

     public class Project { public int ProjectID { get; set; } [Required] public string Name { get; set; } public int ManagerID { get; set; } public virtual User Manager { get; set; } } 

    Now ManagerID is a scalar property and setting the state in Modified will include this property. In addition, you do not need to load the Manager user from the database, you can just assign the ID that you get from your view:

     Project project = new Project(); project.ProjectID = projectViewModel.ProjectID; project.Name = projectViewModel.Name; project.ManagerID = projectViewModel.ManagerID; repository.InsertOrUpdateProject(project); repository.Save(); 
+11
source

There are several options here, I will list 3 of them:

Option 1: Using GraphDiff

* For this, Configuration.AutoDetectChangesEnabled your context is set to true.

Just install GraphDiff with NuGet

 Install-Package RefactorThis.GraphDiff 

Then

 using (var context = new Context()) { var customer = new Customer() { Id = 12503, Name = "Jhon Doe", City = new City() { Id = 8, Name = "abc" } }; context.UpdateGraph(customer, map => map.AssociatedEntity(p => p.City)); context.Configuration.AutoDetectChangesEnabled = true; context.SaveChanges(); } 

Read more about GraphDiff here .

Option 2: Search and Edit

Search for your object using EF to track it in context. Then edit the properties.

* For this, Configuration.AutoDetectChangesEnabled your context is set to true.

 var customer = new Customer() { Id = 12503, Name = "Jhon Doe", City = new City() { Id = 8, Name = "abc" } }; using (var context = new Contexto()) { var customerFromDatabase = context.Customers .Include(x => x.City) .FirstOrDefault(x => x.Id == customer.Id); var cityFromDataBase = context.Cities.FirstOrDefault(x => x.Id == customer.City.Id); customerFromDatabase.Name = customer.Name; customerFromDatabase.City = cityFromDataBase; context.Configuration.AutoDetectChangesEnabled = true; context.SaveChanges(); } 

Option 3: using a scalar property

In terms of performance, this is the best way, but it will prevent your class from having a database problem. Because you will need to create a scalar (primitive type) property to match the identifier.

* Thus, there is no need to set Configuration.AutoDetectChangesEnabled to true. And also you will not need to make a query to the database to extract entities (since the first two parameters are yes, GraphDiff does this behind the scenes!).

 var customer = new Customer() { Id = 12503, Name = "Jhon Doe", City_Id = 8, City = null }; using (var contexto = new Contexto()) { contexto.Entry(customer).State = EntityState.Modified; contexto.SaveChanges(); } 
+3
source

I'm not sure what you mean by navigational properties? Do you mean relations with a foreign key? If so, try the following data annotation:

 public class Project { public int ProjectID { get; set; } [Required] public string Name { get; set; } [ForeignKey("YourNavigationProperty")] public virtual UserManager { get; set; } } 

Refresh your EF context and see what happens.

UPDATE

 public class Project { public int ProjectID { get; set; } [Required] public string Name { get; set; } [ForeignKey("ManagerId")] public ManagerModel UserManager { get; set; } } public class ManagerModel { [Key] public int ManagerId { get; set; } public String ManagerName { get; set; } } 

See if this works?

-2
source

Source: https://habr.com/ru/post/910197/


All Articles