ASP.在数据库中编辑CheckBoxList值

本文关键字:CheckBoxList 编辑 数据库 ASP | 更新日期: 2023-09-27 18:15:44

我很难理解如何在[HttpPost]编辑动作方法CustomerDeviceController上更新CheckBoxList值到我的数据库中的CustomerDevice表。

CustomerDeviceController的My Index Action Method显示客户表中的客户列表。我有一个ActionLink标签"编辑"传递CustId值CustomerDeviceController [HttpGet]编辑(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; }
}

视图模型

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

编辑视图

    <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>

共享/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 });
    }
}

ASP.在数据库中编辑CheckBoxList值

从我的理解来看,基本上你是在尝试做一个多对多的编辑。

首先你应该看看这个多对多。您需要像示例中那样更新您的模型,并在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);

之后,你可以使用include来加载相关的对象(例如,在Edit操作中):

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