ASP.NET Identity 2.1和EF 6 - ApplicationUser与其他实体的关系

时间:2015-04-13 20:28:20

标签: asp.net-mvc entity-framework ef-code-first asp.net-identity-2

似乎找不到这个答案,即使我正在做的事情似乎对大多数开发人员来说都是常见和重要的。在大多数具有用户帐户的系统中,用户表与数据库中的其他表绑定。这就是我想要的一切。我正在使用MSSQL Express 2012和VS 2013。

我有一个类库,我使用代码优先方法生成表。我将IdentityModel类从MVC项目移动到此类库。一切都是单独工作 - 我的表生成并且工作正常,并且在我注册新用户时生成身份表。

但是,现在我需要一个以1-1关系绑定到ApplicationUser的实体/表,但是添加这样的字段会阻止生成Identity表:

public class ApplicationUser : IdentityUser
{
    //***custom field
    public MyPortfolio Portfolio { get; set; }

    public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
    {
        var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
        return userIdentity;
    }
}

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext()
        : base("name=MyDataModel", throwIfV1Schema: false)
    {
    }

    public static ApplicationDbContext Create()
    {
        return new ApplicationDbContext();
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();

        base.OnModelCreating(modelBuilder);

        //sql output log
        Database.Log = s => Debug.Write(s);
    }
}

...&#34; MyPortfolio&#34;只是简单的实体:

    public class MyPortfolio
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }

        [StringLength(45, MinimumLength = 3)]
        public string Name { get; set; }

        public Boolean IsMaster { get; set; }

        //public ApplicationUser User { get; set; } //threw exception!
    }

我不太了解身份,但已经读过迁移可能就是答案。如果可能的话,我宁愿避免任何进一步的复杂性。这真的有必要吗?我处于早期开发阶段,将会多次删除/重新创建表格。

更新1:

好的,我添加了如下所述的adricadar。这里发生了什么......

添加迁移时,我必须从&#34;默认项目&#34;中选择我的类库。包管理器控制台中的下拉列表。在执行启用迁移时,我收到以下错误:

  

在程序集中找到了多个上下文类型&#39; MyProject.Data&#39;。   要为&#39; MyProject.Models.ApplicationDbContext&#39;启用迁移,请使用   启用 - 迁移--ContextTypeName   MyProject.Models.ApplicationDbContext。启用迁移   &#39; MyProject.Data.MyDataModel&#39;,使用Enable-Migrations -ContextTypeName   MyProject.Data.MyDataModel。

...所以我做了以下事情:

  

启用 - 迁移--ContextTypeName   MyProject.Models.ApplicationDbContext

...正如预期的那样,创建了Configuration类和&#34; InitialCreate&#34; AspNetUser *表的类。

然后我运行了#34; Add-Migration UserPortofolioRelation&#34;,它使用Up()和Down()生成DbMigration类。 Up和Down都定义了我在MyDataModel中定义的所有表。我现在在Up()中看到MyPortfolio和AspNetUsers之间的关系:

        CreateTable(
                "dbo.MyPortfolio",
                c => new
                        {
                            Id = c.Int(nullable: false, identity: true),
                            Name = c.String(maxLength: 45),
                            IsMaster = c.Boolean(nullable: false),
                            UserId = c.String(nullable: false, maxLength: 128),
                        })
                .PrimaryKey(t => t.Id)
                .ForeignKey("dbo.AspNetUsers", t => t.UserId)
                .Index(t => t.UserId);

当我运行Update-Database时,出现以下错误:

  

应用显式迁移:   [201504141316068_UserPortofolioRelation]。应用显式迁移:   201504141316068_UserPortofolioRelation。   System.Data.SqlClient.SqlException(0x80131904):已经有了   对象名为&#39; MyPortfolio&#39;在数据库中。

我对迁移的了解程度是这个基本教程:

https://msdn.microsoft.com/en-us/data/jj591621.aspx

这对我有用,并且只在生成的迁移代码中定义了新字段,而不是删除和创建所有表的命令。

更新2:

我遵循了本教程,在尝试使用多个数据上下文进行迁移时似乎更清楚地解释了这些内容:

http://www.dotnet-tricks.com/Tutorial/entityframework/2VOa140214-Entity-Framework-6-Code-First-Migrations-with-Multiple-Data-Contexts.html

我跑了这个命令:

  

启用 - 迁移--ContextTypeName   MyProject.Models.ApplicationDbContext

创建了以下配置:

internal sealed class Configuration : DbMigrationsConfiguration<MyProject.Models.ApplicationDbContext>
{
    public Configuration()
    {
        AutomaticMigrationsEnabled = false;
    }

    protected override void Seed(MyProject.Models.ApplicationDbContext context)
    {
    }
}

