如何为Identity Framework设置实体框架模型以对抗现有数据库?

时间:2014-08-31 16:29:07

标签: c# entity-framework asp.net-identity

我正在将旧网站从PHP迁移到C#MVC。我想使用微软的Identity设置,因为它看起来很整洁。

我已经使用数据库优先实体框架设置了我的解决方案。我有所需的表(Users,UserRoles,UserLogins,UserClaims),并设置了所有外键。

我已经看了几种设置IdentityUser的方法,那些使用MySqlDatabase和代码优先的方法,但我不确定如何在我已经拥有的情况下实现我的IdentityUser已建立的数据库,包括现有的Users表。

我希望我的IdentityUser使用我已经创建的Entity Framework类来操纵我的Users。有没有办法让我的User模型在EF中派生自IdentityUser并匹配我现有的数据库?

我正在努力解决的一件事是我的数据库不使用string值作为主键,它使用自动递增int

目前我有以下课程:

// Copyright (c) KriaSoft, LLC.  All rights reserved.  See LICENSE.txt in the project root for license information.

namespace KriaSoft.AspNet.Identity.EntityFramework
{
    using Microsoft.AspNet.Identity;
    using System;

    public partial class IdentityUser : IUser<int>
    {
        /// <summary>
        /// Default constructor 
        /// </summary>
        public IdentityUser()
        {
            Id = Guid.NewGuid().ToString();
        }

        /// <summary>
        /// Constructor that takes user name as argument
        /// </summary>
        /// <param name="userName"></param>
        public IdentityUser(string userName)
            : this()
        {
            UserName = userName;
        }

        /// <summary>
        /// User ID
        /// </summary>
        public string Id { get; set; }

        /// <summary>
        /// User's name
        /// </summary>
        public string UserName { get; set; }

        /// <summary>
        ///     Email
        /// </summary>
        public virtual string Email { get; set; }

        /// <summary>
        ///     True if the email is confirmed, default is false
        /// </summary>
        public virtual bool EmailConfirmed { get; set; }

        /// <summary>
        ///     The salted/hashed form of the user password
        /// </summary>
        public virtual string PasswordHash { get; set; }

        /// <summary>
        ///     A random value that should change whenever a users credentials have changed (password changed, login removed)
        /// </summary>
        public virtual string SecurityStamp { get; set; }

        /// <summary>
        ///     PhoneNumber for the user
        /// </summary>
        public virtual string PhoneNumber { get; set; }

        /// <summary>
        ///     True if the phone number is confirmed, default is false
        /// </summary>
        public virtual bool PhoneNumberConfirmed { get; set; }

        /// <summary>
        ///     Is two factor enabled for the user
        /// </summary>
        public virtual bool TwoFactorEnabled { get; set; }

        /// <summary>
        ///     DateTime in UTC when lockout ends, any time in the past is considered not locked out.
        /// </summary>
        public virtual DateTime? LockoutEndDateUtc { get; set; }

        /// <summary>
        ///     Is lockout enabled for this user
        /// </summary>
        public virtual bool LockoutEnabled { get; set; }

        /// <summary>
        ///     Used to record failures for the purposes of lockout
        /// </summary>
        public virtual int AccessFailedCount { get; set; }
    }
}

