2016-11-01 3 views
1

У меня возникли проблемы с пониманием того, как обновлять значения CheckBoxList в таблице CustomerDevice в моей базе данных в [HttpPost] Редактировать метод действий CustomerDeviceController.ASP.NET Core MVC Редактировать значения CheckBoxList в базе данных

My Index Action Method для CustomerDeviceController отображает список клиентов из таблицы моих клиентов. У меня есть ActionLink с надписью «Изменить», которая передает значение CustId методу ActionDeviceController [HttpGet] Edit (int? Id), который затем отображает все выбранные значения DevId, назначенные CustId, в CheckBoxList, эта часть работает нормально.

Когда я пытаюсь обновить значения CheckBoxList, теперь я получаю следующее сообщение об ошибке ниже. Мне сказали, что мне нужно будет пропустить и удалить значения DevId, прежде чем я смогу их обновить. Это та часть, с которой мне трудно понять.

Сообщение об ошибке при обновлении CheckBoxList

Исключение типа «System.InvalidOperationException» произошло в Microsoft.EntityFrameworkCore.dll, но не был обработан в пользовательском коде

Дополнительная информация: экземпляр типа объекта «CustomerDevice» не может быть отслежен, потому что еще один экземпляр этого типа с тем же ключом уже отслеживается. При добавлении новых объектов для большинства типов ключей будет создано уникальное временное ключевое значение, если ключ не установлен (т. Е. Если для свойства ключа присвоено значение по умолчанию для его типа). Если вы явно устанавливаете ключевые значения для новых объектов, убедитесь, что они не сталкиваются с существующими объектами или временными значениями, сгенерированными для других новых объектов. При прикреплении существующих объектов убедитесь, что к контексту привязан только один экземпляр объекта с заданным значением ключа.

Модели

public class CheckBoxListItem 
{ 
    public int ID { get; set; } 
    public string Display { get; set; } 
    public bool IsChecked { get; set; } 
} 

    public class Customer 
{ 
    public int CustId { get; set; } 
    public string CustDisplayName { get; set; } 
    ... 
} 

    public class Device 
{ 
    public int DevId { get; set; } 
    public string DevType { get; set; } 
} 

    public class CustomerDevice 
{ 
    public int CustId { get; set; } 
    public int DevId { get; set; } 

    public Customer Customer { get; set; } 
    public Device Device { get; set; } 
} 

ViewModels

public class CustomerDeviceFormViewModel 
{ 
    public int CustId { get; set; } 
    public string CustDisplayName { get; set; } 
    public List<CheckBoxListItem> Devices { get; set; } 
} 

CustomerDeviceController

public ActionResult Create(int? id) 
    { 
     if (id == null) 
     { 
      return NotFound(); 
     } 

     var customervm = new CustomerDeviceFormViewModel(); 
     { 
      Customer customer = db.Customers.SingleOrDefault(c => c.CustId == id); 

      if (customer == null) 
      { 
       return NotFound(); 
      } 

      customervm.CustId = customer.CustId; 
      customervm.CustDisplayName = customer.CustDisplayName; 

      // Retrieves list of Devices for CheckBoxList 
      var deviceList = db.Devices.ToList(); 
      var checkBoxListItems = new List<CheckBoxListItem>(); 
      foreach (var device in deviceList) 
      { 
       checkBoxListItems.Add(new CheckBoxListItem() 
       { 
        ID = device.DevId, 
        Display = device.DevType, 
        IsChecked = false //On the create view, no devices are selected by default 
       }); 
      } 

      customervm.Devices = checkBoxListItems; 
      return View(customervm); 
     } 
    } 


    [HttpPost] 
    [ValidateAntiForgeryToken] 
    public ActionResult Create(CustomerDeviceFormViewModel vm) 
    { 
     if (ModelState.IsValid) 
     { 
      foreach (var deviceId in vm.Devices.Where(x => x.IsChecked).Select(x => x.ID)) 
      { 
       var customerDevices = new CustomerDevice 
       { 
        CustId = vm.CustId, 
        DevId = deviceId 
       }; 

       db.CustomerDevices.Add(customerDevices); 
      } 

      db.SaveChanges(); 
      return RedirectToAction("Index"); 
     } 

     return View(vm); 
    } 


    public ActionResult Edit(int? id) 
    { 
     Customer customer = db.Customers.SingleOrDefault(c => c.CustId == id); 
     if (customer == null) 
     { 
      return NotFound(); 
     } 
     // Get all devices 
     var deviceList = db.Devices.ToList(); 
     // Get the selected device ID's for the customer 
     IEnumerable<int> selectedDevices = db.CustomerDevices 
      .Where(x => x.CustId == id).Select(x => x.DevId); 
     // Build view model 
     var model = new CustomerDeviceFormViewModel() 
     { 
      CustId = customer.CustId, 
      CustDisplayName = customer.CustDisplayName, 
      Devices = deviceList.Select(x => new CheckBoxListItem() 
      { 
       ID = x.DevId, 
       Display = x.DevType, 
       IsChecked = selectedDevices.Contains(x.DevId) 
      }).ToList() 
     }; 
     return View(model); 
    } 


    [HttpPost] 
    [ValidateAntiForgeryToken] 
    public ActionResult Edit(CustomerDeviceFormViewModel vmEdit) 
    { 
     if (ModelState.IsValid) 
     { 
      Customer customer = db.Customers 
         .Include(c => c.CustomerDevices) 
         .SingleOrDefault(c => c.CustId == vmEdit.CustId); 

      if (customer == null) 
      { 
       return NotFound(); 
      } 

      IEnumerable<int> selectedDevices = vmEdit.Devices.Where(x => x.IsChecked).Select(x => x.ID); 

      // Remove all selected devices for this customer 
      foreach (int removeId in selectedDevices) 
      { 
       customer.CustomerDevices.Clear(); 
      } 


      // Add the new selected devices 
      foreach (int deviceId in selectedDevices) 
      { 
       CustomerDevice customerDevice = new CustomerDevice 
       { 
        CustId = customer.CustId, 
        DevId = deviceId 
       }; 
       customer.CustomerDevices.Add(customerDevice);   
      } 

      // Update the customer 
      db.Customers.Update(customer); //or just db.Update(customer); same thing 
      //        // Save and redirect 
      db.SaveChanges(); 
      return RedirectToAction("Index"); 
     } 

     return View(vmEdit); 
    } 

