ASP.NET-Identität mit EF-Datenbank Erste MVC5

88

Ist es möglich, die neue Asp.net-Identität mit Database First und EDMX zu verwenden? Oder nur mit Code zuerst?

Folgendes habe ich getan:

1) Ich habe ein neues MVC5-Projekt erstellt und die neue Identität die neuen Benutzer- und Rollentabellen in meiner Datenbank erstellen lassen.

2) Ich habe dann meine Database First EDMX-Datei geöffnet und in die neue Identity Users-Tabelle gezogen, da ich andere Tabellen habe, die sich darauf beziehen.

3) Beim Speichern des EDMX erstellt der Database First POCO-Generator automatisch eine Benutzerklasse. UserManager und RoleManager erwarten jedoch eine Benutzerklasse, die vom neuen Identitätsnamensraum (Microsoft.AspNet.Identity.IUser) erbt, sodass die Verwendung der POCO-Benutzerklasse nicht funktioniert.

Ich denke, eine mögliche Lösung besteht darin, meine POCO-Generierungsklassen so zu bearbeiten, dass meine Benutzerklasse von IUser erbt.

Oder ist ASP.NET Identity nur mit Code First Design kompatibel?

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

Update: Nach dem folgenden Vorschlag von Anders Abel habe ich Folgendes getan. Es funktioniert, aber ich frage mich, ob es eine elegantere Lösung gibt.

1) Ich habe meine Entitätsbenutzerklasse erweitert, indem ich eine Teilklasse im selben Namespace wie meine automatisch generierten Entitäten erstellt habe.

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

2) Ich habe meinen DataContext so geändert, dass er von IdentityDBContext anstelle von DBContext erbt. Beachten Sie, dass Sie jedes Mal, wenn Sie Ihren EDMX aktualisieren und die DBContext- und Entity-Klassen neu generieren, dies auf diesen Wert zurücksetzen müssen.

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

3) Innerhalb Ihrer automatisch generierten Benutzerentitätsklasse müssen Sie das Schlüsselwort override zu den folgenden 4 Feldern hinzufügen oder diese Felder auskommentieren, da sie von IdentityUser geerbt werden (Schritt 1). Beachten Sie, dass Sie jedes Mal, wenn Sie Ihren EDMX aktualisieren und die DBContext- und Entity-Klassen neu generieren, dies auf diesen Wert zurücksetzen müssen.

    override public string Id { get; set; }
    override public string UserName { get; set; }
    override public string PasswordHash { get; set; }
    override public string SecurityStamp { get; set; }
Patrick Tran
quelle
1
Haben Sie Beispielcode für Ihre Implementierung? Wenn ich versuche, das oben Gesagte zu replizieren, wird beim Versuch, einen Benutzer anzumelden oder zu registrieren, eine Fehlermeldung angezeigt. "Der Entitätstyp AspNetUser ist nicht Teil des Modells für den aktuellen Kontext", wobei AspNetUser meine Benutzerentität ist
Tim
Haben Sie die AspNetUser-Tabelle zu Ihrem EDMX hinzugefügt? Stellen Sie außerdem sicher, dass Ihr AccountController MVC5Test_DBEntities (oder einen beliebigen DB-Kontext) anstelle von ApplicationContext verwendet.
Patrick Tran
8
ASP.NET Identity ist ein dampfender Haufen von ____. Schreckliche Unterstützung für Datenbank-First, keine Dokumentation, schlechte referenzielle Einschränkungen (fehlt bei CASCADE DELETE auf SQL Server) und verwendet Zeichenfolgen für IDs (Leistungsproblem und Indexfragmentierung). Und dies ist ihr 297. Versuch eines Identitätsrahmens ...
DeepSpace101
1
@ DeepSpace101 Identity unterstützt DB-first genauso wie Code-first. Die Vorlagen sind so eingerichtet, dass sie zuerst Code ausführen. Wenn Sie also von einer Vorlage ausgehen, müssen Sie einige Dinge ändern. Das Löschen von Kaskaden funktioniert einwandfrei. Sie können die Zeichenfolgen problemlos in Ints ändern. Siehe meine Antwort unten.
Schuh
1
@ Schuh Ich muss sagen, dass ich denke, dass Sie falsch sein können. Ich habe noch kein funktionierendes, umfassendes Beispiel / Tutorial gefunden, wie man dies in db-first implementiert (keine Dokumentation). Die API versucht, über die Eigenschaft IdentityUser.Roles auf die Junction-Tabelle "IdentityUserRoles" zu verweisen, wodurch die Beziehung in EF db-first unterbrochen wird, da Junction-Tabellen nicht als Entitäten verfügbar gemacht werden (schlechte referenzielle Einschränkungen). Ich bin mit den Zeichenfolgen für IDs nicht einverstanden, da diese durch Angabe der Typparameter in den geerbten Klassen angepasst werden können. Um es zusammenzufassen, sieht es für mich so aus, als hätten sie überhaupt keine DB im Sinn.
Einseitig

Antworten:

16

Es sollte möglich sein, das Identitätssystem mit POCO und Database First zu verwenden, aber Sie müssen einige Änderungen vornehmen:

  1. Aktualisieren Sie die .tt-Datei für die POCO-Generierung, um die Entitätsklassen zu erstellen partial. Dadurch können Sie zusätzliche Implementierungen in einer separaten Datei bereitstellen.
  2. Nehmen Sie eine teilweise Implementierung der UserKlasse in einer anderen Datei vor

 

partial User : IUser
{
}

Dadurch Userimplementiert die Klasse die richtige Schnittstelle, ohne die tatsächlich generierten Dateien zu berühren (das Bearbeiten generierter Dateien ist immer eine schlechte Idee).

Anders Abel
quelle
Vielen Dank. Ich muss es versuchen und berichten, wenn es klappt.
Patrick Tran
Ich habe versucht, was du erwähnt hast. Siehe meinen Beitrag für aktualisierte Informationen ... aber die Lösung war nicht sehr elegant: /
Patrick Tran
Ich habe genau das gleiche Problem gehabt. Es scheint, dass die Dokumentation zu DB-first sehr spärlich ist. Dies ist ein netter Vorschlag, aber ich denke, Sie haben Recht, es funktioniert nicht ganz
Phil
4
Gibt es noch eine Lösung, die kein Hack ist?
user20358
Ich verwende Onion Architecture und alle POCOs sind in Core. Dort wird die Verwendung von IUser nicht empfohlen. Also eine andere Lösung?
Usman Khalid
13

Meine Schritte sind sehr ähnlich, aber ich wollte teilen.

1) Erstellen Sie ein neues MVC5-Projekt

2) Erstellen Sie eine neue Model.edmx. Auch wenn es sich um eine neue Datenbank handelt und keine Tabellen enthält.

3) Bearbeiten Sie die Datei web.config und ersetzen Sie diese generierte Verbindungszeichenfolge:

<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" />

mit diesem Verbindungsstring:

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

Erstellen Sie anschließend die Anwendung und führen Sie sie aus. Registrieren Sie einen Benutzer und dann werden die Tabellen erstellt.

JoshYates1980
quelle
1
Dies löste ein Problem. Ich konnte den Anwendungsbenutzer nicht in der Datenbank sehen, aber nach dem Ersetzen der Standardverbindung funktionierte es.
Hassen Ch.
10

BEARBEITEN: ASP.NET-Identität mit EF-Datenbank zuerst für MVC5-CodePlex-Projektvorlage.


Ich wollte eine vorhandene Datenbank verwenden und Beziehungen zu ApplicationUser erstellen. So habe ich es mit SQL Server gemacht, aber die gleiche Idee würde wahrscheinlich mit jeder Datenbank funktionieren.

  1. Erstellen Sie ein MVC-Projekt
  2. Öffnen Sie die unter DefaultConnection in Web.config aufgeführte Datenbank. Es wird aufgerufen (aspnet- [Zeitstempel] oder so ähnlich.)
  3. Skripten Sie die Datenbanktabellen.
  4. Fügen Sie die Skripttabellen in die vorhandene Datenbank in SQL Server Management Studio ein.
  5. Passen Sie die Beziehungen zu ApplicationUser an und fügen Sie sie hinzu (falls erforderlich).
  6. Neues Webprojekt erstellen> MVC> DB Erstes Projekt> DB mit EF importieren ... Mit Ausnahme der von Ihnen eingefügten Identitätsklassen.
  7. Ändern Sie in IdentityModels.cs den ApplicationDbContext :base("DefaltConnection"), um den DbContext Ihres Projekts zu verwenden.

Bearbeiten: Asp.Net-Identitätsklassendiagramm Geben Sie hier die Bildbeschreibung ein

stinken
quelle
6
Das Problem ist nicht DBContext, aber UserManager und RoleManager erwarten eine Klasse, die von Microsoft.AspNet.Identity.EntityFramework.IdentityUser
Patrick Tran
öffentliche Klasse IdentityDbContext <TUser>: DbContext wobei TUser: Microsoft.AspNet.Identity.EntityFramework.IdentityUser. Wenn Sie zuerst die Datenbank verwenden, erben die generierten Entitätsklassen keine Basisklassen.
Patrick Tran
Schließen Sie sie dann einfach aus der Datenbank aus, wenn Sie die Klassen mit dem Entity Framework generieren.
stinken
Wenn Sie die Identitätstabellen aus dem EDMX ausschließen, verlieren Sie die Navigationseigenschaften in anderen Klassen, die Fremdschlüssel für Ihre Benutzer-ID haben
Patrick Tran
34
Ich zögere nicht, zuerst zum Code zu wechseln ... Es ist nur so, dass in einigen Szenarien und Unternehmen der Datenbankadministrator die Basistabellen erstellt, nicht den Codierer.
Patrick Tran
8

IdentityUserist hier wertlos, weil es das Code-First-Objekt ist, das von der UserStorezur Authentifizierung verwendet wird. Nachdem ich mein eigenes UserObjekt definiert hatte, implementierte ich eine Teilklasse, die implementiert und IUserdie von der UserManagerKlasse verwendet wird. Ich wollte, dass mein Ids intanstelle eines Strings ist, also gebe ich einfach die UserID an String () zurück. Ebenso wollte ich nin Usernameuncapitalized sein.

public partial class User : IUser
{

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

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

Sie brauchen auf keinen Fall IUser. Es ist nur eine Schnittstelle, die von der UserManager. Wenn Sie also einen anderen "IUser" definieren möchten, müssen Sie diese Klasse neu schreiben, um Ihre eigene Implementierung zu verwenden.

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

Sie schreiben jetzt Ihre eigene, UserStoredie die gesamte Speicherung von Benutzern, Ansprüchen, Rollen usw. übernimmt. Implementieren Sie die Schnittstellen von allem, was der Code zuerst UserStoretut, und ändern Sie dort where TUser : IdentityUser, where TUser : Userwo "Benutzer" Ihr Entitätsobjekt ist

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
}

Hier einige Beispiele für einige der Schnittstellenimplementierungen

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

Mit der MVC 5-Vorlage habe ich die so geändert AccountController, dass sie so aussieht.

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

Jetzt sollte die Anmeldung mit Ihren eigenen Tabellen funktionieren.

Schuh
quelle
1
Ich hatte kürzlich die Möglichkeit, dies zu implementieren, und aus irgendeinem Grund (ich glaube aufgrund des 3.0-Updates der Identität) konnte ich die Anmeldung nicht implementieren, indem ich IdentityUser erbte und dann die Eigenschaften überschrieb. Das Schreiben eines benutzerdefinierten UserStore und das Erben von IUser funktionierten jedoch einwandfrei. Nur ein Update geben, vielleicht findet jemand dies nützlich.
Naz Ekin
Können Sie einen Link für die gesamte Schnittstellenimplementierung oder für das gesamte Projekt angeben?
DespeiL
Gibt es einen bestimmten Grund, warum Sie hier eine Teilklasse verwendet haben?
Benutzer8964654
Wenn Sie einen edmx zum Generieren Ihrer Modelle verwenden, müssen Sie eine Teilklasse verwenden. Wenn Sie zuerst Code machen, können Sie dies weglassen
Schuh
3

Gute Frage.

Ich bin eher eine Datenbank-First-Person. Das erste Code-Paradigma scheint mir locker zu sein, und die "Migrationen" scheinen zu fehleranfällig zu sein.

Ich wollte das Aspnet-Identitätsschema anpassen und mich nicht um Migrationen kümmern. Ich bin mit Visual Studio-Datenbankprojekten (SQL Package, Data-Dude) bestens vertraut und weiß, wie gut es beim Aktualisieren von Schemas funktioniert.

Meine vereinfachende Lösung lautet:

1) Erstellen Sie ein Datenbankprojekt, das das Aspnet-Identitätsschema widerspiegelt. 2) Verwenden Sie die Ausgabe dieses Projekts (.dacpac) als Projektressource. 3) Stellen Sie das .dacpac bei Bedarf bereit

Für MVC5 ApplicationDbContextscheint das Ändern der Klasse dies in Gang zu bringen ...

1) Implementieren IDatabaseInitializer

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

2) Signalisieren Sie im Konstruktor, dass diese Klasse die Datenbankinitialisierung implementiert:

Database.SetInitializer<ApplicationDbContext>(this);

3) Implementieren InitializeDatabase:

Hier habe ich mich für DacFX entschieden und mein .dacpac bereitgestellt

    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);
            }
        }
    }
Aaron Hudon
quelle
2

Ich verbrachte mehrere Stunden durch diese Arbeit und fand schließlich eine Lösung , die ich auf meinem Blog geteilt habe hier . Grundsätzlich müssen Sie alles tun, was in der Antwort von stink angegeben ist , aber mit einer zusätzlichen Sache: Sicherstellen, dass Identity Framework über die für Ihre Anwendungsentitäten verwendete Entity Framework-Verbindungszeichenfolge eine bestimmte SQL-Client-Verbindungszeichenfolge verfügt.

Zusammenfassend verwendet Ihre Anwendung eine Verbindungszeichenfolge für Identity Framework und eine andere für Ihre Anwendungsentitäten. Jede Verbindungszeichenfolge ist von einem anderen Typ. Lesen Sie meinen Blog-Beitrag für ein vollständiges Tutorial.

Daniel Eagle
quelle
Ich habe alles, was du in deinem Blog erwähnt hast, zweimal ausprobiert, es hat für mich nicht geklappt.
Badhon Jain
@Badhon Es tut mir sehr leid, dass die Anweisungen in meinem Blog-Beitrag für Sie nicht geklappt haben. Unzählige Menschen haben sich bedankt, als sie nach meinem Artikel Erfolg hatten. Denken Sie immer daran, dass wenn Microsoft etwas aktualisiert, dies das Ergebnis beeinflussen kann. Ich habe den Artikel für ASP.NET MVC 5 mit Identity Framework 2.0 geschrieben. Alles darüber hinaus kann Probleme verursachen, aber bisher habe ich sehr aktuelle Kommentare erhalten, die auf Erfolg hinweisen. Ich würde gerne mehr über Ihr Problem erfahren.
Daniel Eagle
Ich werde versuchen, noch einmal zu folgen. Das Problem war, dass ich meine benutzerdefinierte Datenbank nicht verwenden konnte. Sie verwendete die automatisch erstellte Datenbank. Jedenfalls wollte ich mit Ihrem Artikel nichts Falsches sagen. Vielen Dank für Ihr Wissen.
Badhon Jain
1
@Badhon Keine Sorge, mein Freund, ich hatte nie das Gefühl, dass du sagst, dass etwas mit dem Artikel nicht stimmt. Wir alle haben einzigartige Situationen mit verschiedenen Randfällen, so dass manchmal das, was für andere funktioniert, nicht unbedingt für uns funktioniert. Ich hoffe, Sie konnten Ihr Problem lösen.
Daniel Eagle
1

Wir haben ein Entity Model DLL-Projekt, in dem wir unsere Modellklasse behalten. Wir führen auch ein Datenbankprojekt mit allen Datenbankskripten. Mein Ansatz war wie folgt

1) Erstellen Sie zuerst Ihr eigenes Projekt mit EDMX mithilfe der Datenbank

2) Skripten Sie die Tabellen in Ihrer Datenbank. Ich habe VS2013 verwendet, das mit localDB (Data Connections) verbunden ist, und das Skript in das Datenbankprojekt kopiert. Fügen Sie benutzerdefinierte Spalten hinzu, z. B. BirthDate [DATE] not null

3) Stellen Sie die Datenbank bereit

4) Aktualisieren Sie das Modellprojekt (EDMX). Fügen Sie es dem Modellprojekt hinzu

5) Fügen Sie der Anwendungsklasse benutzerdefinierte Spalten hinzu

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

Im MVC-Projekt hat AccountController Folgendes hinzugefügt:

Der Identitätsanbieter möchte, dass eine SQL-Verbindungszeichenfolge funktioniert. Damit nur eine Verbindungszeichenfolge für die Datenbank erhalten bleibt, extrahieren Sie die Anbieterzeichenfolge aus der EF-Verbindungszeichenfolge

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

Ich fand, dass @ JoshYates1980 die einfachste Antwort hat.

Nach einer Reihe von Versuchen und Fehlern habe ich das getan, was Josh vorgeschlagen hat, und die connectionStringdurch meine generierte DB-Verbindungszeichenfolge ersetzt. Was mich ursprünglich verwirrte, war der folgende Beitrag:

So fügen Sie der vorhandenen Datenbank die ASP.NET MVC5-Identitätsauthentifizierung hinzu

Wo die akzeptierte Antwort von @Win angab, den ApplicationDbContext()Verbindungsnamen zu ändern . Dies ist etwas vage, wenn Sie Entity und einen ersten Ansatz für Datenbank / Modell verwenden, bei dem die Datenbankverbindungszeichenfolge generiert und der Web.configDatei hinzugefügt wird.

Der ApplicationDbContext()Verbindungsname wird abgebildet auf die Standardverbindung in der Web.configDatei. Daher funktioniert Joshs Methode am besten, aber um die ApplicationDbContext()Lesbarkeit zu verbessern, würde ich vorschlagen, den Namen in Ihren Datenbanknamen wie ursprünglich veröffentlicht zu ändern. Achten Sie darauf, den Namen connectionStringfür "DefaultConnection" in der zu ändern Web.configund die Entität zu kommentieren und / oder zu entfernen generierte Datenbank enthalten.

Codebeispiele:

TheSchnitz
quelle