在ViewModel VS中实例化DbContext传递控制器的实例

时间:2015-12-06 20:59:53

标签: asp.net-mvc entity-framework viewmodel dbcontext

我有一个编辑ViewModel来完成一些数据库工作。

我采取哪些方法会有什么不同?

  1. 将控制器的DBContext实例传递给ViewModel(下面的示例1)
  2. 在ViewModel本身内创建一个新的DBContext实例(下面的示例2)
  3. 如果我传入控制器的DBContext实例,这意味着将在某个时刻调用.Dispose()(假设控制器中有.Dispose()方法)。通过在ViewModel方法本身中创建DBContext实例,这种情况永远不会发生。那有关系吗?

    示例1:

    public class ModelOne
    {
        public int ID { get; set; }
        public string PropA { get; set; }
        public string PropB { get; set; }
        public string PropC { get; set; }
        // etc...
    }
    
    
    public class EditModelOnePropAViewModel
    {
        public EditModelOnePropAViewModel(ApplicationDbContext db, int id)
        {
            ID = id;
            PropA = db.ModelOneDbSet
                .Where(i => i.ID == id)
                .Select(i => i.PropA)
                .FirstOrDefault();
        }
    
        public void SaveChanges(ApplicationDbContext db)
        {
            var modelOne = db.ModelOneDbSet.FirstOrDefault(i => i.ID == ID);
            modelOne.PropA = PropA;
            db.SaveChanges();
        }
    
        public string PropA { get; set; }
        public int ID { get; set; }
    }
    
    
    public class ControllerOne : Controller
    {
        private ApplicationDbContext DB = new ApplicationDbContext() { };
    
        [HttpGet]
        public ActionResult Edit(int id)
        {
            var viewModel = new EditModelOnePropAViewModel(DB, id);
            return View(viewModel);
        }
        [HttpPost]
        public ActionResult Edit(EditModelOnePropAViewModel postedModel)
        {
            if (ModelState.IsValid)
            {
                postedModel.SaveChanges(DB);
                return RedirectToAction("index");
            }
            return View(postedModel);
        }
    }
    

    示例2:

    public class ModelTwo
    {
        public int ID { get; set; }
        public string PropA { get; set; }
        public string PropB { get; set; }
        public string PropC { get; set; }
        // etc...
    }
    
    
    public class EditModelTwoPropAViewModel
    {
        public EditModelTwoPropAViewModel(int id)
        {
            using (var db = new ApplicationDbContext())
            {
                ID = id;
                PropA = db.ModelTwoDbSet
                    .Where(i => i.ID == id)
                    .Select(i => i.PropA)
                    .FirstOrDefault();
            }
        }
    
        public void SaveChanges()
        {
            using (var db = new ApplicationDbContext())
            {
                var modelTwo = db.ModelTwoDbSet.FirstOrDefault(i => i.ID == ID);
                modelTwo.PropA = PropA;
                db.SaveChanges();
            }
    
        }
        public string PropA { get; set; }
        public int ID { get; set; }
    }
    
    public class ControllerTwo : Controller
    {
        [HttpGet]
        public ActionResult Edit(int id)
        {
            var viewModel = new EditModelTwoPropAViewModel(id);
            return View(viewModel);
        }
        [HttpPost]
        public ActionResult Edit(EditModelTwoPropAViewModel postedModel)
        {
            if (ModelState.IsValid)
            {
                postedModel.SaveChanges();
                return RedirectToAction("index");
            }
            return View(postedModel);
        }
    }
    

2 个答案:

答案 0 :(得分:4)

查看模型应该是简单的POCO。具有视图所需属性的类。没有其他的。

public class CustomerViewModel
{
  public int Id {set;get;}
  public string FirstName {set;get;}
  public string LastName {set;get;}
}

在你的控制器中,

public ActionResult Edit(int id)
{
  using(var db=new YourDbContext())
  {
     var c= db.Customers.FirstOrDefault(s=>s.Id==id);
     if(c!=null)
     {
       var vm= new CustomerViewModel { Id=id, 
                                       FirstName=c.FirstName,
                                       LastName=c.LastName
                                     };
      return View(vm);
     }
  }
  return View("NotFound");
}

更好的方法是在数据访问层上创建抽象,这样您的控制器代码就不会知道您正在使用哪种数据访问技术。这将帮助您对控制器操作进行单元测试。

所以基本上你会创建一个这样的抽象

public interface ICustomerRepository
{
  CustomerDto GetCustomer(ind id);
}
public class EFCustomerRepository : ICustomerRepository
{
  public CustomerDto GetCustomer(int id)
  {
      using(var db=new YourDbContext())
      {
         var c= db.Customers.FirstOrDefault(s=>s.Id==id);
         if(c!=null)
         { 
          return new CustomerDto { Id=id, FirstName = c.FirstName };
         }
      }
      return null;
  }
}

假设您有一个名为CustomerDto的类,它是一个表示客户实体的数据结构,并且可以访问Web代码和数据访问代码(您可以保留在Common Project中并添加对该引用的引用在两个项目中)

在您的控制器中,您将使用ICustomerRepository的实现

public CustomerController : Controller
{
  ICustomerRepository repo;
  public CustomerController(ICustomerRepository repo)
  {
    this.repo =repo;
  }
  // You will use this.repo in your action methods now.
}

这将帮助您在单元测试中使用假的ICustomerRepository实现。你可以使用像Moq / FakeItEasy这样的模拟库来做到这一点。

您可以使用Unity,StructureMap或Ninject等依赖注入框架在您的应用中注入界面的具体实现。

答案 1 :(得分:0)

ViewModel是MVVM,而不是MVC。 MVC中的Model与MVVM中的Model相同。该模式称为模型 -View-Viewmodel

在MVC中,控制器负责页面流。没有其他的。 DbContext与页面流有 nothing

模型负责业务逻辑。 DbContext有很多与业务逻辑有关。如果要使用靠近表示层的数据库,Model应该创建DbContext。

您的控制器示例2优于Shyju提出的示例。现在,您可以测试页面流,而无需使用DbContext。由于DbContext与页面流无关,因此更有意义。

一些挑剔:POCO不是具有公共属性且没有逻辑的对象。它是一个不依赖于任何特定于框架的类(例如,要实现的特定接口)。