简体   繁体   English

MVC体系结构和自定义的MembershipProvider

[英]MVC architecture and custom membershipProvider

Thank you for any thoughts. 谢谢您的任何想法。

I am trying to learn about MVC architecture, and working on a small project which is as much about me learning the tool than the requirements of the project. 我正在尝试学习MVC架构,并致力于一个小项目,这对我来说学习工具远胜于项目的需求。

I need to figure out what constitutes good, acceptable, and poor practice, and why. 我需要弄清楚什么是好的,可接受的和不良的做法,以及原因。 I fully understand there will be no specific right answer, but there must be architectures which fit into any of the spectrum of good -> terrible. 我完全理解,没有具体的正确答案,但是必须有适合任何良好->糟糕范围的架构。 While in a sense more than a single question, I hope the logical flow of good design practice means they all relate back to a single encapsulating answer. 尽管从某种意义上说,这不仅仅是一个问题,但我希望良好的设计实践的逻辑流程意味着它们都与单个封装答案相关。

I m using Code First Membership provider by Darko Pečnik 我正在使用DarkoPečnik的Code First Membership提供商

User implements an interface IUser which hides many of the properties such as Primary key and password, which should be accessed/altered via the methods belonging to Membership class. 用户实现一个接口IUser,该接口隐藏许多属性,例如主键和密码,应该通过属于Membership类的方法来访问/更改这些属性。 It also uses a getter and setter for an array of strings rather than the User.Roles Collection via: 它还通过字符串而不是User.Roles集合将getter和setter用于字符串数组:

public virtual String[] RoleNames
{
    set 
    {
        this.Roles = (ICollection<Role>)value.Select(r => 
            new Role { RoleName = r }).ToList();

Question 1.) I suspect this property might be bad practice, but am unsure of exactly why. 问题1.)我怀疑此属性可能是不正确的做法,但不确定确切原因。 Would these be better as methods GetRoleNames and SetRoleNames, or would the Icollection itself be better included in the IUser interface? 这些作为方法GetRoleNames和SetRoleNames会更好,还是将Icollection本身更好地包含在IUser接口中?

Two seperate viewModels exist which are mapped from IUser using AutoMapper. 存在两个单独的viewModel,它们是使用AutoMapper从IUser映射的。 These models relate to whether the user is registering/updating details about them-self, or being registered/updated by a website administrator. 这些模型与用户是自己注册/更新有关其自身的详细信息还是由网站管理员进行注册/更新有关。

one viewModel contains an IEnumerable for roles and departments. 一个viewModel包含一个用于角色和部门的IEnumerable。 these properties are currently being mapped via 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)

Question 2.) Is autoMapper an acceptable place to put such logic, and if not where should it go? 问题2。)autoMapper是否可以放置这样的逻辑,如果不是,应该放在哪里?

Question 3.) After postback, business logic is validated via repository methods createUser and updateUser. 问题3.)回发后,通过存储库方法createUser和updateUser验证业务逻辑。 Would it be optimal for these methods to accept an IUser instance as an argument, or preferable for a couple of overloads accepting the various viewModels as arguments, and if so why? 对于这些方法,将IUser实例作为参数是最佳选择,还是对于接受各种viewModels作为参数的几个重载来说是最佳选择,如果可以,为什么?

Thank you very much for any thought ideas and helping my understanding. 非常感谢您的任何想法和帮助我的理解。

Why have you created an IUser interface? 为什么要创建一个IUser界面? I haven't seen any reasons in your question explaining why it is useful. 在您的问题中,我没有看到任何解释其用途的理由。 Do you anticipate swapping out different implementations of it? 您期望换掉它的不同实现吗? Or does 1 project depend on its properties, without having access to the concrete User class? 还是1个项目依赖于它的属性而不访问具体的User类?

Question 1.) I suspect this property might be bad practice, but am unsure of exactly why. 问题1.)我怀疑此属性可能是不正确的做法,但不确定确切原因。 Would these be better as methods GetRoleNames and SetRoleNames, or would the Icollection itself be better included in the IUser interface? 这些作为方法GetRoleNames和SetRoleNames会更好,还是将Icollection本身更好地包含在IUser接口中?

