C# 在视图模型中使用存储库可以吗?
假设我有一个复杂的视图模型,其中包含很多数据,例如国家、产品、类别等的列表,每次创建视图模型时都需要从数据库中获取这些数据 我想解决的主要问题是,当我处理POST操作时,一些C# 在视图模型中使用存储库可以吗?,c#,asp.net-mvc,viewmodel,C#,Asp.net Mvc,Viewmodel,假设我有一个复杂的视图模型,其中包含很多数据,例如国家、产品、类别等的列表,每次创建视图模型时都需要从数据库中获取这些数据 我想解决的主要问题是,当我处理POST操作时,一些TestModel使用不正确的值发布,这导致ModelState.IsValid为false,然后我必须返回与当前发布的模型相同的视图。这迫使我再次获取类别列表,因为我是在get操作中这样做的。这会在控制器中添加大量重复代码,我想将其删除。目前我正在做以下工作: 我的模型和视图模型: public class HomeCon
TestModel
使用不正确的值发布,这导致ModelState.IsValid
为false
,然后我必须返回与当前发布的模型相同的视图。这迫使我再次获取类别列表,因为我是在get操作中这样做的。这会在控制器中添加大量重复代码,我想将其删除。目前我正在做以下工作:
我的模型和视图模型:
public class HomeController : Controller
{
private readonly Repository _repository = ObjectFactory.GetRepositoryInstance();
public ActionResult Index()
{
var model = new TestModel
{
Categories = _repository.Categories.Select(c => new CategoryModel
{
Id = c.Id,
Name = c.Name
})
};
return View(model);
}
[HttpPost]
public ActionResult Index(TestModel model)
{
if (ModelState.IsValid)
{
return RedirectToAction("Succes");
}
model.Categories = _repository.Categories.Select(c => new CategoryModel
{
Id = c.Id,
Name = c.Name
});
return View(model);
}
public ActionResult Succes()
{
return View();
}
}
模型,存储在数据库中的实体:
我想删除重复的类别获取和映射,基本上如下代码:
.Categories = _repository.Categories.Select(c => new CategoryModel
{
Id = c.Id,
Name = c.Name
})
来自控制器。另外,我要删除ModelState
有效性检查,我只想在ModelState.IsValid
时执行操作,以尽可能保持控制器代码。到目前为止,我有以下用于删除ModelState
有效性检查的解决方案:
创建自定义ValidateModelAttribute
public class ValidateModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var viewData = filterContext.Controller.ViewData;
if(viewData.ModelState.IsValid) return;
viewData.Model = filterContext.ActionParameters["model"];
filterContext.Result = new ViewResult
{
ViewData = viewData,
};
}
}
现在,在执行操作之前验证模型。在验证错误的情况下,我们对最近发布的相同模型使用相同的视图。因此,控制器POST操作如下所示:
[HttpPost]
[ValidateModelAttribute]
public ActionResult Index(TestModel model)
{
// Do some important stuff with posted data
return RedirectToAction("Success");
}
这很好,但是现在我的TestModel
的Categories
属性为空,因为我必须从数据库中获取类别,并相应地映射它们那么可以修改我的视图模型,使其看起来像这样吗:
public class TestModel
{
private readonly Repository _repository = ObjectFactory.GetRepositoryInstance();
...
public int SelectedCategory { get; set; }
public IEnumerable<CategoryModel> Categories {
get
{
return _repository.Categories.Select(c => new CategoryModel
{
Id = c.Id,
Name = c.Name
});
}
}
...
}
公共类测试模型
{
私有只读存储库_Repository=ObjectFactory.GetRepositoryInstance();
...
public int SelectedCategory{get;set;}
公共可数范畴{
得到
{
return\u repository.Categories.Select(c=>newcategoryModel
{
Id=c.Id,
Name=c.Name
});
}
}
...
}
这将允许我们拥有非常干净的控制器,但它不会导致某种性能或体系结构问题吗?它不会打破视图模型的单一责任原则吗?ViewModels应该负责获取所需的数据吗?这不好。视图模型应该主要是由服务/查询甚至控制器填充的DTO。以前的版本没有问题,您的控制器只是几行代码 但您的存储库并不是一个真正的存储库,而是一个ORM。一个合适的存储库(在这里它只是一些查询对象)将直接返回视图模型的类别列表
关于自动验证属性,不要重新发明轮子,是别人(在本例中是我)做的。不,不应该将存储库引用和逻辑放入视图模型中。我认为,如果验证失败,您唯一需要的就是能够重建模型。您可以尝试一种自动化的ModelState验证,例如:
我可以从您的方法中看到一些流程
var model = new TestModel
{
Categories = _repository.Categories.Select(c => new CategoryModel
{
Id = c.Id,
Name = c.Name
})
};
public class TestService : ITestService{
private IReposotory repo;
public TestService(IReposotory repo){
this.repo = repo;
}
public TestModel GetModel()
{
return new TestModel()
{
Categories = _repository.Categories.Select(c => new CategoryModel
{
Id = c.Id,
Name = c.Name
})
};
}
}
public class HomeController : Controller
{
private readonly ITestService _service;
public HomeController (ITestService service){
_service = service;
}
public ActionResult Index()
{
return View(_service.GetModel());
}
[HttpPost]
public ActionResult Index(TestModel model)
{
if (ModelState.IsValid)
{
return RedirectToAction("Succes");
}
return View(model);
}
public ActionResult Succes()
{
return View();
}
}
理想情况下,视图模型不会直接与存储库交互。如果您需要从存储库中填充模型,这将发生在您的控制器中。如果您不想在单独的控制器路由中重复类别填充,您可以尝试将此逻辑重构为单独的方法。。。
var model = new TestModel
{
Categories = _repository.Categories.Select(c => new CategoryModel
{
Id = c.Id,
Name = c.Name
})
};
public class TestService : ITestService{
private IReposotory repo;
public TestService(IReposotory repo){
this.repo = repo;
}
public TestModel GetModel()
{
return new TestModel()
{
Categories = _repository.Categories.Select(c => new CategoryModel
{
Id = c.Id,
Name = c.Name
})
};
}
}
public class HomeController : Controller
{
private readonly ITestService _service;
public HomeController (ITestService service){
_service = service;
}
public ActionResult Index()
{
return View(_service.GetModel());
}
[HttpPost]
public ActionResult Index(TestModel model)
{
if (ModelState.IsValid)
{
return RedirectToAction("Succes");
}
return View(model);
}
public ActionResult Succes()
{
return View();
}
}