关于在Asp.net Mvc 5应用程序中创建“瘦控制器”的实现,我有一个问题。在过去的几天里,我一直在研究这个话题,我相信我现在需要一个具体的例子来联系我理解中的点。
因此,我想在我的应用程序中使用Unit测试。我研究过创建视图模型工厂和构建器、瘦控制器-fat模型,但我不确定如何实现这些设计模式中的任何一个,我已经在这个特定的场景中读到过。
下面您会发现在我的管理控制器中有5个不同的操作。我担心,为了简化测试/单元测试,它们会嗅到并需要一些清理。我知道这类问题通常没有“正确的答案”,所以我非常感谢所有有助于简化我的应用程序测试的答案。
以下是我的行动:
行动1:
[HttpPost]
[ValidateAntiForgeryToken]
[Authorize(Roles = "DM_Admin")]
public async Task<ActionResult> Users_Create([DataSourceRequest] DataSourceRequest request, ManageUsersViewModel model)
{
if (model != null && ModelState.IsValid)
{
// instantiate new application user
var user = new ApplicationUser
{
UserName = model.Email,
Email = model.Email,
FirstName = model.FirstName,
LastName = model.LastName
};
// format the RolesList to type List<string> for entry
List<string> rolesToAssign = getRoleNameList(model);
try
{
// persist user to User Db
var createResult = await UserManager.CreateAsync(user, model.Password);
if (createResult.Succeeded)
{
// persist user roles to User Db
var rolesResult = await UserManager.AddToRolesAsync(user.Id, rolesToAssign.ToArray());
if (rolesResult.Succeeded)
{
return RedirectToAction("ManageUsers");
}
AddErrors(rolesResult);
}
else
{
AddErrors(createResult);
}
}
catch (Exception ex)
{
ErrLog.LogError(ex, "ManageController.Users_Create");
}
}
return Json(new[] { model }.AsQueryable().ToDataSourceResult(request, ModelState));
}行动2:
[Authorize(Roles = "DM_Admin")]
public async Task<ActionResult> Users_Read([DataSourceRequest] DataSourceRequest request)
{
List<ManageUsersViewModel> users = null;
List<ApplicationUser> allUsers = null;
try
{
// get all Data Management roles
var dmRoles = await RoleManager.Roles.Where(r => r.Name.Contains("DM_")).ToListAsync();
// find all the users for each Data Management role
foreach (var id in dmRoles.Select(r => r.Id).ToList())
{
if(allUsers == null)
{
allUsers = await UserManager.Users.Where(u => u.Roles.Any(r => r.RoleId == id)).ToListAsync();
}
else
{
allUsers.AddRange(await UserManager.Users.Where(u => u.Roles.Any(r => r.RoleId == id)).ToListAsync());
}
}
// list of users may have repeats, so remove repeated users in list
allUsers = allUsers.Distinct().ToList();
// instantiate view model with list of users and their respective roles
users = allUsers.Select(u => new ManageUsersViewModel
{
Id = u.Id,
FirstName = u.FirstName,
LastName = u.LastName,
Email = u.Email,
Password = u.PasswordHash,
ConfirmPassword = u.PasswordHash,
RolesList = dmRoles.Where(r => r.Users.Any(user => user.UserId == u.Id)).Select(r => new RoleModel
{
Id = r.Id,
Name = r.Name
}).ToList()
}).ToList();
}
catch (Exception ex)
{
ErrLog.LogError(ex, "ManageController.Users_Read");
}
return Json(users.ToDataSourceResult(request));
}行动3:
[HttpPost]
[ValidateAntiForgeryToken]
[Authorize(Roles = "DM_Admin")]
public async Task<ActionResult> Users_Edit([DataSourceRequest] DataSourceRequest request, ManageUsersViewModel model)
{
if (model != null && ModelState.IsValid)
{
// format the RolesList to type List<string> with name and List<string> with Id for entry and comparison
List<string> rolesToAssign = getRoleNameList(model);
List<string> modelRoleIds = getRoleIdList(model);
try
{
var currentUser = await UserManager.FindByIdAsync(model.Id);
// create list of current user's roles to determine if they have been modified
List<string> currentUserRoleIds = new List<string>();
foreach (var role in currentUser.Roles)
{
currentUserRoleIds.Add(role.RoleId);
}
// persist user roles to User Db if changes have been made
if (currentUserRoleIds.Except(modelRoleIds).Any() || modelRoleIds.Except(currentUserRoleIds).Any())
{
var updateRolesResult = await AssignRolesToUser(model.Id, rolesToAssign.ToArray());
if (updateRolesResult.Succeeded)
{
return RedirectToAction("ManageUsers", new { Message = ManageMessageId.AccountUpdateSuccess });
}
AddErrors(updateRolesResult);
}
// persist user info to User Db if changes have been made
else if(currentUser.Email != model.Email || currentUser.FirstName != model.FirstName || currentUser.LastName != model.LastName)
{
currentUser.UserName = model.Email;
currentUser.Email = model.Email;
currentUser.FirstName = model.FirstName;
currentUser.LastName = model.LastName;
var updateUserResult = await UserManager.UpdateAsync(currentUser);
if (updateUserResult.Succeeded)
{
return RedirectToAction("ManageUsers", new { Message = ManageMessageId.AccountUpdateSuccess });
}
AddErrors(updateUserResult);
}
// persist user password to User Db if changes have been made
else
{
var token = await UserManager.GeneratePasswordResetTokenAsync(currentUser.Id);
var updatePasswordResult = await UserManager.ResetPasswordAsync(currentUser.Id, token, model.Password);
if (updatePasswordResult.Succeeded)
{
return RedirectToAction("ManageUsers", new { Message = ManageMessageId.AccountUpdateSuccess });
}
AddErrors(updatePasswordResult);
}
}
catch (Exception ex)
{
ErrLog.LogError(ex, "ManageController.Users_Edit");
}
}
return Json(new[] { model }.AsQueryable().ToDataSourceResult(request, ModelState));
}行动4:
[HttpPost]
[ValidateAntiForgeryToken]
[Authorize(Roles = "DM_Admin")]
public async Task<ActionResult> Users_Delete([DataSourceRequest] DataSourceRequest request, ManageUsersViewModel model)
{
if (model != null && ModelState.IsValid)
{
// format the RolesList to type List<string> for removal
List<string> rolesToRemove = getRoleNameList(model);
try
{
// get the user to be deleted by id
var user = await UserManager.FindByIdAsync(model.Id);
// remove roles from user in User Db
var removeRolesResult = await UserManager.RemoveFromRolesAsync(user.Id, rolesToRemove.ToArray());
if (removeRolesResult.Succeeded)
{
// remove user from User Db
var removeUserResult = await UserManager.DeleteAsync(user);
if (removeUserResult.Succeeded)
{
return RedirectToAction("ManageUsers", new { Message = ManageMessageId.AccountDeleteSuccess });
}
AddErrors(removeUserResult);
}
else
{
AddErrors(removeRolesResult);
}
}
catch (Exception ex)
{
ErrLog.LogError(ex, "ManageController.Users_Delete");
}
}
return Json(new[] { model }.AsQueryable().ToDataSourceResult(request, ModelState));
}行动5:
[HttpPost]
[ValidateAntiForgeryToken]
[Authorize(Roles = "DM_Admin")]
public async Task<IdentityResult> AssignRolesToUser(string id, string[] rolesToAssign)
{
if (rolesToAssign != null)
{
try
{
// find the user to assign roles to
var user = await UserManager.FindByIdAsync(id);
if (user != null)
{
// check if the user currently has any roles
var currentRoles = await UserManager.GetRolesAsync(user.Id);
var rolesNotExist = rolesToAssign.Except(RoleManager.Roles.Select(x => x.Name)).ToArray();
if (!(rolesNotExist.Count() > 0))
{
// remove current roles from user, if any, in User Db
var removeRolesResult = await UserManager.RemoveFromRolesAsync(user.Id, currentRoles.ToArray());
if (!removeRolesResult.Succeeded)
{
AddErrors(removeRolesResult);
return removeRolesResult;
}
// assign new roles to user in User Db
var addRolesResult = await UserManager.AddToRolesAsync(user.Id, rolesToAssign);
if (!addRolesResult.Succeeded)
{
AddErrors(addRolesResult);
}
return addRolesResult;
}
else
{
ModelState.AddModelError("", string.Format("Roles '{0}' do not exist in the system", string.Join(",", rolesNotExist)));
}
}
else
{
ModelState.AddModelError("", string.Format("Unable to find user"));
}
}
catch (Exception ex)
{
ErrLog.LogError(ex, "ManageController.AssignRolesToUser");
}
}
else
{
ModelState.AddModelError("", string.Format("No roles specified"));
}
return null;
}在创建默认Mvc应用程序时,身份框架与Microsoft如何设置身份框架相同,因此,我不太确定如何重构代码。
我知道这是一个较长的帖子,所以非常感谢您抽出时间阅读它,我希望大家尽快收到您的来信。
**注:我增加了5种行动方法,因为我认为它们可能需要显示出来,以便使整个控制器变薄,同时使人们更好地了解正在发生的事情。但是,请不要觉得有必要为列出的所有行动提供例子。**
太感谢你们了!斯纳沃兹
发布于 2017-10-03 13:53:27
新开发人员的罪魁祸首是:陷入模式地狱,而不是仅仅担心应用程序。
设计模式只是解决特定问题的推荐方法。足够多的开发人员已经做了足够多的相同事情,在某种程度上遵循了一套公认的最佳实践。然而,在这种情况下经常会丢失的是,设计模式的存在是为了解决特定类型的问题。如果您的应用程序没有这个特殊的问题,就没有必要实现这个特定的模式。在使用类似于实体框架的ORM存储库模式时,您会经常看到这种情况。存储库模式的存在是为了对所有原始SQL代码进行洗牌,使用的数据库直接要求将所有代码都放在整洁的位置上。如果周围没有原始SQL代码,则不需要存储库模式。
此外,应用程序的需求超过了任何“最佳做法”。例如,很少有人会认为不应该在应用程序中实现控制反转和依赖注入。在99.999%的应用程序中,这极大地改进了应用程序。但是,堆栈溢出核心团队根本不使用它。为什么?因为它增加了应用程序的份量。核心团队的唯一责任是使堆栈溢出尽可能具有性能,而在这个特定的应用程序中,依赖项注入不起作用。
所有这些都是为了构建你的应用程序。不要担心每件事都是正确的,或者所有的“最佳实践”都会被遵循。就建它吧。然后,你可以回去重构,当你重构的时候,你会找到机会以更好的方式去做事情。在这一点上,您可以参考各种设计模式作为指导。一开始就试图去担心所有这些事情是一个陷阱,它常常会让你陷入停滞,无法向前迈进。
还有一点要注意:你需要给自己时间和自由,让自己变得有经验。我的意思是,一旦你有了做这些事情的经验,你就会做一些应该如何做的事情,因为它开始成为第二天性。然而,你必须首先达到这一点。当你刚开始的时候,你的代码并不完美,这一点也不丢人。我不知道有哪个开发人员在他们还是新手的时候,面对他们编写的代码时,他们会不寒而栗。当您构建更多的应用程序,控制新的挑战,解决新的问题,等等,您将建立一个知识库,这将使您更容易做正确的事情。给自己点喘息的空间。
发布于 2017-10-03 13:06:23
基本上,MVC是一种表示模式。它曾经/不是设计成解决所有设计问题的灵丹妙药。它只应格式化数据供用户查看。不多也不差。
它只应获取数据,将其传递给呈现引擎(Razor)并创建路由(仅此而已)。其他一切都应该在其他层/模式中。这是"Skinny控制器“的关键所在,因为它们包含零逻辑(请记住表示模式)。这些“其他”模式应该是什么完全取决于你的情况。这个问题没有一个解决办法。
为了从示例中提供一个瘦控制器的示例,它看起来像,类似于:
private MyLogicClass _logic;
[HttpPost]
[ValidateAntiForgeryToken]
[Authorize(Roles = "DM_Admin")]
public async Task<ActionResult> Users_Delete([DataSourceRequest] DataSourceRequest request, ManageUsersViewModel model)
{
var model = _logic.DoLogic();
return View(model);
}示例中的所有“内容”都应该驻留在一个或多个“逻辑类”中,在上面的示例中是MyLogicClass。您的控制器应该非常愚蠢,只需传递数据。
https://stackoverflow.com/questions/46545093
复制相似问题