0

谢谢你的任何想法。

我正在尝试学习 MVC 架构,并在一个小项目上工作,这与我学习该工具的重要性不亚于项目的要求。

我需要弄清楚什么是好的、可接受的和不好的做法,以及为什么。我完全理解不会有具体的正确答案,但必须有适合任何好 -> 坏范围的架构。虽然从某种意义上说不仅仅是一个问题,但我希望良好设计实践的逻辑流程意味着它们都与一个单一的封装答案相关联。

我正在使用Darko Pečnik 的Code First Membership 提供程序

User 实现了一个接口 IUser ,它隐藏了许多属性,例如主键和密码,应该通过属于 Membership 类的方法来访问/更改这些属性。它还对字符串数组使用 getter 和 setter,而不是通过以下方式使用 User.Roles 集合:

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 映射。这些模型与用户是自己注册/更新有关他们的详细信息,还是由网站管理员注册/更新有关。

一个视图模型包含角色和部门的 IEnumerable。这些属性当前正在通过 automapper 进行映射:

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 实例作为参数是否是最佳选择,或者对于接受各种视图模型作为参数的几个重载来说更可取,如果是,为什么?

非常感谢您提出任何想法并帮助我理解。

4

1 回答 1

1

为什么要创建 IUser 界面?我在您的问题中没有看到任何原因来解释它为什么有用。你预计换掉它的不同实现吗?或者 1 个项目是否依赖于其属性,而无法访问具体的 User 类?

问题 1.) 我怀疑这个属性可能是不好的做法,但我不确定具体原因。这些作为 GetRoleNames 和 SetRoleNames 方法会更好,还是 Icollection 本身会更好地包含在 IUser 接口中?

在我看来,您想要做的是仅使用角色名称字符串访问和操作 ICollection Roles 属性中的角色项。您可以单独保留该类(不要创建属性或方法),并将其作为扩展方法实现:

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 是一个可以接受的放置这种逻辑的地方吗?如果不是,它应该放在哪里?

我想我明白你在做什么:你有一个角色名称的字符串 [](可能来自你的 User.GetRoleNames 属性/方法)。给定该字符串数组,您希望创建一个 SelectListItems 的 IEnumerable。每个角色都应该有一个 SelectListItem,但只应选择与数组中的字符串匹配的角色。由于您的客户端代码没有所有角色名称,因此您将该责任交给了值解析器。您的客户端代码可能如下所示:

var user = MethodToGetOrCreateUser();
string[] roleNames = user.GetRoleNames();
var rolesMenu = Mapper.Map<IEnumerable<SelectListItem>>(roleNames);

从本质上讲,您正在使 automapper 足够“智能”,以知道如何获取该用户不属于的所有其他角色名称。 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 实例作为参数是否是最佳选择,或者对于接受各种视图模型作为参数的几个重载来说更可取,如果是,为什么?

你的业务层不应该接受视图模型作为参数。它们是视图的模型,而不是业务模型。将业务代码视为您的 MVC 项目的客户端。如果您曾经将业务代码移到 MVC 项目之外,并且您的业务代码将 ViewModels 作为参数,那么代码将无法编译。为什么?因为视图模型在 MVC 项目中,而 MVC 项目依赖于业务项目——反之则不然。

于 2012-07-15T03:37:08.243 回答