web-dev-qa-db-de.com

Ändern des ID-Typs in Microsoft.AspNet.Identity.EntityFramework.IdentityUser

(ASP.NET MVC 5, EF6, VS2013)

Ich versuche herauszufinden, wie man ändern Sie den Typ des "Id" -Feldes von string in int im Typ:

Microsoft.AspNet.Identity.EntityFramework.IdentityUser

damit neue Benutzerkonten einer Ganzzahl-ID und nicht einer GUID zugeordnet werden können. Dies scheint jedoch komplizierter zu sein, als einfach eine neue Id-Eigenschaft mit dem Typ int in meiner abgeleiteten Benutzerklasse hinzuzufügen. Schauen Sie sich diese Methodensignatur an:

(von Assembly Microsoft.AspNet.Identity.Core.dll)

public class UserManager<TUser> : IDisposable where TUser : global::Microsoft.AspNet.Identity.IUser
  {
  ...
  public virtual Task<IdentityResult> AddLoginAsync(string userId, UserLoginInfo login);
  ...
  }

Es scheint also andere Methoden zu geben, die in das ASP.NET-Identitätsframework eingebunden sind und für die die Benutzer-ID eine Zeichenfolge sein muss. Muss ich diese Klassen auch neu implementieren?

Eine Erklärung, warum ich keine GUIDs für IDs in der Benutzertabelle speichern möchte:

- Es gibt andere Tabellen, die Daten über einen Fremdschlüssel mit der Benutzertabelle verknüpfen. (Wenn Benutzer Inhalte auf der Site speichern.) Ich sehe keinen Grund, den größeren Feldtyp zu verwenden und zusätzlichen Datenbankspeicherplatz ohne klare Vorteile auszugeben. (Ich weiß, dass es andere Posts gibt, in denen GUIDs im Vergleich zu int-IDs verwendet werden, aber es scheint, als würden viele vorschlagen, dass int-IDs schneller sind und weniger Speicherplatz belegen, was mich immer noch wundert.)

-Ich plane, einen erholsamen Endpunkt bereitzustellen, damit Benutzer Daten über einen bestimmten Benutzer abrufen können. Ich glaube:

/users/123/name

ist sauberer als

/users/{af54c891-69ba-4ddf-8cb6-00d368e58d77}/name

Weiß jemand, warum das ASP.NET-Team beschlossen hat, IDs auf diese Weise zu implementieren? Bin ich kurzsichtig, wenn ich versuche, dies in einen Int-Typ zu ändern? (Vielleicht gibt es Vorteile, die ich vermisse.)

Vielen Dank...

-Ben

66
BenjiFB

Wenn Sie also Int-IDs möchten, müssen Sie Ihre eigene POCO-IUser-Klasse erstellen und Ihren IUserStore für Ihre benutzerdefinierte IUser-Klasse in der Version 1.0 RTM implementieren.

Dies ist etwas, für das wir keine Zeit hatten zu unterstützen, aber ich bin gerade dabei, dies in 1.1 einfach (ier) zu machen. Hoffentlich wird bald etwas in den nächtlichen Builds verfügbar sein.

Aktualisiert mit 1.1-alpha1 Beispiel: Wie man nächtliche Gebäude bekommt

