使用具有单元测试的ViewModel进行CRUD编辑操作

问题描述 投票:2回答:1

首先:我找到了this post,但我并不完全理解它,所以请不要把它锁定为副本。

我正在尝试使用ViewModel进行编辑操作。

我的问题是由于某种原因adds一个新的行到桌子而不是editing它。在我进行测试之前,这一切都已经过去了。

我相信这是我想念的傻事,但我不知道我做错了什么。

如果它有任何区别,我正在使用code-first

我的ViewModel:

public class CreateViewModel
{
    public string Title { get; set; }

    [Display(Name = "Author")]
    public int AuthorId { get; set; }
    public DateTime? PublicationDate { get; set; }
    public float? Edition { get; set; }
    public SelectList Authors { get; set; }
}

我的控制器功能:

    // GET: Books/Edit/5
    public ViewResult Edit(int? id)
    {
        if (id == null)
        {
            return View("Error");
        }
        Book book = db.Books.FirstOrDefault(a => a.Id == id);

        var vm = new CreateViewModel()
        {
            AuthorId = book.AuthorId,
            Authors = new SelectList(db.Authors, "Id", "Name"),
            PublicationDate = book.PublicationDate,
            Title = book.Title,
            Edition = book.Edition
        };

        if (book == null)
        {
            return View("Error");
        }
        return View(vm);
    }

    // POST: Books/Edit/5
    // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
    // more details see https://go.microsoft.com/fwlink/?LinkId=317598.
    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Edit(CreateViewModel vm)
    {
        if (ModelState.IsValid)
        {
            var repo = new EFLibraryRepository();
            repo.Save(new Book(){
                AuthorId = vm.AuthorId,
                PublicationDate = vm.PublicationDate,
                Title = vm.Title,
                Edition = vm.Edition
            });
            return RedirectToAction("Index");
        }
        return View("Edit", vm);
    }

我的模拟存储库:

public class EFLibraryRepository : ILibraryRepository
{
    AuthorAndBookDbModel db = new AuthorAndBookDbModel();
    public IQueryable<Author> Authors { get { return db.Authors; } }

    public IQueryable<Book> Books { get { return db.Books; } }

    public void Delete(Book book)
    {
        db.Books.Remove(book);
        db.SaveChanges();
    }

    public void Delete(Author author)
    {
        db.Authors.Remove(author);
        db.SaveChanges();
    }

    public Book Save(Book book)
    {
        if (book.Id == 0)
        {
            db.Books.Add(book);
        }
        else
        {
            db.Entry(book).State = System.Data.Entity.EntityState.Modified;
        }
        db.SaveChanges();
        return book;
    }
    public Author Save(Author author)
    {
        if (author.Id == 0)
        {
            db.Authors.Add(author);
        }
        else
        {
            db.Entry(author).State = System.Data.Entity.EntityState.Modified;
        }
        db.SaveChanges();
        return author;
    }
}

图书馆资料库:

public interface ILibraryRepository
{
    IQueryable<Book> Books { get; }
    IQueryable<Author> Authors { get; }

    Book Save(Book book);
    Author Save(Author author);

    void Delete(Book book);
    void Delete(Author author);
}
c# asp.net asp.net-mvc unit-testing
1个回答
2
投票

在你的POST方法中,你永远不会设置Book的Id属性的值,所以它总是0int的默认值),所以反过来,你总是执行代码来添加一个新的Book

首先需要在视图模型中为id包含一个属性,以便它的值将在POST方法中绑定。

public class CreateViewModel
{
    public int? Id { get; set; } // add this
    ....

请注意,您不需要在视图中包含隐藏输入,假设您使用默认路由(其值将从表单action属性中的路由值绑定)。

然后在POST方法中,根据视图模型设置IdBook

repo.Save(new Book() {
    Id = vm.Id, // add
    AuthorId = vm.AuthorId,
    ....

但是,编辑现有记录时的正确方法是从基于Id的存储库中获取原始数据模型并更新其属性,例如

Book book = db.Books.FirstOrDefault(a => a.Id == vm.Id);
book.AuthorId = vm.AuthorId;
....
repo.Save(book);

而不是创建一个新的Book实例。这种方法的一些好处包括

  1. 您的数据模型通常包含不应在视图中的属性(例如,用于指示创建记录的日期的属性以及由谁执行的属性)。创建数据模型的新实例并保存它意味着这些属性将被覆盖并设置为其默认值。
  2. 您可以进行并发检查,例如,您可以检查TIMESTAMP值,如果它们不同,您知道另一个用户在此期间修改了记录(您可能采取不同的操作过程而不是仅覆盖以前的使用更改)
© www.soinside.com 2019 - 2024. All rights reserved.