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

时间:2017-12-21 00:36:11

标签: c# asp.net asp.net-mvc unit-testing

首先:我找到了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;
    }
}

ILibraryRepository:

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

1 个答案:

答案 0 :(得分:2)

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

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

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

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

然后在POST方法中,根据视图模型设置Id的{​​{1}}

Book

但是,编辑现有记录时的正确方法是根据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); 实例。这种方法的一些好处包括

  1. 您的数据模型通常包含不应包含的属性 视图(例如,用于指示记录日期的属性) 创造,由谁)。创建数据模型的新实例 保存它意味着这些属性将被覆盖并设置为 他们的默认值。
  2. 您可以进行并发检查,例如您可以查看 Book值,如果它们不同,则您知道另一个 用户在此期间修改了记录(你可能会采取一个 不同的行动方式,而不仅仅是覆盖以前 使用变化)