Wenn Sie auf die neuesten nächtlichen Versionen aktualisieren, können Sie die neuen 1.1-alpha1-APIs ausprobieren, die dies jetzt einfacher machen sollen: So sollte beispielsweise das Einstecken von Guids anstelle von Strings aussehen

    public class GuidRole : IdentityRole<Guid, GuidUserRole> { 
        public GuidRole() {
            Id = Guid.NewGuid();
        }
        public GuidRole(string name) : this() { Name = name; }
    }
    public class GuidUserRole : IdentityUserRole<Guid> { }
    public class GuidUserClaim : IdentityUserClaim<Guid> { }
    public class GuidUserLogin : IdentityUserLogin<Guid> { }

    public class GuidUser : IdentityUser<Guid, GuidUserLogin, GuidUserRole, GuidUserClaim> {
        public GuidUser() {
            Id = Guid.NewGuid();
        }
        public GuidUser(string name) : this() { UserName = name; }
    }

    private class GuidUserContext : IdentityDbContext<GuidUser, GuidRole, Guid, GuidUserLogin, GuidUserRole, GuidUserClaim> { }
    private class GuidUserStore : UserStore<GuidUser, GuidRole, Guid, GuidUserLogin, GuidUserRole, GuidUserClaim> {
        public GuidUserStore(DbContext context)
            : base(context) {
        }
    }
    private class GuidRoleStore : RoleStore<GuidRole, Guid, GuidUserRole> {
        public GuidRoleStore(DbContext context)
            : base(context) {
        }
    }

    [TestMethod]
    public async Task CustomUserGuidKeyTest() {
        var manager = new UserManager<GuidUser, Guid>(new GuidUserStore(new GuidUserContext()));
        GuidUser[] users = {
            new GuidUser() { UserName = "test" },
            new GuidUser() { UserName = "test1" }, 
            new GuidUser() { UserName = "test2" },
            new GuidUser() { UserName = "test3" }
            };
        foreach (var user in users) {
            UnitTestHelper.IsSuccess(await manager.CreateAsync(user));
        }
        foreach (var user in users) {
            var u = await manager.FindByIdAsync(user.Id);
            Assert.IsNotNull(u);
            Assert.AreEqual(u.UserName, user.UserName);
        }
    }
30
Hao Kung

Unter Verwendung der Antwort von Stefan Cebulak und eines großartigen Blog-Artikels von Ben Foster ASP.NET Identity Stripped Bare habe ich die folgende Lösung gefunden, die ich auf ASP.NET angewendet habe Identität 2.0 mit einem von Visual Studio 2013 generierten AccountController.

Die Lösung verwendet eine Ganzzahl als Primärschlüssel für Benutzer und ermöglicht es außerdem, eine ID des aktuell angemeldeten Benutzers abzurufen, ohne die Datenbank zu durchsuchen.

Hier sind die Schritte, die Sie befolgen müssen:

1. Erstellen Sie benutzerdefinierte benutzerbezogene Klassen

Standardmäßig verwendet AccountController Klassen, die string als Typ eines Primärschlüssels verwenden. Wir müssen die folgenden Klassen erstellen, die stattdessen ein int verwenden. Ich habe alle folgenden Klassen in einer Datei definiert: AppUser.cs

public class AppUser :
    IdentityUser<int, AppUserLogin, AppUserRole, AppUserClaim>,
    IUser<int>
{

}

public class AppUserLogin : IdentityUserLogin<int> { }

public class AppUserRole : IdentityUserRole<int> { }

public class AppUserClaim : IdentityUserClaim<int> { }

public class AppRole : IdentityRole<int, AppUserRole> { }

Es ist auch nützlich, einen benutzerdefinierten ClaimsPrincipal zu haben, der die Benutzer-ID auf einfache Weise offenlegt

public class AppClaimsPrincipal : ClaimsPrincipal
{
    public AppClaimsPrincipal( ClaimsPrincipal principal ) : base( principal )
    { }

    public int UserId
    {
        get { return int.Parse(this.FindFirst( ClaimTypes.Sid ).Value); }
    }
}

2. Erstellen Sie ein benutzerdefiniertes IdentityDbContext

Der Datenbankkontext unserer Anwendung wird IdentityDbContext erweitern, wodurch standardmäßig alle authentifizierungsbezogenen DbSets implementiert werden. Auch wenn DbContext.OnModelCreating eine leere Methode ist, bin ich mir über den IdentityDbContext.OnModelCreating nicht sicher. Denken Sie daher beim Überschreiben daran, base.OnModelCreating( modelBuilder )AppDbContext.cs aufzurufen.

