0

ご意見ありがとうございます。

私は MVC アーキテクチャについて学ぼうとしており、プロジェクトの要件よりもツールを学ぶことと同じくらい小さなプロジェクトに取り組んでいます。

私は何が良い、受け入れられる、悪い習慣を構成するのか、そしてその理由を理解する必要があります。特定の正解がないことは十分に理解していますが、良いものからひどいものまでの範囲のいずれかに適合するアーキテクチャが存在するに違いありません。ある意味では 1 つの質問ではありませんが、優れたデザイン プラクティスの論理的な流れが、それらすべてが 1 つのカプセル化された回答に関連付けられることを意味することを願っています。

Darko Pečnik によるCode First メンバーシップ プロバイダーを使用しています

User は、Membership クラスに属するメソッドを介してアクセス/変更する必要がある、主キーやパスワードなどの多くのプロパティを非表示にするインターフェイス IUser を実装します。また、User.Roles Collection ではなく、文字列の配列に getter と setter を使用します。

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

質問 1.) このプロパティは悪い習慣ではないかと思いますが、正確な理由はわかりません。これらはメソッド GetRoleNames および SetRoleNames として使用する方がよいでしょうか? それとも Icollection 自体を IUser インターフェイスに含めた方がよいでしょうか?

AutoMapper を使用して IUser からマップされる 2 つの別個のビューモデルが存在します。これらのモデルは、ユーザーが自分自身に関する詳細を登録/更新しているか、または Web サイト管理者によって登録/更新されているかに関連しています。

1 つの viewModel には、役割と部門の 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 インターフェイスを作成したのですか? なぜそれが役立つのかを説明するあなたの質問には理由がありません。それのさまざまな実装を交換する予定はありますか? それとも、具体的な User クラスにアクセスせずに、1 つのプロジェクトがそのプロパティに依存していますか?

質問 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 クラスで既に定義されているものに対して機能します。拡張メソッドは、具体的な User クラスではなく、IUser インターフェイスに対して簡単に記述できます。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 プロパティ/メソッドから)。その文字列配列を考慮して、SelectListItems の IEnumerable を作成します。すべての役割に SelectListItem が必要ですが、配列内の文字列に一致するものだけを選択する必要があります。クライアント コードにはすべてのロール名が含まれているわけではないため、その責任を値リゾルバーに与えました。クライアント コードは次のようになります。

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

本質的には、そのユーザーが属していない他のすべてのロール名を取得する方法を知るために、オートマッパーを「スマート」にしています。オートマッパーはこれほどスマートであってはなりません。通常、あらゆる種類のオートマッパー リゾルバーがデータ ストアにアクセスすることはお勧めできません。可能であれば避けるべきです。そうしないと、データ ストレージにアクセスするための静的な参照になってしまいます。このようなものがコントローラーに入る可能性があり、より明確です。

// 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 クラスで実行できることはすべて、ラムダ オーバーロード .ResolveUsing() でも実行できます。

質問 3.) ポストバック後、ビジネス ロジックはリポジトリ メソッド createUser および updateUser を介して検証されます。これらのメソッドが IUser インスタンスを引数として受け入れるのが最適でしょうか、それともさまざまなビューモデルを引数として受け入れるいくつかのオーバーロードに適しているのでしょうか。

ビジネス層はビューモデルを引数として受け入れてはなりません。それらはビジネスではなく、ビューのモデルです。ビジネス コードを MVC プロジェクトのクライアントと考えてください。ビジネス コードを MVC プロジェクトの外に移動したことがあり、ViewModel を引数として取るビジネス コードがある場合、コードはコンパイルされません。なんで?ビューモデルは MVC プロジェクトにあり、MVC プロジェクトはビジネス プロジェクトに依存しているため、その逆ではありません。

于 2012-07-15T03:37:08.243 に答える