Edit View

<div class="form-group"> 
    Please select the Devices to assign to <b>@Html.DisplayFor(c => c.CustDisplayName)</b> 
</div> 

<div class="form-group"> 
    @Html.EditorFor(x => x.Devices) 
</div> 

@Html.HiddenFor(c => c.CustId) 

<div class="form-group"> 
    <button type="submit" class="btn btn-primary">Submit</button> 
</div> 

Shared/EditorTemplates/CheckBoxListItem.chtml

<div class="checkbox"> 
<label> 
    @Html.HiddenFor(x => x.ID) 
    @Html.CheckBoxFor(x => x.IsChecked) 
    @Html.LabelFor(x => x.IsChecked, Model.Display) 
</label> 
<br /> 

WebFormContext

public class WebFormContext : DbContext 
{ 
    public WebFormContext(DbContextOptions<WebFormContext> options) 
     : base(options) 
    { } 

    public DbSet<Customer> Customers { get; set; } 
    public DbSet<State> States { get; set; } 
    public DbSet<Device> Devices { get; set; } 
    public DbSet<CustomerDevice> CustomerDevices { get; set; } 

    protected override void OnModelCreating(ModelBuilder modelBuilder) 
    { 
     modelBuilder.Entity<Customer>() 
      .HasKey(c => c.CustId); 

     modelBuilder.Entity<Customer>() 
      .Property(c => c.CustDisplayName) 
      .HasColumnType("varchar(100)") 
      .HasMaxLength(100) 
      .IsRequired(); 

     modelBuilder.Entity<Customer>() 
      .Property(c => c.CustFirstName) 
      .HasColumnType("varchar(50)") 
      .HasMaxLength(50); 

     modelBuilder.Entity<Customer>() 
      .Property(c => c.CustLastName) 
      .HasColumnType("varchar(50)") 
      .HasMaxLength(50); 

     modelBuilder.Entity<Customer>() 
      .Property(c => c.CustCompanyName) 
      .HasColumnType("varchar(50)") 
      .HasMaxLength(50); 

     modelBuilder.Entity<Customer>() 
      .Property(c => c.CustAddress) 
      .HasColumnType("varchar(100)") 
      .HasMaxLength(100) 
      .IsRequired(); 

     modelBuilder.Entity<Customer>() 
      .Property(c => c.CustPhoneNumber) 
      .HasColumnType("varchar(12)") 
      .HasMaxLength(12); 

     modelBuilder.Entity<Customer>() 
      .Property(c => c.CustMobileNumber) 
      .HasColumnType("varchar(12)") 
      .HasMaxLength(12); 

     modelBuilder.Entity<Customer>() 
      .Property(c => c.CustEmailAddress) 
      .HasColumnType("varchar(320)") 
      .HasMaxLength(320); 

     modelBuilder.Entity<Device>() 
      .HasKey(d => d.DevId); 

     modelBuilder.Entity<Device>() 
      .Property(d => d.DevType) 
      .HasColumnType("varchar(50)") 
      .HasMaxLength(50) 
      .IsRequired(); 

     modelBuilder.Entity<CustomerDevice>() 
      .HasKey(cd => new { cd.CustId, cd.DevId }); 
    } 
} 