It seems to me that what you want to be able to do is access and manipulate Role items in your ICollection Roles property using role name strings only. 在我看来,您想要做的就是仅使用角色名称字符串访问和操作ICollection Roles属性中的Role项目。 You could leave the class alone (don't create a property or a method), and just implement this as an extension method: 您可以不理会类(不创建属性或方法),而仅将其实现为扩展方法:

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();
    }
}

With this, you can get and set role names accordingly. 这样,您可以相应地获取和设置角色名称。 The extension method just works against what is already defined in the User class, without cluttering it with overloads. 扩展方法仅针对User类中已定义的方法,而不会因重载而混乱。 The extension method could just as easily be written against the IUser interface instead of the concrete User class. 可以很容易地针对IUser接口而不是具体的User类编写扩展方法。 You would just write this IUser user instead of this User user . 您只需编写this IUser user而不是this User user

var user = MethodToGetOrCreateUser();
string[] roleNames = user.GetRoleNames();
if (!roleNames.Any())
    user.SetRoleNames("StandardUser", "SomeOtherRole");

Question 2.) Is autoMapper an acceptable place to put such logic, and if not where should it go? 问题2。)autoMapper是否可以放置这样的逻辑,如果不是,应该放在哪里?

I think I see what you are doing: You have a string[] of role names (probably from your User.GetRoleNames property/method). 我想我知道您在做什么:您有一个string []角色名称(可能来自User.GetRoleNames属性/方法)。 Given that string array, you want to create an IEnumerable of SelectListItems. 给定该字符串数组,您要创建一个IEnumerable的SelectListItems。 There should be a SelectListItem for every role, but only the ones which match a string in your array should be selected. 每个角色都应该有一个SelectListItem,但是只能选择与数组中的字符串匹配的角色。 Since your client code does not have all of the Role names, you gave that responsibility to the value resolver. 由于您的客户代码没有所有的角色名称,因此您将责任交给了价值解析器。 Your client code may then look something like this: 您的客户代码可能看起来像这样:

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

In essence you are making automapper "smart" enough to know how to get all of the other role names that that user is not in. Automapper shouldn't be this smart; 从本质上讲,您使自动映射器“足够聪明”,足以知道如何获取该用户不在的所有其他角色名称。自动映射器不应该这么聪明; having any kind of automapper resolver access a data store is generally not a good idea, and you should avoid it if possible. 通常,让任何类型的automapper解析器访问数据存储区都不是一个好主意,因此应尽可能避免使用它。 Otherwise you end up with static references to access data storage. 否则,您最终将获得用于访问数据存储的静态引用。 Something like this could go in your controller, and is clearer: 这样的事情可能会进入您的控制器,并且更加清晰:

// 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;
});

I have found that you can avoid ValueResolver classes altogether. 我发现您可以完全避免使用ValueResolver类。 Anything you can do with a ValueResolver class you can also do with the lambda overload .ResolveUsing(). 您可以使用ValueResolver类做的任何事情,也可以使用lambda重载.ResolveUsing()进行。

Question 3.) After postback, business logic is validated via repository methods createUser and updateUser. 问题3.)回发后,通过存储库方法createUser和updateUser验证业务逻辑。 Would it be optimal for these methods to accept an IUser instance as an argument, or preferable for a couple of overloads accepting the various viewModels as arguments, and if so why? 对于这些方法,将IUser实例作为参数是最佳选择,还是对于接受各种viewModels作为参数的几个重载来说是最佳选择,如果可以,为什么?

Your business layer should never accept viewmodels as arguments. 您的业​​务层永远不应接受将视图模型作为参数。 They are models for the view, not for the business. 它们是视图的模型,而不是业务的模型。 Think of the business code as your MVC project's client. 将业务代码视为您的MVC项目的客户。 If you ever moved your business code outside of the MVC project, and you had business code that took ViewModels as arguments, the code would not compile. 如果您曾经将业务代码移出MVC项目,并且您有将ViewModels作为参数的业务代码,则该代码将无法编译。 Why? 为什么? Because the viewmodels are in the MVC project, and the MVC project takes a dependency on the business project -- not vice versa. 因为视图模型在MVC项目中,并且MVC项目依赖于业务项目-而不是相反。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM