我有一个应用程序,它是在ASP的顶部使用c#编写的。NET MVC 5框架。我已安装并安装
Unity.Mvc
允许我进行依赖注入的包。
我的一个控制器中有以下代码。虽然这段代码对我来说似乎很干净,但有几件事困扰着我,我正在努力改进。
查看以下内容
FlagsController
您将正确地注意到所有这些必须注入到我的控制器中的讨厌的依赖项。其次,我觉得我的控制器应该只需要依赖FlagService、Flash和Mapper。其余的都是我的ViewModels需要的依赖项,以使它们具有可展示性。
// Here is my current code
public class FlagsController : Controller
{
protected IProductService ProductService;
protected IClientService ClientService;
protected IUserPassport Passport;
protected ITaskFlagService FlagService;
protected IFlashManager Flash;
protected IMapper Mapper;
public FlagsController (
IProductService productService,
IClientService clientService,
IUserPassport passport,
ITaskFlagService flagService,
IFlashManager flash,
IMapper mapper)
{
ProductService = productService;
ClientService = clientService;
Passport = passport;
FlagService = flagService;
Flash = flash;
Mapper = mapper;
}
public ActionResult Index(ListFlagsViewModel viewModel)
{
viewModel.Flags = FlagService.GetPagedRecords(viewModel, 25);
viewModel.SetMenuItems(ProductService, ClientService, Passport);
return View(viewModel);
}
public ActionResult Create()
{
var viewModel = new CreateFlagViewModel();
viewModel.SetMenutItems(ClientService, Passport);
return View(viewModel);
}
[HttpPost, ValidateAntiForgeryToken]
public ActionResult Create(CreateFlagViewModel viewModel)
{
if (ModelState.IsValid)
{
try
{
FlagService.Add(viewModel);
Flash.AddSuccess("New flag has been added.", 3);
return RedirectToAction("Index");
}
catch (Exception e)
{
Flash.AddError(e.Message);
}
}
viewModel.SetMenutItems(ClientService, Passport);
return View(viewModel);
}
public ActionResult Edit(int? id)
{
var flag = FlagService.Get(id);
if (flag == null)
{
return RedirectToAction("Index");
}
var viewModel = Mapper.Map<EditFlagViewModel>(flag);
viewModel.SetMenutItems(ClientService, Passport, flag.ClientId, ProductService);
return View(viewModel);
}
[HttpPost, ValidateAntiForgeryToken]
public ActionResult Edit(EditFlagViewModel viewModel)
{
if (ModelState.IsValid)
{
var flag = FlagService.Get(viewModel.Id);
if (flag == null)
{
return RedirectToAction("Index");
}
TaskFlag updatedFlag = Mapper.Map(viewModel, flag);
FlagService.Update(updatedFlag);
Flash.AddSuccess("Flag has been updated.", 3);
return RedirectToAction("Index");
}
viewModel.SetMenutItems(ClientService, Passport, viewModel.Client.Id, ProductService);
return View(viewModel);
}
public ActionResult Details(int? id)
{
var flag = FlagService.Get(id);
if (flag == null)
{
return RedirectToAction("Index");
}
var viewModel = Mapper.Map<DisplayFlagViewModel>(flag);
return View(viewModel);
}
}
现在,我正试图严格地将视图模型作为数据传输对象。但是为了让我的视图模型将实体模型转换为视图就绪的html表单,它需要一些服务来从数据库中提取数据以用于下拉菜单或其他事情。因此,我的视图模型不会有任何业务逻辑,但它需要调用不同的服务,以便将所有部分放在一起,以便为视图做好准备。为了更好地解释这一点,请看一下我的
CreateFlagViewModel
下面列出的类别。
public class CreateFlagViewModel
{
[Required, MaxLength(50)]
public string Title { get; set; }
public OptionalClientMenuViewModel Client { get; set; }
public OptionalProductMenuViewModel Product { get; set; }
[MaxLength(255), DataType(DataType.MultilineText)]
public string Description { get; set; }
public CreateFlagViewModel()
{
Client = new OptionalClientMenuViewModel();
Product = new OptionalProductMenuViewModel();
}
public void SetMenutItems(IClientService clientService, IUserPassport passport)
{
Client.SetOptions(clientService, passport);
}
}
您会注意到,我在viewmodel中使用了子视图模型,以允许重用代码,并为每个子视图模型提供了EditorTemplate,以消除到处编写的重复代码。这是子视图模型aka
OptionalClientMenuViewModel
负责显示出现在上述视图模型aka中的客户端列表
CreateFlagViewModel
。
public class OptionalClientMenuViewModel
{
public int? Id { get; set; }
public IEnumerable<SelectListItem> Options { get; set; }
public OptionalClientMenuViewModel()
{
Options = new List<SelectListItem>();
}
public void SetOptions(IClientService service, IUserPassport passport, bool isActive = true)
{
Options = service.GetClientItemForUser(passport.User, isActive);
}
}
如您所见
CreateFlagViewModel
视图模型类非常简洁。但是它有那么恶心
SetMenuItems
需要2个依赖项的方法。这些依赖关系迫使任何使用者“在本例中是控制器”必须传递它们。我觉得如果我能以某种方式摆脱
设置菜单项
方法,我的控制器将被清理,我的视图模型将看起来更干净。
既然我使用的是依赖项注入,为什么我不能将代码传输到如下内容
首先将子视图模型中的依赖项从方法交换到构造函数中。
public class OptionalClientMenuViewModel
{
protected IClientService ClientService;
protected IUserPassport Passport;
public int? Id { get; set; }
public IEnumerable<SelectListItem> Options { get; set; }
public OptionalClientMenuViewModel(IClientService clientService, IUserPassport passport)
{
Options = new List<SelectListItem>();
}
public void SetOptions(bool isActive = true)
{
Options = ClientService.GetClientItemForUser(Passport.User, isActive);
}
}
然后创建一个接口来自动调用
SetMenutItems()
每次渲染视图时使用。
公共接口IHaveMenuSetter
{
void SetMenutItems();
}
最后,实现
IHaveMenuSetter
契约到我的视图模型中,并将依赖项注入构造函数中,以使
设置菜单项
方法参数较少。
public class CreateFlagViewModel : IHaveMenuSetter
{
[Required, MaxLength(50)]
public string Title { get; set; }
public OptionalClientMenuViewModel Client { get; set; }
public OptionalProductMenuViewModel Product { get; set; }
[MaxLength(255), DataType(DataType.MultilineText)]
public string Description { get; set; }
public CreateFlagViewModel(OptionalClientMenuViewModel client)
{
Client = client;
Product = new OptionalProductMenuViewModel();
}
public void SetMenutItems()
{
Client.SetOptions();
}
}
最后,我的控制器看起来是这样的“假设其余的viewModels被重构为相同的代码模式”
public class FlagsController : Controller
{
protected ITaskFlagService FlagService;
protected IFlashManager Flash;
protected IMapper Mapper;
protected CreateFlagViewModel CreateViewModel;
public FlagsController (
ITaskFlagService flagService,
IFlashManager flash,
IMapper mapper,
CreateFlagViewModel createViewModel)
{
FlagService = flagService;
Flash = flash;
Mapper = mapper;
CreateViewModel = createViewModel;
}
public ActionResult Index(ListFlagsViewModel viewModel)
{
viewModel.Flags = FlagService.GetPagedRecords(viewModel, 25);
// This line could be even eliminated and auto called using `ActionFilterAttribute` by utilizing a new contract
viewModel.SetMenuItems();
return View(viewModel);
}
public ActionResult Create()
{
// This line could be even eliminated and auto called using `ActionFilterAttribute` by utilizing a new contract
CreateViewModel.SetMenutItems();
return View(CreateViewModel);
}
[HttpPost, ValidateAntiForgeryToken]
public ActionResult Create(CreateFlagViewModel viewModel)
{
if (ModelState.IsValid)
{
try
{
FlagService.Add(viewModel);
Flash.AddSuccess("New flag has been added.", 3);
return RedirectToAction("Index");
}
catch (Exception e)
{
Flash.AddError(e.Message);
}
}
// This line could be even eliminated and auto called using `ActionFilterAttribute` by utilizing a new contract
CreateViewModel.SetMenutItems();
return View(viewModel);
}
public ActionResult Edit(int? id)
{
var flag = FlagService.Get(id);
if (flag == null)
{
return RedirectToAction("Index");
}
var viewModel = Mapper.Map<EditFlagViewModel>(flag);
// This line could be even eliminated and auto called using `ActionFilterAttribute` by utilizing a new contract
viewModel.SetMenutItems();
return View(viewModel);
}
[HttpPost, ValidateAntiForgeryToken]
public ActionResult Edit(EditFlagViewModel viewModel)
{
if (ModelState.IsValid)
{
var flag = FlagService.Get(viewModel.Id);
if (flag == null)
{
return RedirectToAction("Index");
}
TaskFlag updatedFlag = Mapper.Map(viewModel, flag);
FlagService.Update(updatedFlag);
Flash.AddSuccess("Flag has been updated.", 3);
return RedirectToAction("Index");
}
// This line could be even eliminated and auto called using `ActionFilterAttribute` by utilizing a new contract
viewModel.SetMenutItems();
return View(viewModel);
}
public ActionResult Details(int? id)
{
var flag = FlagService.Get(id);
if (flag == null)
{
return RedirectToAction("Index");
}
var viewModel = Mapper.Map<DisplayFlagViewModel>(flag);
return View(viewModel);
}
}
清洁工可能但是上面的代码会给我一个错误,因为我的视图模型没有默认构造函数。当asp。净mvc
DefaultModelBinder
尝试在
HttpPost
要求
为了使设置正常工作,我必须覆盖
DefaultModelBinder
因此,它从IoC容器解析视图模型,而不是使用默认构造函数,这是我犹豫要做的事情。
问题
我当前代码的状态看起来可以接受吗?那么我提议的代码更改呢,提议的代码不是更好地分离了关注点吗?是否有更好的方法来清理此代码?