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

时间:2019-04-04 18:24:46

标签: asp.net-mvc entity-framework

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

我是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).

2 个答案:

答案 0 :(得分:0)

将实体传递给视图不是一个好习惯,根据从视图返回它们时您对它们的处理方式,这可能会使您遭受数据篡改。您的“ TableViewModel”应该只包含来自来宾和联系人的扁平字段,或者包含只显示您需要显示/编辑的键和详细信息的GuestViewModel和ContactViewModel。实体被设计为与DbContext关联。在视图模型中放置对它们的引用会使它们孤立。当您将它们传递回控制器时,它们只是从视图产生的JSON数据反序列化的POCO实例。它们没有更改跟踪等。从DbContext刚加载它们时,您可能会期望使用实体。您可以将它们附加到DbContext,但是必须手动将实体状态设置为“已修改”,否则上下文不知道实体已更改。

您目前遇到的问题可能是您的SaveGuest方法正在做什么。

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

查看:

  • 从上下文中加载实体
  • 填充视图模型
  • 查看。

更新:

  • 针对当前会话验证视图模型
  • 根据键从上下文中加载实体
  • 检查视图模型是否陈旧(上次修改日期/时间戳记/行版本匹配)
  • 仅验证和复制可从视图模型更新到实体的详细信息
  • 保存更改。

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

答案 1 :(得分:0)

我通过替换

解决了我的问题
@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; }
    }

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