带有EF数据库优先MVC5的ASP.NET身份


88

是否可以将新的Asp.net标识与Database First和EDMX一起使用?还是仅使用代码?

这是我所做的:

1)我创建了一个新的MVC5项目,并让新的Identity在我的数据库中创建了新的User和Roles表。

2)然后,我打开了Database First EDMX文件,并将其拖到新的Identity Users表中,因为我还有其他与此表相关的表。

3)保存EDMX后,数据库优先POCO生成器将自动创建一个User类。但是,UserManager和RoleManager希望从新的Identity命名空间(Microsoft.AspNet.Identity.IUser)继承一个User类,因此使用POCO User类将不起作用。

我想可能的解决方案是编辑POCO生成类以使我的User类继承自IUser?

还是ASP.NET Identity仅与Code First Design兼容?

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

更新:按照安德斯·阿贝尔(Anders Abel)的建议,这就是我所做的。它的工作,但我想知道是否有一个更优雅的解决方案。

1)通过在与我的自动生成的实体相同的名称空间中创建局部类,扩展了实体User类。

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

2)我将DataContext更改为从IdentityDBContext继承,而不是从DBContext继承。请注意,每次更新EDMX并重新生成DBContext和Entity类时,都必须将其设置为此。

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

3)在自动生成的User实体类中,您必须将override关键字添加到以下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使用MVC5Test_DBEntities(或任何您使用数据库上下文命名的)而不是ApplicationContext。
Patrick Tran

8
ASP.NET Identity是____蒸蒸日上的东西。对数据库优先的可怕支持,没有文档,参考约束差(在SQL Server上缺少CASCADE DELETE),并且使用字符串作为ID(性能问题和索引碎片)。这是他们在身份框架方面的第297次尝试...
DeepSpace101,2014年

1
@ DeepSpace101身份与代码优先相同,支持数据库优先。模板设置为执行代码优先,因此,如果您从模板开始,则必须更改一些内容。级联删除工作得很好,您可以轻松地将字符串更改为整数。请参阅下面的答案。
2014年

1
@鞋我不得不说我认为你可能是错的。我尚未找到有关如何在db-first中实现此功能的全面,有效的示例/教程(无文档)。API会尝试通过属性IdentityUser.Roles引用联结表“ IdentityUserRoles”,由于联结表未公开为实体(引用参照约束不佳),因此这打破了EF db-first上的关系。我不同意ID的字符串,因为可以通过在继承的类中指定类型参数来自定义ID。综上所述,在我看来,他们根本就没有DB。
2016年

Answers:


16

可以将身份系统与POCO和Database First一起使用,但是您必须进行一些调整:

  1. 更新.tt文件以生成POCO以创建实体类partial。这样您就可以在单独的文件中提供其他实现。
  2. User在另一个文件中对该类进行部分实现

 

partial User : IUser
{
}

这将使User该类实现正确的界面,而不用触碰实际生成的文件(编辑生成的文件始终是一个坏主意)。


谢谢。我必须尝试一下并报告是否可行。
Patrick Tran

我尝试了你提到的内容。请参阅我的帖子以获取更新的信息...但是解决方案不是很优雅:/
Patrick Tran

我一直遇到同样的问题。DB-first上的文档似乎很少。这是一个不错的建议,但我认为您是对的,它不太奏效
Phil

4
有没有什么解决办法不是hack?
user20358 2014年

我正在使用Onion Architecture,所有POCO都在Core中。不建议使用IUser。还有其他解决方案吗?
Usman Khalid 2015年

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身份。


我想使用现有的数据库并与ApplicationUser建立关系。这就是我使用SQL Server的方式,但是相同的想法可能适用于任何数据库。

  1. 创建一个MVC项目
  2. 打开Web.config中DefaultConnection下列出的数据库。它将被称为(aspnet- [timestamp]或类似名称。)
  3. 编写数据库表脚本。
  4. 将脚本化的表插入SQL Server Management Studio中的现有数据库。
  5. 自定义关系并将其添加到ApplicationUser(如有必要)。
  6. 创建新的Web项目> MVC>数据库第一个项目>使用EF导入数据库...不包括您插入的标识类。
  7. IdentityModels.cs中,将ApplicationDbContext更改:base("DefaltConnection")为使用项目的DbContext。

编辑:Asp.Net身份类图 在此处输入图片说明


6
问题不是DBContext,而是UserManager和RoleManager期望从Microsoft.AspNet.Identity.EntityFramework.IdentityUser继承的类
Patrick Tran

公共类IdentityDbContext <TUser>:DbContext,其中TUser:Microsoft.AspNet.Identity.EntityFramework.IdentityUser。首先使用数据库时,生成的实体类不会从任何基类继承。
Patrick Tran