ответ

0

В основном вы пытаетесь сделать многое для многих из того, что я понимаю.

Сначала вы должны взглянуть на это Many-To-Many. Вам нужно будет обновить свои модели, как в примере, а также добавить отношения вручную в методе OnModelCreating. Также не забудьте добавить DbSet для него в контексте БД.

В Клиенте и модели устройства добавить:

public List<CustomerDevice> CustomerDevices{ get; set; }

В WebFormContext OnModelCreating вы должны иметь:

 modelBuilder.Entity<CustomerDevice>() 
      .HasKey(c => new { c.CustId, c.DevId }); 

     modelBuilder.Entity<CustomerDevice>() 
      .HasOne(cd => cd.Customer) 
      .WithMany(c => c.CustomerDevices) 
      .HasForeignKey(cd => cd.CustId); 

     modelBuilder.Entity<CustomerDevice>() 
      .HasOne(cd => cd.Device) 
      .WithMany(d => d.CustomerDevices) 
      .HasForeignKey(cd => cd.DevId); 

После этого вы можете использовать, включают жадно загружать связанные объекты (в Редактировать например, действие) (see more about that):

Customer customer = db.Customers 
        .Include(c => c.CustomerDevices) 
        .SingleOrDefault(c => c.CustId == id); 

Затем вы должны выполнять операции только с отдельным пользователем.

customer.CustomerDevices.Add(...); 
customer.CustomerDevices.Remove(...); 
//edit 

После этого сохраните изменения в ваш клиент

db.Customers.Update(customer); 
db.SaveChanges(); 

В вашем случае вы можете сделать что-то вроде этого:

public ActionResult Edit(CustomerDeviceFormViewModel vmEdit) 
{ 
    if (ModelState.IsValid) 
    { 
     Customer customer = db.Customers 
        .Include(c => c.CustomerDevices) 
        .SingleOrDefault(c => c.CustId == id); 

     if (customer == null) 
     { 
      return NotFound(); 
     } 

     IEnumerable<int> selectedDevices = vmEdit.Devices.Where(x => x.IsChecked).Select(x => x.ID); 

     // Add the new selected devices 
     foreach (int deviceId in selectedDevices) 
     { 
      var customerDevice = customer.CustomerDevices.FirstOrDefault(cd => cd.DevId == deviceId); 
      if(customerDevice != null) 
      { 
       customer.CustomerDevices.Remove(customerDevice); 
      } 
      else 
      { 
       CustomerDevice customerDevice = new CustomerDevice 
       { 
        CustId = customer.Id, 
        DevId = deviceId 
       }; 
       customer.CustomerDevices.Add(customerDevice); 
      } 
     } 

     // Update the customer 
     db.Customers.Update(customer); //or just db.Update(customer); same thing 
     // Save and redirect 
     db.SaveChanges(); 
     return RedirectToAction("Index"); 
    } 

    return View(vmEdit); 
} 
+0

Я редактировал свой пост, чтобы включить мой ** Метод OnModelCreating * *. Я не уверен, как или с нетерпением загружать связанные объекты в свой метод OnModelCreating. Кроме того, клиент Customer = db.Customers .Include (c => c.CustomerDevices) .SingleOrDefault (c => c.CustId == id); 'для метода EditPost? Можете ли вы также помочь мне с 'customer.CustomerDevices.Add (...); 'и' customer.CustomerDevices.Remove (...); '// edit –

+0

См. Мое обновленное сообщение. Вы по-прежнему должны изменить свои модели, добавив список CustomerDevices как в Customer, так и в Device, и вы пропустили многие из многих отношений в OnModelCreating. Я также добавил пример того, как вы можете реализовать действие редактирования. – korn3l

+0

В качестве побочного примечания вы выбрали очень плохой идентификатор модели. По соглашению свойство Id или Id будет настроено как ключ объекта. В противном случае вы должны настроить это самостоятельно. См. Https://docs.efproject.net/ru/latest/modeling/keys.html – korn3l

Смежные вопросы