使用 Asp.Net MVC 5 框架和 IoC 容器时如何构建视图模型的实例?

How to construct an instance of a view-model when using Asp.Net MVC 5 framework and IoC container?

我正在使用 ASP.NET MVC 5 框架和 Unity-Container 编写一个项目来处理我的依赖项。

我正在寻找一种最佳实践来处理在 Asp.Net MVC 5 应用程序中使用 IoC 容器时构建视图模型。我希望这个问题不被视为基于意见并被关闭。

我有以下视图模型class

public class CategoryReportViewModel
{
    public IUnitOfWork UnitOfWork { get; set; }
    public IEnumerable<object> Records { get; set; }
    // ....

    public TasksSummaryByProductViewModel(IUnitOfWork unitOfWork)
    {
        UnitOfWork = unitOfWork;
    }

    public void SetFilters()
    {
        // Do something.....
        UnitOfWork.....
    }

    public void SetData()
    {
        // Do something.....
        Records = UnitOfWork.....
    }
}

如您所见,我的 CategoryReportViewModel class 需要一个 IUnitOfWork 的实例来允许我的方法执行它们的任务 "unless I manually pass the IUnitOfWork to every method that needs it." 我想知道什么是新建 CategoryReportViewModel 实例的正确方法。是通过 IoC 容器还是通过从构造函数传递 IUnitOfWork 的实例来使用旧方法?

换句话说,使用我的视图模型的正确方法是什么?

以下是尝试处理这种情况时想到的方法,但哪种方法是正确的?也许还有其他选择?

一、老手新涨攻略

public class ReportsController : Controller
{
    protected IUnitOfWork UnitOfWork { get; set; }

    public ReportsController(IUnitOfWork unitOfWork)
    {
        UnitOfWork = unitOfWork;
    }

    [HttpGet]
    public ActionResult CategoryByName()
    {
        var viewModel = new CategoryReportViewModel(UnitOfWork);

        viewModel.SetFilters();

        return View(viewModel);
    }

    [HttpPost, ValidateAntiForgeryToken]
    public ActionResult CategoryByName(CategoryReportViewModel viewModel)
    {
        viewModel.UnitOfWork = UnitOfWork; // This is ugly
        if (ModelState.IsValid)
        {
            viewModel.SetData();
        }

        viewModel.SetFilters();

        return View(viewModel);
    }
}

其次,手动新建,但将 UnitOfWork 传递给任何需要它的方法

public class ReportsController : Controller
{
    protected IUnitOfWork UnitOfWork { get; set; }

    public ReportsController(IUnitOfWork unitOfWork)
    {
        UnitOfWork = unitOfWork;
    }

    [HttpGet]
    public ActionResult CategoryByName()
    {
        var viewModel = new CategoryReportViewModel();

        viewModel.SetFilters();

        return View(viewModel);
    }

    [HttpPost, ValidateAntiForgeryToken]
    public ActionResult CategoryByName(CategoryReportViewModel viewModel)
    {
        if (ModelState.IsValid)
        {
            viewModel.SetData(UnitOfWork);
        }

        viewModel.SetFilters(UnitOfWork);

        return View(viewModel);
    }
}

三、注册CategoryReportViewModel后使用IoC容器。但这将需要我修改 Asp.Net MVC 5 默认行为,因为我的视图模型 may/does 没有默认构造函数。

public class ReportsController : Controller
{
    [HttpGet]
    public ActionResult CategoryByName()
    {
        var viewModel = DependencyResolver.Current.GetService<CategoryReportViewModel>();
        viewModel.SetFilters();

        return View(viewModel);
    }

    // This will now work since I my view-model does not have a default constructor
    // unless I override the `DefaultModelBinder` class somehow to resolve the class
    // from the IoC container
    [HttpPost, ValidateAntiForgeryToken]
    public ActionResult CategoryByName(CategoryReportViewModel viewModel)
    {
        if (ModelState.IsValid)
        {
            viewModel.SetData();
        }

        viewModel.SetFilters();

        return View(viewModel);
    }
}

在这方面有不同方法和意见的空间,但我发现最有效的模式是使 ViewModel 成为 "dumb" DTO,其属性由控制器创建和填充。将服务注入控制器,并在您的操作方法中包含所有业务逻辑。

换句话说,SetFilters()SetData()应该是在控制器上,并以模型为参数。

public class ReportsController : Controller
{
    protected IUnitOfWork UnitOfWork { get; set; }

    public ReportsController(IUnitOfWork unitOfWork)
    {
        UnitOfWork = unitOfWork;
    }

    [HttpGet]
    public ActionResult CategoryByName()
    {
        var viewModel = new CategoryReportViewModel();

        this.SetFilters(viewModel);

        return View(viewModel);
    }

    [HttpPost, ValidateAntiForgeryToken]
    public ActionResult CategoryByName(CategoryReportViewModel viewModel)
    {
        if (ModelState.IsValid)
        {
            this.SetData(viewModel);
        }

        this.SetFilters(viewModel);

        return View(viewModel);
    }

    private void SetFilters(CategoryReportViewModel viewModel)
    {
        // Do something.....
        UnitOfWork.....
    }

    private void SetData(CategoryReportViewModel viewModel)
    {
        // Do something.....
        Records = UnitOfWork.....
    }
}