然后,在使用实体框架生成类时,只需将它们从数据库中排除即可。
臭味

如果您从EDMX中排除身份表,那么您将丢失其他类中具有UserID外键的Navigation属性
Patrick Tran 2013年

34
我不愿意先编写代码……只是在某些场景和公司中,数据库管理员创建基本表,而不是编码器。
Patrick Tran

8

IdentityUser在这里一文不值,因为它是UserStore供身份验证使用的代码优先对象。在定义了自己的User对象之后,我实现了一个部分类,该类实现了该类IUser所使用的部分UserManager。我希望我的Ids int不是字符串,所以我只返回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来处理用户,声明,角色等的所有存储。实现代码优先执行的所有操作的接口,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
我最近有机会实现这一点,并且由于某种原因(我相信由于身份的3.0更新),我无法通过继承IdentityUser然后覆盖属性来实现登录。但是编写自定义UserStore并继承IUser可以正常工作;只是提供更新,也许有人会觉得有用。
纳兹·埃金

您可以为所有接口实现或整个项目提供链接吗?
DespeiL 2016年

您在这里使用局部类的具体原因是什么?
user8964654 '19

如果使用edmx生成模型,则必须使用局部类。如果您是先编写代码,则可以忽略这一点
Shoe


3

好问题。

我更像是数据库第一人。对我而言,代码优先范式似乎有些松懈,“迁移”似乎太容易出错。

我想自定义aspnet身份架构,而不必为迁移所困扰。我精通Visual Studio数据库项目(sqlpackage,data-dude)以及它如何在升级架构方面做得很好。

我的简单解决方案是:

1)创建一个镜像aspnet身份模式的数据库项目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回答中所说的所有事情,但还有另外一件事:确保Identity Framework在用于您的应用程序实体的Entity Framework连接字符串之上具有特定的SQL-Client连接字符串。

总之,您的应用程序将为Identity Framework使用一个连接字符串,为您的应用程序实体使用另一个。每个连接字符串具有不同的类型。阅读我的博客文章以获取完整的教程。


我两次尝试了您在博客中提到的所有内容,但对我来说却没有用。
Badhon Jain 2015年

@Badhon对不起,我的博客文章中的说明对您没有帮助。我的文章发表后,无数人表达了他们的谢意,因为他们获得了成功。永远记住,如果Microsoft更新了某些内容,则可能会影响结果。我为带有Identity Framework 2.0的ASP.NET MVC 5撰写了文章。除此之外,任何其他事情都可能会遇到问题,但是到目前为止,我已经收到了很多表明成功的评论。我想知道更多关于您的问题。
丹尼尔·伊格尔

我将尝试再次遵循,我面临的问题是我无法使用自定义数据库,而是使用了自动构建的数据库。无论如何,我并不是要说您的文章有问题。感谢您分享您的知识。
Badhon Jain 2015年

1
@Badhon不用担心我的朋友,我从不觉得你在说这篇文章有什么问题。我们每个人都有独特的处境,情况各不相同,因此有时对他人有用的东西不一定对我们有用。希望您能够解决您的问题。
丹尼尔·伊格尔 Daniel Eagle)

1

我们有一个实体模型DLL项目,用于保存模型类。我们还将保留一个包含所有数据库脚本的数据库项目。我的方法如下

1)首先使用数据库创建自己的具有EDMX的项目

2)脚本化数据库中的表,我使用VS2013连接到localDB(数据连接)并将脚本复制到数据库项目,添加任何自定义列,例如BirthDate [DATE]不为null

3)部署数据库

4)更新模型(EDMX)项目添加到模型项目

5)将任何自定义列添加到应用程序类

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

在MVC项目AccountController中添加了以下内容:

身份提供程序希望它可以使用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确实有最简单的答案。

经过一系列的试验和错误后,我按照乔希的建议做了,并替换了 connectionString生成的DB连接字符串。我最初的困惑是以下帖子:

如何将ASP.NET MVC5身份验证添加到现有数据库

@Win接受的答案指出要更改ApplicationDbContext()连接名称。如果您使用实体和数据库/模型优先方法来生成数据库连接字符串并将其添加到Web.config文件,则这有点模糊。

ApplicationDbContext()连接名称映射到在默认的连接Web.config文件。因此,Josh的方法效果最好,但是为了使ApplicationDbContext()可读性更好,我建议将名称更改为您最初发布的@Win的数据库名称,确保更改connectionString中的“ DefaultConnection” Web.config并注释掉和/或删除实体生成的数据库包括。

代码示例:

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.