Cannot get relationship to update for navigation properties in entity framework

后端 未结 3 644
梦谈多话
梦谈多话 2020-12-05 21:38

I am currently using EF4.3 and Code First. Creation of my objects works (via my views - just using the auto-generated Create), but when I attempt to edit an object, it does

相关标签:
3条回答
  • 2020-12-05 22:22

    Setting the state to Modified only marks scalar properties as modified, not navigation properties. You have several options:

    • A hack (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 (eager loading) the current manager. Then set the properties. Change tracking will detect a change of the manager again and write an UPDATE.

    • Expose a 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 to Modified will include this property. Moreover you don't need to load the Manager user from the database, you can just assign the ID 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();
      
    0 讨论(0)
  • 2020-12-05 22:24

    I am not sure exactly what you mean by navigation properties? Do you mean like a foreign key relationship? If so then 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; }
    }
    

    Update 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 that works?

    0 讨论(0)
  • 2020-12-05 22:30

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

    Option 1: Using GraphDiff

    *This needs the Configuration.AutoDetectChangesEnabled of your context 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();
    }
    

    For more details about GraphDiff look here.

    Option 2: Find and Edit

    Searching your entity with EF to track it to the context. Then edit the properties.

    *This needs the Configuration.AutoDetectChangesEnabled of your context 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 a matter of performance this is the best way, but it mess your class with database concerns. Because you will need to create a scalar (primitive type) property to map the Id.

    *In this way there is no need to set the Configuration.AutoDetectChangesEnabled to true. And also you won't need to do a query to the database to retrieve the entities (as the first two options would - yes GraphDiff does it 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();
    }
    
    0 讨论(0)
提交回复
热议问题