当然,我的Entity Framework数据库上下文中的Users实体已经具有所有这些属性,因此在那里以及我的上下文中指定它们似乎有点无意义......我真的不是确定:(。

我想我的问题是:如何使用我现有的实体框架User模型(数据库优先)作为我的Asp.net Identity的IdentityUser?

2 个答案:

答案 0 :(得分:6)

我现在对此更熟悉了。

使代码优先或数据库优先的最简单方法是更改​​现有数据库,使其至少具有ASP使用的最小数据库模式(表,列和外键) .NET Identity Framework。

您可以在下面的图片中看到最小架构:

Minimum Identity Framework Schema

虽然它没有列类型,但仍然有用。您可以从SQL Database Project template listed on this page获得精确的架构。

我确信通过在代码中(代码优先)创建某种映射或使用EF工具(数据库优先)从映射中映射,可以避免必须使现有数据库遵循此模式。列名到你代码中的另一个名字......但我还没试过。

我从头开始创建大部分表格,而不是User表,我将原始Username列更改为名称UserName(案例更正)以匹配并添加其他列尚不存在。

Code First

一旦您拥有数据库并且您知道架构是正确的,那么您可能需要考虑使用Visual Studio的reverse-engineer code first功能来为您构建EF类。这样,您的新EF类将精确匹配您的表格布局。否则,您必须自己使用所有映射对所有模型进行编码。

准备好EF类之后,应该从Identity Framework继承不同的类。当你首先作为代码执行此操作时,可以将继承添加到EF类中,而不必担心它们被覆盖(与数据库优先不同)。

public class User : IdentityUser<int, UserLogin, UserRole, UserClaim>
{
    // Any additional columns from your existing users table will appear here.
}

public class Role : IdentityRole<int, UserRole>
{
}

public class UserClaim : IdentityUserClaim<int>
{
}

public class UserLogin : IdentityUserLogin<int>
{
}

public class UserRole : IdentityUserRole<int>
{
}

注意每个中指定的int,它指定User表的主键类型。默认情况下,这是一个字符串,但我现有数据库中的Id值是int,它会自动递增。

数据库优先

当您首先使用EF数据库时,您无法直接将Identity Framework类的继承添加到自动生成的类中。这是因为每次使用Entity Framework Visual Studio工具对模型进行更改时都会覆盖它们。

但是,创建的类是自动生成的所有partial类,因此可以通过创建一个新文件来实现,该文件包含不会被覆盖的部分类的定义。它们必须位于相同的名称空间中且名称完全相同。

例如,这可能是EF生成的类:

namespace Dal
{
    public partial class User
    {
        // This class is auto generated by using EF database-first
        // We can't edit this class to add the inheritance,
        // because it will be overwritten every time
        // we update our model using the database-first tools
    }
}

这是我们可以创建的模型,用于将我们的继承添加到:

// same namespace as existing class
namespace Dal
{
    // Same name as existing class
    public partial class User : IdentityUser<int, UserLogin, UserRole, UserClaim>
    {
        // This can probably be left blank
    }
}

因此,您将为ASP.NET Identity Framework所需的每个类执行此操作:

public partial class User : IdentityUser<int, UserLogin, UserRole, UserClaim>
{
    // Any additional columns from your existing users table will appear here.
}

public partial class Role : IdentityRole<int, UserRole>
{
}

public partial class UserClaim : IdentityUserClaim<int>
{
}

public partial class UserLogin : IdentityUserLogin<int>
{
}

public partial class UserRole : IdentityUserRole<int>
{
}

答案 1 :(得分:1)

如果有人需要的脚本:

CREATE TABLE [User](
    UserID               INT IDENTITY(1,1) NOT NULL,
    UserName             NVARCHAR (100)           NOT NULL,
    Email                NVARCHAR (100)          NULL,
    EmailConfirmed       bit           NOT NULL,
    PasswordHash         NVARCHAR (100)         NULL,
    SecurityStamp        NVARCHAR (100)         NULL,
    PhoneNumber          NVARCHAR (100)          NULL,
    PhoneNumberConfirmed bit           NOT NULL,
    TwoFactorEnabled     bit           NOT NULL,
    LockoutEndDateUtc    DATETIME               NULL,
    LockoutEnabled       bit           NOT NULL,
    AccessFailedCount    INT                    NOT NULL,

    CONSTRAINT PK_User_UserID PRIMARY KEY CLUSTERED (UserID ASC),
    CONSTRAINT UK_User_UserName UNIQUE NONCLUSTERED (UserName ASC)
);

---
CREATE TABLE UserClaim
(
    UserID     INT                   NOT NULL,
    ClaimID    INT IDENTITY (1,1) NOT NULL,
    ClaimType  NVARCHAR (MAX)        NULL,
    ClaimValue NVARCHAR (MAX)        NULL,

    CONSTRAINT PK_UserClaim_ClaimID PRIMARY KEY CLUSTERED (ClaimID ASC),
    CONSTRAINT FK_UserClaim_User FOREIGN KEY (UserID) REFERENCES [User] (UserID) ON DELETE CASCADE
);

GO
CREATE NONCLUSTERED INDEX IX_UserClaim_UserID
    ON UserClaim (UserID ASC);

---
CREATE TABLE UserLogin
(
    UserID        INT            NOT NULL,
    LoginProvider NVARCHAR (128) NOT NULL,
    ProviderKey   NVARCHAR (128) NOT NULL,

    CONSTRAINT PK_UserLogin_UserID_LoginProvider_ProviderKey PRIMARY KEY CLUSTERED (UserID ASC, LoginProvider ASC, ProviderKey ASC),
    CONSTRAINT FK_UserLogin_User FOREIGN KEY (UserID) REFERENCES [User] (UserID) ON DELETE CASCADE
);

GO
CREATE NONCLUSTERED INDEX IX_UserLogin_UserID
    ON UserLogin (UserID ASC);  

---
CREATE TABLE UserRole
(
    RoleID INT          NOT NULL,
    [Name]   NVARCHAR (100) NOT NULL,

    CONSTRAINT PK_UserRole_RoleID PRIMARY KEY CLUSTERED (RoleID ASC),
    CONSTRAINT UK_UserRole_Name UNIQUE NONCLUSTERED (Name ASC)
);  


---
CREATE TABLE UserUserRole
(
    UserID INT NOT NULL,
    RoleID INT NOT NULL,

    CONSTRAINT PK_UserUserRole_UserID_RoleID PRIMARY KEY CLUSTERED (UserID ASC, RoleID ASC),
    CONSTRAINT FK_UserUserRole_User FOREIGN KEY (UserID) REFERENCES [User] (UserID) ON DELETE CASCADE,
    CONSTRAINT FK_UserUserRole_UserRole FOREIGN KEY (RoleID) REFERENCES UserRole (RoleID) ON DELETE CASCADE
);

GO
CREATE NONCLUSTERED INDEX IX_UserUserRole_UserID
    ON UserUserRole (UserID ASC);

GO
CREATE NONCLUSTERED INDEX IX_UserUserRole_RoleID
    ON UserUserRole (RoleID ASC);