如何修复局部视图的视图模型

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何修复局部视图的视图模型相关的知识,希望对你有一定的参考价值。

我在SQLServer上为空表创建新记录时遇到问题当尝试传递新记录时,我得到“对象引用未设置为对象的实例”。错误当我尝试编辑现有记录时,表格正确显示内容,但保存更改将不起作用。使用Guests表,它只会重新加载旧条目而不进行更改,而对于Contacts,它会返回与创建新记录时相同的错误。应该通过显示每个表的部分视图编辑器表单来创建和编辑记录。

我是MVC的初学者。

以下是我的代码。

表模型:


    [Table("GuestsTest")]
        public class Guest
        {
            [Key]
            [HiddenInput(DisplayValue = false)]
            public int GuestID { get; set; }        
            public string GuestLastName { get; set; }
            public string GuestFirstName { get; set; }
            public string GuestMiddleName { get; set; }
            public string GuestEmail { get; set; }
            public string GuestSex { get; set; }
        }

        [Table("ContactsTest")]
        public class Contact
        {        
            [Key]
            [HiddenInput(DisplayValue = false)]
            public int ContactID { get; set; }
            [HiddenInput(DisplayValue = false)]
            public int GuestID { get; set; }
            public int PostalCode { get; set; }
            public string City { get; set; }
            public string Street { get; set; }
            public string HouseNumber { get; set; }
            public string PhoneNumber { get; set; }

My view model


    public class TableViewModel
        {
            public Guest GetGuest { get; set; }
            public Contact GetContact { get; set; }
        }


My controllers 

    public class AdminController : Controller
        {
            private IGuestRepository guestRepository;
            private IContactRepository contactRepository;
            private IQRCodeRepository qrcodeRepository;

            public AdminController(IGuestRepository repoG, IContactRepository repoC, IQRCodeRepository repoQ)
            {
                guestRepository = repoG;
                contactRepository = repoC;
                qrcodeRepository = repoQ;
            }

            public ActionResult Index()
            {            
                return View(guestRepository.Guests);
            }        

            public ActionResult EditGuest(int? id)
            {
                if (id == null)
                {
                    return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
                }
                TableViewModel viewModel = new TableViewModel();
                viewModel.GetGuest = guestRepository.Guests.FirstOrDefault(g => g.GuestID == id);
                viewModel.GetContact = contactRepository.Contacts.FirstOrDefault(c => c.ContactID == id);
                if (viewModel.GetGuest == null)
                {
                    return HttpNotFound();
                }
                return View(viewModel);
            }

            public ActionResult GuestForm(int? id)
            {
                var viewModel = new TableViewModel();
                viewModel.GetGuest = guestRepository.Guests.FirstOrDefault(g => g.GuestID == id);
                return PartialView("_GuestForm", viewModel.GetGuest);
            }

            [HttpPost]
            public ActionResult GuestForm(TableViewModel getGuest)
            {            
                if (ModelState.IsValid)
                {                
                    guestRepository.SaveGuest(getGuest.GetGuest);
                    qrcodeRepository.CreateQRCode(getGuest.GetGuest);
                    TempData["message"] = string.Format("Zapisano {0} {1}", getGuest.GetGuest.GuestFirstName, getGuest.GetGuest.GuestLastName);
                    return RedirectToAction("EditGuest/" + getGuest.GetGuest.GuestID);
                }
                else
                {                     
                    return PartialView(getGuest.GetGuest);
                }
            }

            public ActionResult ContactForm(int? id)
            {
                var viewModel = new TableViewModel();
                viewModel.GetContact = contactRepository.Contacts.FirstOrDefault(c => c.ContactID == id);
                return PartialView("_ContactForm", viewModel.GetContact);
            }

            [HttpPost]
            public ActionResult ContactForm(TableViewModel getGuest)
            {
                if (ModelState.IsValid)
                {
                    contactRepository.SaveContact(getGuest.GetContact);
                    TempData["message"] = string.Format("Zapisano {0} {1}", getGuest.GetGuest.GuestFirstName, getGuest.GetGuest.GuestLastName);
                    return RedirectToAction("EditGuest/" + getGuest.GetGuest.GuestID);
                }
                else
                {
                    return PartialView(getGuest.GetContact);
                }
            }

            public ActionResult Create()
            {
                return View("EditGuest", new TableViewModel());
            }


My view

    @model MSConference.WebUI.Models.TableViewModel

    @{
        if (Model.GetGuest.GuestEmail == null)
        {
            ViewBag.Title = "Tworzenie nowego użytkownika";
        }
        else
        {
            ViewBag.Title = "Edycja";
        }
        Layout = "~/Views/Shared/_AdminLayout.cshtml";
    }

    @if (Model.GetGuest.GuestEmail == null)
    {
        <h2>Tworzenie nowego użytkownika</h2>
    }
    else
    {
        <h2>Edycja - @Model.GetGuest.GuestFirstName @Model.GetGuest.GuestLastName</h2>
    }


    @using (Html.BeginForm("EditGuest", "Admin"))
    {
        @Html.AntiForgeryToken()
        <div class="container">
            <ul class="nav nav-pills">
                <li class="active"><a data-toggle="pill" href="#EditGuest">Edycja - Gość</a></li>
                <li><a data-toggle="pill" href="#EditContact">Edycja - Kontakt</a></li>
                <li><a data-toggle="pill" href="#EditBill">Edycja - Rezerwacja</a></li>
                <li><a data-toggle="pill" href="#EditPlan">Edycja - Konferencja</a></li>
            </ul>
            <div class="tab-content">
                <div id="EditGuest" class="tab-pane fade in active">@Html.Partial("_GuestForm", new MSConference.WebUI.Models.TableViewModel())</div>
                <div id="EditContact" class="tab-pane fade">@Html.Partial("_ContactForm", new MSConference.WebUI.Models.TableViewModel())</div>
                <div id="EditBill" class="tab-pane fade">sgdg</div>
                <div id="EditPlan" class="tab-pane fade">gsdgsgsgsg</div>
            </div>
        </div>    
     }

            <div>
                @Html.ActionLink("Powrót do Listy", "Index", null, new { @class = "btn btn-success" })
            </div>


I tried every method of passing model I could find and understand

EDIT

Here are my Repositories. Create error comes from if (contact.ContactID == 0)

    public class EFGuestRepository : IGuestRepository
        {
            private EfDbContext context = new EfDbContext();

            public IEnumerable<Guest> Guests
            {
                get { return context.Guests; }
            }

            public void SaveGuest(Guest guest)
            {
                if (guest.GuestID == 0)
                {
                    context.Guests.Add(guest);
                }
                else
                {
                    Guest dbEntry = context.Guests.Find(guest.GuestID);
                    if (dbEntry != null)
                    {
                        dbEntry.GuestLastName = guest.GuestLastName;
                        dbEntry.GuestFirstName = guest.GuestFirstName;
                        dbEntry.GuestMiddleName = guest.GuestMiddleName;
                        dbEntry.GuestEmail = guest.GuestEmail;
                        dbEntry.GuestSex = guest.GuestSex;
                    }
                }
                context.SaveChanges();
            }
            public Guest DeleteGuest(int guestId)
            {
                Guest dbEntry = context.Guests.Find(guestId);
                if (dbEntry != null)
                {
                    context.Guests.Remove(dbEntry);
                    context.SaveChanges();
                }
                return dbEntry;
            }
        }

        public class EFContactRepository : IContactRepository
        {
            private EfDbContext context = new EfDbContext();

            public IEnumerable<Contact> Contacts
            {
                get { return context.Contacts; }
            }

            public void SaveContact(Contact contact)
            {
                if (contact.ContactID == 0)
                {
                    contact.GuestID = contact.ContactID;
                    context.Contacts.Add(contact);
                }
                else
                {
                    Contact dbEntry = context.Contacts.Find(contact.ContactID);
                    if (dbEntry != null)
                    {
                        contact.GuestID = contact.ContactID;
                        dbEntry.PostalCode = contact.PostalCode;
                        dbEntry.City = contact.City;
                        dbEntry.Street = contact.Street;
                        dbEntry.HouseNumber = contact.HouseNumber;
                        dbEntry.PhoneNumber = contact.PhoneNumber;
                    }
                }
                context.SaveChanges();
            }

            public Contact DeleteContact(int guestId)
            {
                Contact dbEntry = context.Contacts.Find(guestId);
                if (dbEntry != null)
                {
                    context.Contacts.Remove(dbEntry);
                    context.SaveChanges();
                }
                return dbEntry;
            }


    public interface IGuestRepository
        {
            IEnumerable<Guest> Guests { get; }

            void SaveGuest(Guest guest);

            Guest DeleteGuest(int guestId);
        }

        public interface IContactRepository
        {
            IEnumerable<Contact> Contacts { get; }

            void SaveContact(Contact guest);

            Contact DeleteContact(int guestId);
        }

I've built whole project working with Adam Freeman pro asp.net mvc 5 book (SportsStore project).
答案

将实体传递给视图不是一个好习惯,并且根据您从视图返回时对它们执行的操作,这可能会使您面临数据篡改。您的“TableViewModel”应该只包含来自guest虚拟机和联系人的展平字段,或者GuestViewModel和ContactViewModel,仅显示您需要显示/编辑的密钥和详细信息。实体被设计为与DbContext相关联。在视图模型中引用它们是孤立它们。当您将它们传递回控制器时,它们将成为仅从视图中的JSON数据反序列化的POCO实例。它们没有更改跟踪等,您可能期望在从DbContext新加载时使用实体。您可以将它们附加到DbContext,但是您必须手动将实体State设置为“Modified”,否则上下文不知道实体已被更改。

您现在面临的问题可能在于您的SaveGuest方法正在执行的操作。

数据的典型MVC生命周期大致如下:

视图:

  • 从上下文加载实体
  • 填充视图模型
  • 传递给视图。

更新:

  • 针对当前会话验证视图模型
  • 基于键从上下文加载实体
  • 检查视图模型是否过时(最后的模态日期/时间戳/行版本匹配)
  • 仅验证和复制可从视图模型更新为实体的详细信息
  • 保存更改。

如果您没有看到更改,则可能是您将实体附加到新上下文而不将实体State设置为“已修改”。请注意,建议不要这样做,因为您无条件地信任来自客户端的数据。例如,您可能只想看到用户修改了您为其创建控件的数据,但是通过附加实体,您可以打开门,以便截取或使用实体上的任何/所有数据回放POST调用。改变。您无论如何都需要加载现有实体,以验证不应更改任何不应更改的内容。另一种可能性是你可能在没有意识到的情况下重新加载实体,在调用SaveChanges之前没有从视图模型的实体复制值,或者将实体添加到上下文中,认为它会更新现有行,但是它正在保存一个全新的行新的PK。

另一答案

我通过替换解决了我的问题

@Html.Partial("PartialView", Model)

使用@ {Html.RenderPartial(“PartialView”,Model);}

我也重建了我的模型

现在我的实体模型看起来像这样:

[Table("GuestsTest")]
    public class Guest
    {
        [Key]
        public int GuestID { get; set; }
        public string GuestLastName { get; set; }
        public string GuestFirstName { get; set; }
        public string GuestMiddleName { get; set; }
        public string GuestEmail { get; set; }
        public string GuestSex { get; set; }

        [Required]
        public virtual Contact Address { get; set; }
    }

    [Table("ContactsTest")]
    public class Contact
    {                
        public int ContactID { get; set; }
        [Key, ForeignKey("Guest")]
        public int GuestID { get; set; }
        public int PostalCode { get; set; }
        public string City { get; set; }
        public string Street { get; set; }
        public string HouseNumber { get; set; }
        public string PhoneNumber { get; set; }

        public virtual Guest Guest { get; set; }
    }

我的视图模型得到了完全重建:

public class TableViewModel
    {
        public GuestViewModel GetGuest { get; set; }
        public ContactViewModel GetContact { get; set; }
    }

    public class GuestViewModel
    {
        [Key]
        [HiddenInput(DisplayValue = false)]
        public int? GuestID { get; set; }

        [MaxLength(50)]
        [Required(ErrorMessage = "Proszę podać nazwisko.")]
        [Display(Name = "Nazwisko")]
        public string GuestLastName { get; set; }

        [MaxLength(50)]
        [Required(ErrorMessage = "Proszę podać imię.")]
        [Display(Name = "Imię")]
        public string GuestFirstName { get; set; }

        [MaxLength(50)]
        [Display(Name = "Drugie imię")]
        public string GuestMiddleName { get; set; }

        [MaxLength(50)]
        [Required(ErrorMessage = "Proszę podać adres email.")]
        [RegularExpression(".+\@.+\..+", ErrorMessage = "Proszę podać prawidłowy adres e-mail.")]
        [Display(Name = "Email")]
        public string GuestEmail { get; set; }

        [MaxLength(1)]
        [Required(ErrorMessage = "Proszę podać płeć.")]
        public string GuestSex { get; set; }

    }

    public class ContactViewModel
    {
        [HiddenInput(DisplayValue = false)]
        public int ContactID { get; set; }

        [Key, ForeignKey("Guest")]
        [HiddenInput(DisplayValue = false)]
        public int GuestID { get; set; }

        [Required(ErrorMessage = "Proszę podać kod pocztowy.")]
        [Display(Name = "Kod pocztowy")]
        public int PostalCode { get; set; }

        [Required(ErrorMessage = "Proszę podać Miejscowość.")]
        [Display(Name = "Miejscowość")]
        public string City { get; set; }

        [Required(ErrorMessage = "Proszę podać ulicę.")]
        [Display(Name = "Ulica")]
        public string Street { get; set; }

        [Required(ErrorMessage = "Proszę podać numer domu/mieszkania.")]
        [Display(Name = "Numer domu/mieszkania")]
        public string HouseNumber { get; set; }

        [Required(ErrorMessage = "Proszę podać numer telefonu.")]
        [Display(Name = "Numer telefonu")]
        public string PhoneNumber { get; set; }
    }

最后,我重载了我的保存功能以使用新模型

以上是关于如何修复局部视图的视图模型的主要内容,如果未能解决你的问题,请参考以下文章

如何在作为选项卡的 2 个片段之间共享视图模型

我如何使用视图模型从另一个片段访问函数

如何对绑定到 mvc 中模型的相同属性的多个局部视图应用验证?

如何在 Html:encode 中从模型调用数组到局部视图

片段的视图模型

片段的视图模型而不是访问活动视图模型?