ASP.NET Identity with EF Database First MVC5


88

Database FirstとEDMXで新しいAsp.net Identityを使用することは可能ですか?それとも最初にコードだけですか?

これが私がしたことです:

1)新しいMVC5プロジェクトを作成し、新しいIDでデータベースに新しいユーザーテーブルとロールテーブルを作成しました。

2)次に、Database First EDMXファイルを開き、新しいIdentity Usersテーブルにドラッグしました。それに関連する他のテーブルがあるからです。

3)EDMXを保存すると、Database First POCOジェネレーターがユーザークラスを自動作成します。ただし、UserManagerおよびRoleManagerは、新しいIdentity名前空間(Microsoft.AspNet.Identity.IUser)から継承するUserクラスを想定しているため、POCO Userクラスを使用しても機能しません。

私のPOCO生成クラスを編集して、ユーザークラスがIUserから継承するようにすることは、可能な解決策だと思いますか?

または、ASP.NET IdentityはCode First Designとのみ互換性がありますか?

++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++

更新:以下のAnders Abelの提案に従って、これは私がやったことです。それはうまくいきますが、もっとエレガントな解決策があるかどうか疑問に思っています。

1)自動生成されたエンティティと同じ名前空間内に部分クラスを作成して、エンティティUserクラスを拡張しました。

namespace MVC5.DBFirst.Entity
{
    public partial class AspNetUser : IdentityUser
    {
    }
}

2)DBContextではなくIdentityDBContextから継承するようにDataContextを変更しました。EDMXを更新し、DBContextクラスとEntityクラスを再生成するたびに、これをこれに戻す必要があることに注意してください。

 public partial class MVC5Test_DBEntities : IdentityDbContext<AspNetUser>  //DbContext

3)自動生成されたユーザーエンティティクラス内で、次の4つのフィールドにオーバーライドキーワードを追加するか、これらのフィールドはIdentityUserから継承されるため(ステップ1)、コメント化する必要があります。EDMXを更新し、DBContextクラスとEntityクラスを再生成するたびに、これをこれに戻す必要があることに注意してください。

    override public string Id { get; set; }
    override public string UserName { get; set; }
    override public string PasswordHash { get; set; }
    override public string SecurityStamp { get; set; }

1
実装のサンプルコードはありますか?上記を複製しようとすると、ユーザーをログインまたは登録しようとすると、エラーが発生します。「エンティティタイプAspNetUserは、現在のコンテキストのモデルの一部ではありません」ここで、AspNetUserは私のユーザーエンティティ
Tim

AspNetUserテーブルをEDMXに追加しましたか?また、AccountControllerがApplicationContextではなくMVC5Test_DBEntities(またはDBコンテキストの名前)を使用していることを確認してください。
Patrick Tran、

8
ASP.NET Identityは、____の山です。データベースファーストの恐ろしいサポート、ドキュメントなし、不十分な参照制約(SQLサーバーではON CASCADE DELETEがない)、IDに文字列を使用(パフォーマンスの問題とインデックスの断片化)。そして、これはアイデンティティフレームワークでの彼らの297回目の試みです
DeepSpace101

1
@ DeepSpace101 Identityは、Code-firstと同じようにDB-firstをサポートします。テンプレートはコードファーストを実行するように設定されているため、テンプレートから開始する場合は、いくつかの点を変更する必要があります。カスケード削除は問題なく機能します。文字列をintに簡単に変更できます。以下の私の答えを参照してください。

1
@靴私はあなたが間違っているかもしれないと思います。これをdb-firstで実装する方法に関する実用的な包括的な例/チュートリアルはまだありません(ドキュメントなし)。APIは、プロパティIdentityUser.Rolesを介してジャンクションテーブル "IdentityUserRoles"を参照しようとします。ジャンクションテーブルはエンティティとして公開されていないため(参照の制約が不十分)、EF db-firstの関係が壊れます。IDの文字列については、継承されたクラスでタイプパラメータを指定することでカスタマイズできるため、同意しません。要約すると、最初はDBをまったく考えていなかったようです。
2016

回答:


16

POCOとDatabase FirstでIDシステムを使用することは可能ですが、いくつかの微調整を行う必要があります。

  1. POCO生成用の.ttファイルを更新して、エンティティークラスを作成しますpartial。これにより、別のファイルで追加の実装を提供できるようになります。
  2. User別のファイルでクラスの部分的な実装を行う

 

