[英]MVC architecture and custom membershipProvider
谢谢您的任何想法。
我正在尝试学习MVC架构,并致力于一个小项目,这对我来说学习工具远胜于项目的需求。
我需要弄清楚什么是好的,可接受的和不良的做法,以及原因。 我完全理解,没有具体的正确答案,但是必须有适合任何良好->糟糕范围的架构。 尽管从某种意义上说,这不仅仅是一个问题,但我希望良好的设计实践的逻辑流程意味着它们都与单个封装答案相关。
我正在使用DarkoPečnik的Code First Membership提供商
用户实现一个接口IUser,该接口隐藏许多属性,例如主键和密码,应该通过属于Membership类的方法来访问/更改这些属性。 它还通过字符串而不是User.Roles集合将getter和setter用于字符串数组:
public virtual String[] RoleNames
{
set
{
this.Roles = (ICollection<Role>)value.Select(r =>
new Role { RoleName = r }).ToList();
问题1.)我怀疑此属性可能是不正确的做法,但不确定确切原因。 这些作为方法GetRoleNames和SetRoleNames会更好,还是将Icollection本身更好地包含在IUser接口中?
存在两个单独的viewModel,它们是使用AutoMapper从IUser映射的。 这些模型与用户是自己注册/更新有关其自身的详细信息还是由网站管理员进行注册/更新有关。
一个viewModel包含一个用于角色和部门的IEnumerable。 这些属性当前正在通过自动映射器进行映射:
internal class RoleStringArrayToSelectListResolver
: ValueResolver<String[], IEnumerable<SelectListItem>>
{
protected override IEnumerable<SelectListItem> ResolveCore(String[] source)
{
return Roles.GetAllRoles().Select(x => new SelectListItem
{
Value = x,
Text = StringExtensions.ToSeparatedWords(x),
Selected = source.Contains(x)
问题2。)autoMapper是否可以放置这样的逻辑,如果不是,应该放在哪里?
问题3.)回发后,通过存储库方法createUser和updateUser验证业务逻辑。 对于这些方法,将IUser实例作为参数是最佳选择,还是对于接受各种viewModels作为参数的几个重载来说是最佳选择,如果可以,为什么?
非常感谢您的任何想法和帮助我的理解。
为什么要创建一个IUser界面? 在您的问题中,我没有看到任何解释其用途的理由。 您期望换掉它的不同实现吗? 还是1个项目依赖于它的属性而不访问具体的User类?
问题1.)我怀疑此属性可能是不正确的做法,但不确定确切原因。 这些作为方法GetRoleNames和SetRoleNames会更好,还是将Icollection本身更好地包含在IUser接口中?
在我看来,您想要做的就是仅使用角色名称字符串访问和操作ICollection Roles属性中的Role项目。 您可以不理会类(不创建属性或方法),而仅将其实现为扩展方法:
public static class UserExtensions
{
public static string[] GetRoleNames(this User user)
{
return user.Roles.Select(r => r.Name).ToArray();
}
public static void SetRoleNames(this User user, params string[] roleNames)
{
user.Roles = roleNames.Select(s => new Role { RoleName = s }).ToList();
}
}
这样,您可以相应地获取和设置角色名称。 扩展方法仅针对User类中已定义的方法,而不会因重载而混乱。 可以很容易地针对IUser接口而不是具体的User类编写扩展方法。 您只需编写this IUser user
而不是this User user
。
var user = MethodToGetOrCreateUser();
string[] roleNames = user.GetRoleNames();
if (!roleNames.Any())
user.SetRoleNames("StandardUser", "SomeOtherRole");
问题2。)autoMapper是否可以放置这样的逻辑,如果不是,应该放在哪里?
我想我知道您在做什么:您有一个string []角色名称(可能来自User.GetRoleNames属性/方法)。 给定该字符串数组,您要创建一个IEnumerable的SelectListItems。 每个角色都应该有一个SelectListItem,但是只能选择与数组中的字符串匹配的角色。 由于您的客户代码没有所有的角色名称,因此您将责任交给了价值解析器。 您的客户代码可能看起来像这样:
var user = MethodToGetOrCreateUser();
string[] roleNames = user.GetRoleNames();
var rolesMenu = Mapper.Map<IEnumerable<SelectListItem>>(roleNames);
从本质上讲,您使自动映射器“足够聪明”,足以知道如何获取该用户不在的所有其他角色名称。自动映射器不应该这么聪明; 通常,让任何类型的automapper解析器访问数据存储区都不是一个好主意,因此应尽可能避免使用它。 否则,您最终将获得用于访问数据存储的静态引用。 这样的事情可能会进入您的控制器,并且更加清晰:
// this should actually go in Application_Start
Mapper.CreateMap<IEnumerable<Role>, IEnumerable<SelectListItem>>()
.ForMember(d => d.Value, o => o.MapFrom(s => s.RoleName))
.ForMember(d => d.Text, o => o.MapFrom(s => s.RoleName.ToSeparatedWords()))
;
// create your menu with all roles
var rolesMenu = Mapper.Map<IEnumerable<SelectListItem>>(Roles.GetAllRoles());
// select only the roles that the user is in
var user = MethodToGetOrCreateUser();
user.GetRoleNames().ToList().ForEach(r =>
{
var match = rolesMenu.SingleOrDefault(i => i.Value == r);
if (match != null) match.Selected = true;
});
我发现您可以完全避免使用ValueResolver类。 您可以使用ValueResolver类做的任何事情,也可以使用lambda重载.ResolveUsing()进行。
问题3.)回发后,通过存储库方法createUser和updateUser验证业务逻辑。 对于这些方法,将IUser实例作为参数是最佳选择,还是对于接受各种viewModels作为参数的几个重载来说是最佳选择,如果可以,为什么?
您的业务层永远不应接受将视图模型作为参数。 它们是视图的模型,而不是业务的模型。 将业务代码视为您的MVC项目的客户。 如果您曾经将业务代码移出MVC项目,并且您有将ViewModels作为参数的业务代码,则该代码将无法编译。 为什么? 因为视图模型在MVC项目中,并且MVC项目依赖于业务项目-而不是相反。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.