c# - 使用带有单元测试的 ViewModel 进行 CRUD 编辑操作

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

首先:我找到了this post ,但我不完全理解它,所以请不要将其锁定为重复项。

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

我的问题是由于某种原因添加一个新行到表中而不是编辑它。在我进行测试之前,这一切都有效。

我相信我错过了一些愚蠢的事情,但我不知道我做错了什么。

如果有什么区别的话,我正在使用code-first

我的 View 模型:

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

我的 Controller 功能:

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

最佳答案

在 POST 方法中,您从未设置图书的 Id 属性的值,因此它始终为 0(int 的默认值)因此,您总是执行代码来添加新的 Book

您首先需要在 View 模型中包含 id 的属性,以便将其值绑定(bind)在 POST 方法中。

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

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

然后在POST方法中,根据 View 模型设置BookId

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. 您的数据模型通常会包含不应包含在中的属性 View (例如,指示记录日期的属性 以及由谁创建)。创建数据模型的新实例并 保存它意味着这些属性将被覆盖并设置为 它们的默认值。
  2. 您可以进行并发检查,例如您可以检查 TIMESTAMP 值,如果它们不同,您就知道另一个 用户同时修改了记录(您可能需要 采取不同的行动方针,而不是仅仅覆盖以前的行动方针 使用更改)

关于c# - 使用带有单元测试的 ViewModel 进行 CRUD 编辑操作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47916275/

相关文章:

c# - 有人可以纠正我对 ViewDataDictionary 工作原理的理解吗?

ios - ASP.NET MVC Web 应用程序中的 Bing map 在移动 View 中无法正确显示

asp.net-mvc - ASP.NET MVC2 - 模型在一个 View 页面上绑定(bind)多个数据源 - 选项?

c# - 如何确保一个类可以调用另一个类的方法,但其他类不能调用该方法?

java - Android 共享 Intent - 某些文件不会共享

asp.net - 您是否预编译了网站,为什么/为什么不呢?

c# - 在执行任何操作之前需要在控件上单击动态创建的链接两次吗?

c# - 具有自定义日期格式: works on windows but not on raspbian的DateTime.ParseExact

c# - 在Windows服务中通过网络访问共享文件夹

c# - ASP.Net/C# - 缺少程序集引用