......看起来不错。然后我跑了这个:

  

添加 - 迁移 - 配置MyProject.Data.Migrations.Configuration   MigrationIdentity

...生成此文件:

namespace MyProject.Data.Migrations
{
    using System;
    using System.Data.Entity.Migrations;

    public partial class MigrationIdentity : DbMigration
    {
        public override void Up()
        {
            CreateTable(
                    "dbo.MyPortfolio",
                    c => new
                            {
                                Id = c.Int(nullable: false, identity: true),
                                Name = c.String(maxLength: 45),
                                IsMaster = c.Boolean(nullable: false),
                                UserId = c.String(nullable: false, maxLength: 128),
                            })
                    .PrimaryKey(t => t.Id)
                    .ForeignKey("dbo.AspNetUsers", t => t.UserId)
                    .Index(t => t.UserId);

            ...my other non-identity entities...

            CreateTable(
                    "dbo.AspNetUsers",
                    c => new
                            {
                                Id = c.String(nullable: false, maxLength: 128),
                                Email = c.String(maxLength: 256),
                                EmailConfirmed = c.Boolean(nullable: false),
                                PasswordHash = c.String(),
                                SecurityStamp = c.String(),
                                PhoneNumber = c.String(),
                                PhoneNumberConfirmed = c.Boolean(nullable: false),
                                TwoFactorEnabled = c.Boolean(nullable: false),
                                LockoutEndDateUtc = c.DateTime(),
                                LockoutEnabled = c.Boolean(nullable: false),
                                AccessFailedCount = c.Int(nullable: false),
                                UserName = c.String(nullable: false, maxLength: 256),
                            })
                    .PrimaryKey(t => t.Id)
                    .Index(t => t.UserName, unique: true, name: "UserNameIndex");

            ...other Identity entities/tables...

        }

        public override void Down()
        {
            ...everything you'd expect...
        }
    }
}

真棒!一个文件中的所有表/实体!所以我跑了它:

  

Update-Database -Configuration MyProject.Data.Migrations.Configuration   -verbose

...和bam!它使用MyPortfolio表上的UserId FK生成所有表。对世界来说,一切似乎都很棒。没有什么可以阻止我了!然后我跑了它并得到了这个例外:

  

在模型生成期间检测到一个或多个验证错误:

     

System.Data.Entity.ModelConfiguration.ModelValidationException

     

MyProject.Data.IdentityUserLogin :: EntityType&#39; IdentityUserLogin&#39;具有   没有键定义。定义此EntityType的键。   MyProject.Data.IdentityUserRole :: EntityType&#39; IdentityUserRole&#39;具有   没有键定义。定义此EntityType的键。   IdentityUserLogins:EntityType:EntitySet&#39; IdentityUserLogins&#39;是   基于类型&#39; IdentityUserLogin&#39;没有定义键。   IdentityUserRoles:EntityType:EntitySet&#39; IdentityUserRoles&#39;基于   在类型&#39; IdentityUserRole&#39;没有定义键。

快速谷歌让我回到Stack Exchange,自然而然地:EntityType 'IdentityUserLogin' has no key defined. Define the key for this EntityType

接受的答案概述了一大堆新的可能角度,试图让它正常工作。这让我回到原来的问题。我可以在没有迁移的情况下执行此操作吗?这有可能,无论如何?随着复杂程度的提高,我正在进行认真的辩论,并且我已经开始讨论自己的问题。验证,如果没有。我已经花了大量时间试图简单地将代码优先实体绑定到Identity用户。每个新门都要经过两次。它似乎并不值得......但也许他们会在下一个版本中清理一下。

1 个答案:

答案 0 :(得分:2)

您可以在OnModelCreating

中指定关系

尝试对每个数据库使用一个 DbContext。通常,您为不同的数据库设置不同的 DbContexts,而不是相同的数据库。

将所有实体移至ApplicationDbContext并按照以下说明操作。

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext()
        : base("name=MyDataModel", throwIfV1Schema: false)
    {
    }

    public DbSet<MyPortfolio> Portfolios { get; set; }
    // The rest of the entities
    // goes here

    public static ApplicationDbContext Create()
    {
        return new ApplicationDbContext();
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();

        modelBuilder.Entity<MyPortfolio>()
            .HasRequired(m => m.User  )
            .WithOptional(m => m.Portfolio )
            .Map(m => { m.MapKey("UserId"); });

        base.OnModelCreating(modelBuilder);

        //sql output log
        Database.Log = s => Debug.Write(s);
    }

}

通过迁移,您必须更新数据库,这非常简单。在Visual Studio中打开Package Manager Console并按顺序输入此命令。

Enable-Migration
Add-Migration UserPortofolioRelation`
Update-Database