webdevqa.jp.net

Microsoft.AspNet.Identityのカスタムメンバーシップ-CreateLocalUserが失敗する

Visual Studio 2013を使用して、ASP.NET 4.5(Microsoft.AspNet.Identity)に新しいバージョンのIdentity機能のカスタムバージョンを実装しようとしています。これを何時間も試した後、コードをエラーなしで実行するための努力。以下にコードを記載しました。ローカル登録を行うと、データベーステーブルが作成されますが、CreateLocalUserメソッドは失敗します。誰かが必要な変更を特定するのを手伝ってくれることを願っています。

Models/MembershipModel.cs

using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Linq;
using System.Web;

namespace thePulse.web.Models
{
    public class PulseUser : IUser
    {
        public PulseUser() { }
        public PulseUser(string userName) 
        {
            UserName = userName;
        }

        [Key]
        public string Id { get; set; }
        [Required]
        [StringLength(20)]
        public string UserName { get; set; }
        [StringLength(100)]
        public string Email { get; set; }
        [Column(TypeName = "Date")]
        public DateTime? BirthDate { get; set; }
        [StringLength(1)]
        public string Gender { get; set; }
    }

    public class PulseUserClaim : IUserClaim
    {
        public PulseUserClaim() { }

        [Key]
        public string Key { get; set; }
        public string UserId { get; set; }
        public string ClaimType { get; set; }
        public string ClaimValue { get; set; }

    }

    public class PulseUserSecret : IUserSecret
    {
        public PulseUserSecret() { }
        public PulseUserSecret(string userName, string secret)
        {
            UserName = userName;
            Secret = secret;
        }

        [Key]
        public string UserName { get; set; }
        public string Secret { get; set; }

    }

    public class PulseUserLogin : IUserLogin
    {
        public PulseUserLogin() { }
        public PulseUserLogin(string userId, string loginProvider, string providerKey) 
        {
            LoginProvider = LoginProvider;
            ProviderKey = providerKey;
            UserId = userId;
        }

        [Key, Column(Order = 0)]
        public string LoginProvider { get; set; }
        [Key, Column(Order = 1)]
        public string ProviderKey { get; set; }
        public string UserId { get; set; }
    }

    public class PulseRole : IRole
    {
        public PulseRole() { }
        public PulseRole(string roleId)
        {
            Id = roleId;
        }

        [Key]
        public string Id { get; set; }
    }

    public class PulseUserRole : IUserRole
    {
        public PulseUserRole() { }

        [Key, Column(Order = 0)]
        public string RoleId { get; set; }
        [Key, Column(Order = 1)]
        public string UserId { get; set; }
    }

    public class PulseUserContext : IdentityStoreContext
    {
        public PulseUserContext(DbContext db) : base(db)
        {
            Users = new UserStore<PulseUser>(db);
            Logins = new UserLoginStore<PulseUserLogin>(db);
            Roles = new RoleStore<PulseRole, PulseUserRole>(db);
            Secrets = new UserSecretStore<PulseUserSecret>(db);
            UserClaims = new UserClaimStore<PulseUserClaim>(db);
        }
    }

    public class PulseDbContext : IdentityDbContext<PulseUser, PulseUserClaim, PulseUserSecret, PulseUserLogin, PulseRole, PulseUserRole>
    {
    }
}

Controllers/AccountController.csの変更

public AccountController() 
{

    IdentityStore = new IdentityStoreManager(new PulseUserContext(new PulseDbContext()));
    AuthenticationManager = new IdentityAuthenticationManager(IdentityStore);
}

//
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
    if (ModelState.IsValid)
    {
        try
        {
            // Create a profile, password, and link the local login before signing in the user
            PulseUser user = new PulseUser(model.UserName);
            if (await IdentityStore.CreateLocalUser(user, model.Password))
            {
                await AuthenticationManager.SignIn(HttpContext, user.Id, isPersistent: false);
                return RedirectToAction("Index", "Home");
            }
            else
            {
                ModelState.AddModelError("", "Failed to register user name: " + model.UserName);
            }
        }
        catch (IdentityException e)
        {
            ModelState.AddModelError("", e.Message);
        }
    }

    // If we got this far, something failed, redisplay form
    return View(model);
}

上で述べたように、この実装はCreateLocalUserメソッドが失敗すると失敗します(Microsoft.AspNet.Identity.EntityFramework)。理由がわかりません。

22
Vito

ここでの問題は、IdentityStoreManagerがIDEFモデルのデフォルトの実装に強く依存していることです。たとえば、CreateLocalUserメソッドは、UserSecretオブジェクトとUserLoginオブジェクトを作成してストアに保存しますが、ストアがデフォルトのモデルタイプを使用していない場合は機能しません。そのため、モデルタイプをカスタマイズすると、IdentityStoreManagerではスムーズに機能しません。

IUserモデルのみをカスタマイズするため、コードを簡略化して、デフォルトのIDユーザーからカスタムユーザーを継承し、IDEFモデルから他のモデルを再利用しました。

using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Linq;
using System.Web;

namespace WebApplication11.Models
{
    public class PulseUser : User
    {
        public PulseUser() { }
        public PulseUser(string userName) : base(userName)
        {
        }

        [StringLength(100)]
        public string Email { get; set; }
        [Column(TypeName = "Date")]
        public DateTime? BirthDate { get; set; }
        [StringLength(1)]
        public string Gender { get; set; }
    }

    public class PulseUserContext : IdentityStoreContext
    {
        public PulseUserContext(DbContext db) : base(db)
        {
            this.Users = new UserStore<PulseUser>(this.DbContext);
        }
    }

    public class PulseDbContext : IdentityDbContext<PulseUser, UserClaim, UserSecret, UserLogin, Role, UserRole>
    {
    }
}

上記のコードは、IdentityAPIのプレビューバージョンで機能するはずです。

次のリリースのIdentityStoreManager APIはすでにこの問題を認識しており、EF以外のすべての依存関係コードを基本クラスに変更して、継承してカスタマイズできるようにしています。ここですべての問題を解決するはずです。ありがとう。

12
Hongye Sun

PulseUser.Idは文字列として定義されていますが、値に設定されていないようです。 IDにGUIDを使用するつもりでしたか?使用する場合は、コンストラクターで初期化してください。

    public PulseUser() : this(String.Empty) { }
    public PulseUser(string userName) 
    {
        UserName = userName;
        Id = Guid.NewGuid().ToString();
    }

また、ユーザー名がまだ存在していないことを確認することもできます。 PulseDbContextのDbEntityValidationResultのオーバーライドを見てください。例を確認するには、VS2013で新しいMVCプロジェクトを実行してください。

1
Grant

RTMに行くときにこれに多くの変更があるので、すべてのIDサインインなどにWebApiコントローラーを使用するSPAテンプレートを更新しました。あなたがそれを見たことがなければ、それは本当にクールなテンプレートです。

すべてのコードをここに配置します: https://github.com/s093294/aspnet-identity-rtm/tree/master

(注意してください、それはインスピレーションのためだけです。私はそれを機能させるだけで、それ以上は何もしませんでした。適切にバグが1つか2つあります)。

0