public class AppDbContext :
    IdentityDbContext<AppUser, AppRole, int, AppUserLogin, AppUserRole, AppUserClaim>
{
    public AppDbContext() : base("DefaultConnection")
    {
        // Here use initializer of your choice
        Database.SetInitializer( new CreateDatabaseIfNotExists<AppDbContext>() );
    }


    // Here you define your own DbSet's



    protected override void OnModelCreating( DbModelBuilder modelBuilder )
    {
        base.OnModelCreating( modelBuilder );

        // Here you can put FluentAPI code or add configuration map's
    }
}

3. Erstellen Sie benutzerdefinierte UserStore und UserManager, die oben verwendet werden

AppUserStore.cs

public interface IAppUserStore : IUserStore<AppUser, int>
{

}

public class AppUserStore :
    UserStore<AppUser, AppRole, int, AppUserLogin, AppUserRole, AppUserClaim>,
    IAppUserStore
{
    public AppUserStore() : base( new AppDbContext() )
    {

    }

    public AppUserStore(AppDbContext context) : base(context)
    {

    }
}

AppUserManager.cs

public class AppUserManager : UserManager<AppUser, int>
{
    public AppUserManager( IAppUserStore store ) : base( store )
    {

    }
}

4. Ändern Sie AccountController, um Ihre benutzerdefinierten Klassen zu verwenden

Ändern Sie alle UserManager in AppUserManager, UserStore in AppUserStore usw. Nehmen Sie ein Beispiel für diese Konstruktoren:

public AccountController()
    : this( new AppUserManager( new AppUserStore( new AppDbContext() ) ) )
{
}

public AccountController(AppUserManager userManager)
{
    UserManager = userManager;
}

5. Fügen Sie die Benutzer-ID als Anspruch auf ClaimIdentity hinzu, die in einem Cookie gespeichert ist

In Schritt 1 haben wir AppClaimsPrincipal erstellt, wodurch die aus ClaimType.Sid entnommene Benutzer-ID verfügbar gemacht wird. Damit dieser Anspruch jedoch verfügbar ist, müssen wir ihn beim Anmelden des Benutzers hinzufügen. In AccountController ist eine SingInAsync -Methode für die Anmeldung verantwortlich. Wir müssen dieser Methode eine Zeile hinzufügen, um den Anspruch hinzuzufügen.

private async Task SignInAsync(AppUser user, bool isPersistent)
{
    AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
    ClaimsIdentity identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);

    // Extend identity claims
    identity.AddClaim( new Claim( ClaimTypes.Sid, user.Id.ToString() ) );

    AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
}

6. Erstellen Sie ein BaseController mit einer CurrentUser -Eigenschaft

Um einen einfachen Zugriff auf die ID eines aktuell angemeldeten Benutzers in Ihren Controllern zu erhalten, erstellen Sie ein abstraktes BaseController, von dem Ihre Controller abgeleitet werden. Erstellen Sie im BaseController ein CurrentUser wie folgt:

public abstract class BaseController : Controller
{
    public AppClaimsPrincipal CurrentUser
    {
        get { return new AppClaimsPrincipal( ( ClaimsPrincipal )this.User ); }
    }


    public BaseController()
    {

    }
}

7. Vererben Sie Ihre Controller von BaseController und genießen Sie

Von nun an können Sie CurrentUser.UserId in Ihren Controllern verwenden, um auf die ID eines aktuell angemeldeten Benutzers zuzugreifen, ohne die Datenbank abzurufen. Sie können es verwenden, um nur Objekte abzufragen, die dem Benutzer gehören.

Sie müssen sich nicht um die automatische Generierung von Benutzerprimärschlüsseln kümmern - keine Überraschung, Entity Framework verwendet beim Erstellen von Tabellen standardmäßig Identity für ganzzahlige Primärschlüssel.

Achtung! Beachten Sie, dass bei der Implementierung in bereits freigegebene Projekte für bereits angemeldete Benutzer ClaimsType.Sid nicht existiert und FindFirst gibt in AppClaimsPrincipal null zurück. Sie müssen entweder die Abmeldung aller Benutzer erzwingen oder dieses Szenario in AppClaimsPrincipal behandeln.

52
krzychu

@ Haokung

Ich habe es geschafft, mit deinen nächtlichen Builds int-IDs zu erstellen. User.Identity.GetUserId () Problem ist immer noch da, aber ich habe gerade int.parse () für jetzt.

Die größte Überraschung war, dass ich keine eigene ID erstellen musste, die Datenbank mit der Identitäts-ID erstellt wurde und sie irgendwie automatisch für neue Benutzer festgelegt wurde.

Modell:

    public class ApplicationUser : IdentityUser<int, IntUserLogin, IntUserRole, IntUserClaim>
{
    public ApplicationUser()
    {
    }
    public ApplicationUser(string name) : this() { UserName = name; }
}

public class ApplicationDbContext : IntUserContext
{
    public ApplicationDbContext()
    {

    }
}

private class IntRole : IdentityRole<int, IntUserRole>
{
    public IntRole()
    {
    }
    public IntRole(string name) : this() { Name = name; }
}
private class IntUserRole : IdentityUserRole<int> { }
private class IntUserClaim : IdentityUserClaim<int> { }
private class IntUserLogin : IdentityUserLogin<int> { }
private class IntUserContext : IdentityDbContext<ApplicationUser, IntRole, int, IntUserLogin, IntUserRole, IntUserClaim>
{
    public IntUserContext()
        : base("DefaultConnection")
    {

    }
}
private class IntUserStore : UserStore<ApplicationUser, IntRole, int, IntUserLogin, IntUserRole, IntUserClaim>
{
    public IntUserStore(DbContext context)
        : base(context)
    {

    }
}
private class IntRoleStore : RoleStore<IntRole, int, IntUserRole>
{
    public IntRoleStore(DbContext context)
        : base(context)
    {
    }
}

Regler:

        public AccountController()
        : this(new UserManager<ApplicationUser, int>(new IntUserStore(new ApplicationDbContext())))
    {
    }

    public AccountController(UserManager<ApplicationUser, int> userManager)
    {
        UserManager = userManager;
    }

    public UserManager<ApplicationUser, int> UserManager { get; private set; }

Hoffe, Release Build wird bald kommen: D ...

P.S. Ich kann keine Kommentare schreiben, also habe ich eine Antwort gegeben, sorry.

6
Stefan Cebulak

Wie gesagt hier :

In Visual Studio 2013 verwendet die Standardwebanwendung einen Zeichenfolgenwert für den Schlüssel für Benutzerkonten. Mit ASP.NET Identity können Sie den Schlüsseltyp an Ihre Datenanforderungen anpassen. Beispielsweise können Sie den Typ des Schlüssels von einer Zeichenfolge in eine Ganzzahl ändern.

In diesem Thema über den obigen Link wird gezeigt, wie Sie mit der Standardwebanwendung beginnen und den Benutzerkontoschlüssel in eine Ganzzahl ändern. Sie können dieselben Änderungen verwenden, um einen beliebigen Schlüsseltyp in Ihr Projekt zu implementieren. Es wird gezeigt, wie diese Änderungen in der Standardwebanwendung vorgenommen werden. Sie können jedoch ähnliche Änderungen an einer benutzerdefinierten Anwendung vornehmen. Es werden die Änderungen angezeigt, die beim Arbeiten mit MVC oder Web Forms erforderlich sind.

4
Manik Arora

Grundsätzlich muss man:

-Ändern Sie den Typ des Schlüssels in der Benutzerklasse Identity auf int
- Fügen Sie benutzerdefinierte Identitätsklassen hinzu, die int als Schlüssel verwenden
- Ändern Sie die Kontextklasse und den Benutzermanager, um int als Schlüssel zu verwenden
- Ändern Sie die Startkonfiguration, um int als Schlüssel zu verwenden
- Ändern Sie den AccountController, um int als Schlüssel zu übergeben

hier ist ein Link, in dem alle Schritte erklärt werden, um dies zu erreichen.

3
lyz