partial User : IUser
{
}

これUserにより、実際に生成されたファイルに触れることなく、クラスに適切なインターフェースが実装されます(生成されたファイルの編集は常に悪い考えです)。


ありがとう。うまくいったら、これを試して報告する必要があります。
Patrick Tran

あなたの言ったことを試してみました。更新された情報については私の投稿を参照してください...しかし、解決策はあまりエレガントではありませんでした:/
Patrick Tran

私も同じ問題を抱えています。DBファーストに関するドキュメントは非常にまばらであるようです。これはいい提案ですが、私はあなたが正しいと思います、それはまったくうまくいきません
Phil

4
ハックではない解決策はまだありますか?
user20358 14

私はOnion Architectureを使用しており、すべてのPOCOがコアにあります。そこでは、IUserの使用は推奨されません。他の解決策はありますか?
ウスマンハリド

13

私の手順は非常に似ていますが、共有したいと思いました。

1)新しいMVC5プロジェクトを作成する

2)新しいModel.edmxを作成します。新しいデータベースでテーブルがない場合でも。

3)web.configを編集して、この生成された接続文字列を置き換えます。

<add name="DefaultConnection" connectionString="Data Source=(LocalDb)\v11.0;AttachDbFilename=|DataDirectory|\aspnet-SSFInventory-20140521115734.mdf;Initial Catalog=aspnet-SSFInventory-20140521115734;Integrated Security=True" providerName="System.Data.SqlClient" />

この接続文字列で:

<add name="DefaultConnection" connectionString="Data Source=.\SQLExpress;database=SSFInventory;integrated security=true;" providerName="System.Data.SqlClient" />

その後、アプリケーションをビルドして実行します。ユーザーを登録すると、テーブルが作成されます。


1
これで問題が解決し、データベースにアプリケーションユーザーが表示されませんでしたが、デフォルトの接続を置き換えた後、問題なく動作しました。
Hassen Ch。

10

編集: MVC5 CodePlexプロジェクトテンプレートの最初のEFデータベースを備えたASP.NET ID。


既存のデータベースを使用して、ApplicationUserとの関係を作成したいと考えていました。これはSQL Serverを使用して行った方法ですが、同じアイデアがおそらくどのDBでも機能します。

  1. MVCプロジェクトを作成する
  2. Web.configのDefaultConnectionの下にリストされているDBを開きます。(aspnet- [timestamp]またはそのようなものと呼ばれます。)
  3. データベーステーブルのスクリプトを作成します。
  4. スクリプトテーブルをSQL Server Management Studioの既存のデータベースに挿入します。
  5. 関係をカスタマイズしてApplicationUserに追加する(必要な場合)。
  6. 新しいWebプロジェクトを作成> MVC> DB最初のプロジェクト> EFでDBをインポート...挿入したIDクラスを除外します。
  7. IdentityModels.cs ApplicationDbContextを変更する:base("DefaltConnection")プロジェクトのDbContextを使用します。

編集:Asp.Net IDクラス図 ここに画像の説明を入力してください


6
問題はDBContextではありませんが、UserManagerとRoleManagerはMicrosoft.AspNet.Identity.EntityFramework.IdentityUserから継承するクラスを期待しています
Patrick Tran

パブリッククラスIdentityDbContext <TUser>:DbContextここでTUser:Microsoft.AspNet.Identity.EntityFramework.IdentityUser。最初にデータベースを使用する場合、生成されたエンティティークラスは基本クラスから継承されません。
Patrick Tran

次に、エンティティフレームワークでクラスを生成するときに、それらをデータベースから除外します。
2013年

IdentityテーブルをEDMXから除外すると、UserIDへの外部キーを持つ他のクラスのNavigationプロパティが失われます
Patrick Tran

34
最初にコードに移動することに抵抗はありません...いくつかのシナリオや企業では、db管理者がコーダーではなくベーステーブルを作成するだけです。
Patrick Tran

8

IdentityUserこれはUserStore、認証にによって使用されるコードファーストオブジェクトであるため、ここでは意味がありません。独自のUserオブジェクトを定義した後IUserUserManagerクラスで使用されるを実装する部分クラスを実装しました。文字列Idではintなくs にしたかったので、UserIDのtoString()を返します。同様に、私は無人になりたかっnUsername

public partial class User : IUser
{

    public string Id
    {
        get { return this.UserID.ToString(); }
    }

    public string UserName
    {
        get
        {
            return this.Username;
        }
        set
        {
            this.Username = value;
        }
    }
}

あなたは決して必要ありませんIUser。これは、が使用するインターフェイスにすぎませんUserManager。したがって、別の「IUser」を定義する場合は、このクラスを書き直して独自の実装を使用する必要があります。

public class UserManager<TUser> : IDisposable where TUser: IUser

UserStoreユーザー、クレーム、ロールなどのすべてのストレージを処理する独自のコードを記述します。コードを最初に実行するすべてのインターフェイスを実装し、「User」がエンティティオブジェクトである場所にUserStore変更where TUser : IdentityUserwhere TUser : Userます

public class MyUserStore<TUser> : IUserLoginStore<TUser>, IUserClaimStore<TUser>, IUserRoleStore<TUser>, IUserPasswordStore<TUser>, IUserSecurityStampStore<TUser>, IUserStore<TUser>, IDisposable where TUser : User
{
    private readonly MyAppEntities _context;
    public MyUserStore(MyAppEntities dbContext)
    { 
        _context = dbContext; 
    }

    //Interface definitions
}

以下は、いくつかのインターフェース実装の例です。

async Task IUserStore<TUser>.CreateAsync(TUser user)
{
    user.CreatedDate = DateTime.Now;
    _context.Users.Add(user);
    await _context.SaveChangesAsync();
}

async Task IUserStore<TUser>.DeleteAsync(TUser user)
{
    _context.Users.Remove(user);
    await _context.SaveChangesAsync();
}

MVC 5テンプレートを使用して、AccountControllerを次のように変更しました。

public AccountController()
        : this(new UserManager<User>(new MyUserStore<User>(new MyAppEntities())))
{
}

ログインすると、独自のテーブルを使用できるようになります。


1
私は最近これを実装する機会があり、何らかの理由で(IDの3.0の更新のためと考えています)、IdentityUserを継承してからプロパティをオーバーライドすることでログインを実装できませんでした。しかし、カスタムUserStoreを作成し、IUserを継承することはうまくいきました。アップデートを与えるだけで、誰かがこれが便利だと思うかもしれません。
Naz Ekin

すべてのインターフェース実装またはプロジェクト全体へのリンクを提供できますか?
DespeiL 2016

ここで部分クラスを使用した特定の理由はありますか?
user8964654

edmxを使用してモデルを生成している場合は、部分クラスを使用する必要があります。最初にコードを実行している場合は、これを省略できます
Shoe


3

良い質問。

私はデータベースが一番の人です。コードの最初のパラダイムは、私には緩やかでありがちなものであるように見え、「マイグレーション」はエラーが発生しやすいようです。

私はaspnet IDスキーマをカスタマイズし、移行に煩わされないようにしたいと思っていました。私はVisual Studioデータベースプロジェクト(sqlpackage、data-dude)に精通しており、スキーマのアップグレードでそれがどのようにうまく機能するかを理解しています。

私の単純な解決策は:

1)aspnet IDスキーマをミラーリングするデータベースプロジェクトを作成する2)このプロジェクトの出力(.dacpac)をプロジェクトリソースとして使用する3)必要に応じて.dacpacをデプロイする

MVC5の場合、ApplicationDbContextクラスを変更するとこのようになります...

1)実装する IDatabaseInitializer

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>, IDatabaseInitializer<ApplicationDbContext> { ... }

2)コンストラクターで、このクラスがデータベースの初期化を実装することを通知します。

Database.SetInitializer<ApplicationDbContext>(this);

3)実装InitializeDatabase

ここでは、DacFXを使用して.dacpacをデプロイすることを選択しました

    void IDatabaseInitializer<ApplicationDbContext>.InitializeDatabase(ApplicationDbContext context)
    {
        using (var ms = new MemoryStream(Resources.Binaries.MainSchema))
        {
            using (var package = DacPackage.Load(ms, DacSchemaModelStorageType.Memory))
            {
                DacServices services = new DacServices(Database.Connection.ConnectionString);
                var options = new DacDeployOptions
                {
                    VerifyDeployment = true,
                    BackupDatabaseBeforeChanges = true,
                    BlockOnPossibleDataLoss = false,
                    CreateNewDatabase = false,
                    DropIndexesNotInSource = true,
                    IgnoreComments = true,

                };
                services.Deploy(package, Database.Connection.Database, true, options);
            }
        }
    }

2

私はこれに数時間かけて取り組み、ついに私がブログ共有している解決策を見つけました。基本的に、stinkの回答で述べたすべてを実行する必要がありますが、1つ追加する必要があります。IdentityFrameworkが、アプリケーションエンティティに使用されるEntity Framework接続文字列の上に特定のSQL-Client接続文字列を持っていることを確認します。

要約すると、アプリケーションはIdentity Framework用の接続文字列とアプリケーションエンティティ用の接続文字列を使用します。各接続文字列は異なるタイプです。完全なチュートリアルについては、私のブログ投稿をお読みください。


私はあなたがあなたのブログで言及したすべてを2回試しました、それは私のためにうまくいきませんでした。
Badhon Jain、2015

@Badhon申し訳ありませんが、私のブログ投稿の説明がうまく機能しませんでした。私の記事に続いて成功を収めた数え切れないほどの人々に感謝の気持ちを伝えてきました。マイクロソフトが何かを更新した場合、結果に影響を与える可能性があることを常に覚えておいてください。Identity Framework 2.0を使用したASP.NET MVC 5の記事を書きました。それを超えると問題が発生する可能性がありますが、これまでのところ、成功を示す非常に最近のコメントを受け取っています。あなたの問題についてもっと聞きたいです。
ダニエルイーグル

もう一度フォローしてみましょう。私が直面した問題は、カスタムデータベースを使用できず、自動構築されたデータベースを使用していたことです。とにかく、私はあなたの記事で何か間違ったことを言うつもりはありませんでした。あなたの知識を共有してくれてありがとう。
Badhon Jain

1
@Badhon友だちを心配する必要はありません。記事のどこかに問題があると言ったことはありませんでした。私たちは皆、さまざまなエッジケースで独特の状況を持っているため、他の人にとってはうまくいくことが必ずしも私たちにとってうまくいくとは限らないことがあります。あなたがあなたの問題を解決できたといいのですが。
Daniel Eagle

1

モデルクラスを保持するエンティティモデルDLLプロジェクトがあります。また、すべてのデータベーススクリプトを含むデータベースプロジェクトも保持しています。私のアプローチは次のとおりでした

1)最初にデータベースを使用してEDMXを含む独自のプロジェクトを作成します

2)dbのテーブルをスクリプト化し、localDB(データ接続)に接続されたVS2013を使用して、スクリプトをデータベースプロジェクトにコピーし、カスタム列を追加します(例:BirthDate [DATE] not null)

3)データベースをデプロイする

4)モデル(EDMX)プロジェクトを更新します。モデルプロジェクトに追加します。

5)アプリケーションクラスにカスタム列を追加する

public class ApplicationUser : IdentityUser
{
    public DateTime BirthDate { get; set; }
}

MVCプロジェクトで、AccountControllerは以下を追加しました。

IDプロバイダーは、SQL接続文字列が機能するために必要です。データベースの接続文字列を1つだけ保持し、EF接続文字列からプロバイダー文字列を抽出します

public AccountController()
{
   var connection = ConfigurationManager.ConnectionStrings["Entities"];
   var entityConnectionString = new EntityConnectionStringBuilder(connection.ConnectionString);
        UserManager =
            new UserManager<ApplicationUser>(
                new UserStore<ApplicationUser>(
                    new ApplicationDbContext(entityConnectionString.ProviderConnectionString)));
}

1

@ JoshYates1980が最も簡単な答えを持っていることがわかりました。

一連の試行錯誤の後、Joshが提案したことをconnectionString行い、生成したDB接続文字列に置き換えました。私が最初に混乱したのは次の投稿でした:

ASP.NET MVC5 ID認証を既存のデータベースに追加する方法

@Winからの受け入れられた回答がApplicationDbContext()接続名を変更するように述べている場所。エンティティと、データベース接続文字列が生成されてWeb.configファイルに追加されるデータベース/モデルの最初のアプローチを使用している場合、これは少しあいまいです。

ApplicationDbContext()接続名がされてマッピングされたのデフォルトの接続にWeb.configファイル。したがって、ジョシュの方法が最も効果的ですが、作るためにApplicationDbContext()変更することを確認しながら、より読みやすい私はもともと投稿@Winとしてデータベース名に名前を変更することをお勧めconnectionStringで「DefaultConnectionを」のためのWeb.configエンティティをしてコメントアウトおよび/またはremov生成されたデータベースが含まれます。

コード例:

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.