我问过一个关于在我的控制器中将ViewModel映射到Entity Framework模型的最佳实践的问题,并且被告知我的代码是正确的(使用LINQ投影),尽管可以另外使用AutoMapper。
现在我觉得我需要/想要将Controller方法中发生的大部分内容移动到新的Service层,这样我就可以在需要时在此层添加业务逻辑,然后在我的控制器中进行方法调用。但我不确定该怎么做。我的ViewModel当然都会保留在Web项目中,那么我的服务层中的方法应该是什么样子以及我在哪里/如何映射ViewModel?
以下是当前GET和POST控制器方法的示例:
public ActionResult Laboratories()
{
var context = new PASSEntities();
var model = (from a in context.Laboratories
select new LaboratoryViewModel()
{
ID = a.ID,
Description = a.Description,
LabAdmins = (from b in context.Users_Roles
join c in context.Users on b.User_ID equals c.ID
where b.Laboratory_ID == a.ID
select new LabAdminViewModel()
{
ID = b.ID,
User_ID = b.User_ID,
Role_ID = b.Role_ID,
Laboratory_ID = b.Laboratory_ID,
BNL_ID = c.BNL_ID,
First_Name = c.Pool.First_Name,
Last_Name = c.Pool.Last_Name,
Account = c.Account
})
});
return View(model);
}
[HttpPost]
public ActionResult AddLaboratory(LaboratoryViewModel model)
{
try
{
using (PASSEntities context = new PASSEntities())
{
var laboratory = new Laboratory()
{
ID = model.ID,
Description = model.Description
};
context.Laboratories.Add(laboratory);
context.SaveChanges();
}
return RedirectToAction("Laboratories");
}
catch
{
return View();
}
}
答案 0 :(得分:4)
您的服务层应该返回您的域模型。控制器负责将它们映射到视图模型并将其返回到视图。一个小例子:
public ActionResult Laboratories()
{
// Get the laboratories domain models from the service layer.
var laboratories = _laboratoryService.GetLaboratories();
// Map the domain models to view models using AutoMapper.
var laboratoriesModel = Mapper.Map<List<LaboratoryViewModel>>(laboratories);
// Return view model to the view.
return View(laboratoriesModel);
}
使用此方法,您需要一个核心/域层,您的域实体可以在其中存在。服务层包含业务逻辑并与域模型交互(例如通过存储库)并将实体化对象返回给控制器。您的视图模型确实应该在您提出的网站项目中。
同时查看this question我提供了类似解决方案的示例。
<强>更新强>
服务层中的GetLaborarties
方法返回(模型)域模型的集合:
public List<Laboratory> GetLaboratories()
{
return _db.Laboratories.ToList();
}
现在,在您的控制器中,您可以调用此方法并将其映射到视图模型。您可以使用Linq Select
方法执行此操作:
public ActionResult Laboratories()
{
// Get the laboratories domain models from the service layer.
var laboratories = _laboratoryService.GetLaboratories();
var laboratoriesModel = laboratories.Select(new LaboratoryViewModel
{
// Map here..
}).ToList();
return View(laboratoriesModel);
}
如上所述,或使用AutoMapper。
<小时/> 更新2
包含相关对象的导航属性的简单示例:
假设我们有这个域名模型:
public class Category
{
public string Name { get; set; }
public string UrlName { get; set; }
// Other properties..
public virtual ICollection<Product> Products { get; set; }
}
我们可以在服务层创建一个方法:
public CategoryService : ICategoryService
{
public Category GetByName(string name)
{
return _categoryRepository.Table
.Include(c => c.Products) // Include related products
.FirstOrDefault(c => c.UrlName = name);
}
}
我为实体框架配置了一个类别包含零个或多个产品。使用Include
方法,我要求Entity Framework在sql查询中包含相关产品。现在Products
将包含该类别的所有相关产品。
答案 1 :(得分:3)
我不认为将这样简单的代码重构到另一个层是有意义的,但是在其他情况下这个问题是有意义的,在这些情况下会发生更复杂的处理,例如,您创建一个帐户,为其设置默认密码并指定在单个业务事务中进行少量插入和可能很少选择的角色。
服务层由服务组成。您的服务在域实体级别运行。
public class AccountService
{
private PASSEntities _context { get; set; }
public AccountService( PASSEntities context )
{
this._context = context;
}
public User CreateAccount( string username, string password )
{
// implementation here
}
您可以从控制器访问服务,这是视图模型和模型之间的转换,以便服务不知道视图模型层:
[HttpPost]
public ActionResult CreateUser( UserViewModel model )
{
using ( PASSEntities context = new PASSEntities() )
{
AccountService service = new AccountService( context );
service.CreateUser( model.Username, model.Password );
// return something appropriate here
}
}