【问题标题】:MVC Identity wants to insert Null into table when value is not Null当值不是 Null 时,MVC Identity 想要将 Null 插入表中
【发布时间】:2015-10-19 20:51:32
【问题描述】:

我最近刚刚发布了一个关于adding properties to ApplicationUsers 的问题,现在这个问题来自于:

所以现在ApplicationUserFirstNameLastName 属性是AspNetUsers 表中的字段。但是当我去注册为新用户时,我得到了这个错误:

System.Data.SqlClient.SqlException:无法将值 NULL 插入 列“姓氏”,表 'aspnet-CustomUserProps-20151019035309.dbo.AspNetUsers';列确实 不允许空值。插入失败。

这是我在AccountController的注册帖:

Public Async Function Register(model As RegisterViewModel) As Task(Of ActionResult)
    If ModelState.IsValid Then
        Dim user = New ApplicationUser() With {
            .UserName = model.Email,
            .Email = model.Email,
            .FirstName = model.FirstName,
            .LastName = model.LastName
        }
        Dim result = Await UserManager.CreateAsync(user, model.Password)

用户 DO 的 FirstNameLastName 属性包含预期值,但此错误发生在“Dim result =...”行。

我在注册视图中添加了名字和姓氏字段,如下所示:

<div class="form-group">
    @Html.LabelFor(Function(m) m.Email, New With {.class = "col-md-2 control-label"})
    <div class="col-md-10">
        @Html.TextBoxFor(Function(m) m.Email, New With {.class = "form-control"})
    </div>
</div>
<div class="form-group">
    @Html.LabelFor(Function(m) m.FirstName, New With {.class = "col-md-2 control-label"})
    <div class="col-md-10">
        @Html.TextBoxFor(Function(m) m.FirstName, New With {.class = "form-control"})
    </div>
</div>
<div class="form-group">
    @Html.LabelFor(Function(m) m.LastName, New With {.class = "col-md-2 control-label"})
    <div class="col-md-10">
        @Html.TextBoxFor(Function(m) m.LastName, New With {.class = "form-control"})
    </div>
</div>

然后像这样注册视图模型:

<Required>
<Display(Name:="First Name")>
Public Property FirstName As String

<Required>
<Display(Name:="Last Name")>
Public Property LastName As String

我还缺少什么?

这是堆栈跟踪的顶部(最后)几行,如果有帮助的话:

[SqlException (0x80131904): 无法将值 NULL 插入列 '姓氏',表 'aspnet-CustomUserProps-20151019035309.dbo.AspNetUsers';列确实 不允许空值。插入失败。声明已终止。]
System.Data.SqlClient.SqlConnection.OnError(SqlException 异常, 布尔型 breakConnection,Action'1 wrapCloseInAction) +1787814
System.Data.SqlClient.SqlInternalConnection.OnError(SqlException 异常,布尔型 breakConnection,Action'1 wrapCloseInAction) +5341674 System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose) +546
System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler、SqlDataReader 数据流、 BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean & dataReady) +1693
System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior,字符串 resetOptionsString) +275
System.Data.SqlClient.SqlCommand.CompleteAsyncExecuteReader() +220
System.Data.SqlClient.SqlCommand.EndExecuteNonQueryInternal(IAsyncResult 异步结果)+738
System.Data.SqlClient.SqlCommand.EndExecuteNonQueryAsync(IAsyncResult asyncResult) +147

【问题讨论】:

  • 很奇怪。调试时看到LastName 的值了吗?
  • @A.BurakErbora - 是的。如果我在此处设置一个停止点,调试窗口会显示我在该页面上输入的 FirstName 和 LastName 的值。
  • 我能想到的唯一可能与此有关的事情是,我必须手动将 FirstName 和 LastName 字段添加到迁移中 AspNetUsers 表的 Up 函数中。也许因为它没有自动将它们放入其他地方,所以缺少这些??

标签: asp.net-mvc vb.net entity-framework asp.net-identity


【解决方案1】:

这必须是您将额外列添加到身份数据库的方式。

您需要扩展 IdentityUser 并重新定义您的 DbContext,方法是使用您的新扩展 IdentityUser 类作为其通用参数从 IdentityDbContext 扩展。以下是你的做法:

Public Class YourApplicationUser
Inherits IdentityUser
Public Property LastName() As String
    Get
        Return m_LastName
    End Get
    Set
        m_LastName = Value
    End Set
End Property
Private m_LastName As String

Public Function GenerateUserIdentityAsync(manager As UserManager(Of YourApplicationUser)) As Task(Of ClaimsIdentity)
    ' Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
    Dim userIdentity = Await manager.CreateIdentityAsync(Me, DefaultAuthenticationTypes.ApplicationCookie)
    ' Add custom user claims here
    Return userIdentity
End Function
End Class

Public Class YourApplicationIdentityDbContext
Inherits IdentityDbContext(Of YourApplicationUser)
Public Sub New()

    MyBase.New("IdentityConnection", throwIfV1Schema := False)
End Sub

Public Shared Function Create() As YourApplicationIdentityDbContext
    Return New YourApplicationIdentityDbContext()
End Function

Protected Overrides Sub OnModelCreating(modelBuilder As System.Data.Entity.DbModelBuilder)
    MyBase.OnModelCreating(modelBuilder)

    modelBuilder.Entity(Of YourApplicationIdentityDbContext)().ToTable("IdentityUser").[Property](Function(p) p.Id).HasColumnName("UserId")

    modelBuilder.Entity(Of IdentityUserRole)().ToTable("IdentityUserRole").HasKey(Function(p) New From { _
        p.RoleId, _
        p.UserId _
    })

    modelBuilder.Entity(Of IdentityUserLogin)().ToTable("IdentityUserLogin").HasKey(Function(p) New From { _
        p.LoginProvider, _
        p.ProviderKey, _
        p.UserId _
    })

    modelBuilder.Entity(Of IdentityUserClaim)().ToTable("IdentityUserClaim").HasKey(Function(p) p.Id).[Property](Function(p) p.Id).HasColumnName("UserClaimId")

    modelBuilder.Entity(Of IdentityRole)().ToTable("IdentityRole").[Property](Function(p) p.Id).HasColumnName("RoleId")
End Sub
End Class

请注意,这还允许您将 db 表名称更改为您选择的任何名称 - 因为您使用自己的 DbContext 覆盖。

【讨论】:

  • 您指的是在自己的上下文中创建一个全新的配置文件表。我不应该做所有这些 - 如果需要,您可以简单地将属性添加到 ApplicationDbContext 中的标准配置文件表。所以我的问题是:如何正确地向 AspNetUsers 添加属性,即我做错了什么导致它们没有被添加到我创建的迁移中?谢谢!
  • 不,这不是指创建一个全新的配置文件;如果不实施自己的 OWIN 身份验证,您实际上无法做到这一点。如果您使用的是 Identity(MS 的实现),它不是开源的(还)所以您不能编写自己的配置文件表 - 您需要从 IdentityUser 扩展。 ApplicationUserApplicationDbContext 已经是 IdentityUserIdentityDbContext 的扩展类。我上面写的是如何你实现它们。将YourApplicationUser 与上面的ApplicationUser 切换,您将看到如何将属性添加到IdentityUser
【解决方案2】:

感谢 A. Burak Erbora - 您的建议确实让我走上了正确的道路。但我做的不完全一样……有点不一样:

从头开始一个新项目,首先我创建了一个新的用户类和上下文,但没有 OnModelCreating 函数:

Imports Microsoft.AspNet.Identity.EntityFramework
Imports System.Threading.Tasks
Imports System.Security.Claims
Imports Microsoft.AspNet.Identity

Public Class AppUser
    Inherits IdentityUser

    Private m_FirstName As String
    Public Property FirstName() As String
        Get
            Return m_FirstName
        End Get
        Set(value As String)
            m_FirstName = value
        End Set
    End Property

    Private m_LastName As String
    Public Property LastName() As String
        Get
            Return m_LastName
        End Get
        Set(value As String)
            m_LastName = value
        End Set
    End Property

    Public Async Function GenerateUserIdentityAsync(manager As UserManager(Of AppUser)) As Task(Of ClaimsIdentity)
        ' Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
        Dim userIdentity = Await manager.CreateIdentityAsync(Me, DefaultAuthenticationTypes.ApplicationCookie)
        ' Add custom user claims here
        Return userIdentity
    End Function
End Class

Public Class MyAppIdentityDbContext
    Inherits IdentityDbContext(Of AppUser)

    Public Sub New()
        MyBase.New("IdentityConnection", throwIfV1Schema:=False)
    End Sub

    Public Shared Function Create() As MyAppIdentityDbContext
        Return New MyAppIdentityDbContext()
    End Function
End Class

然后我删除了 IdentityModels(AppUser 替换它)。

是否在项目范围内将所有“ApplicationUser”替换为“AppUser”。

然后在项目范围内将所有“ApplicationDbContext”替换为“MyAppIdentityDbContext”。

确保在 Web.config 中 connectionString 的名称与新上下文(“IdentityConnection”)的名称相同。

修改了 RegisterViewModel、Register View 和 Account Controller 以包含添加到 AppUser 的字段。

从那里所有用户的东西都是一样的。例如,我想在用户登录时在导航栏中显示名字而不是电子邮件地址,所以这是 _LoginPartial 视图:

@Imports Microsoft.AspNet.Identity
@Code
    Dim db = New MyAppIdentityDbContext
    Dim curUserID = User.Identity.GetUserId()
    Dim myFirstName As String = (From users In db.Users Where users.Id = curUserID Select users.FirstName).FirstOrDefault
End Code

@If Request.IsAuthenticated
    @Using Html.BeginForm("LogOff", "Account", FormMethod.Post, New With { .id = "logoutForm", .class = "navbar-right" })
        @Html.AntiForgeryToken()
        @<ul class="nav navbar-nav navbar-right">
            <li>
                @Html.ActionLink("Hello " + myFirstName + "!", "Index", "Manage", routeValues:=Nothing, htmlAttributes:=New With {.title = "Manage"})
            </li>
            <li><a href="javascript:document.getElementById('logoutForm').submit()">Log off</a></li>
        </ul>
    End Using
Else
    @<ul class="nav navbar-nav navbar-right">
        <li>@Html.ActionLink("Register", "Register", "Account", routeValues := Nothing, htmlAttributes := New With { .id = "registerLink" })</li>
        <li>@Html.ActionLink("Log in", "Login", "Account", routeValues := Nothing, htmlAttributes := New With { .id = "loginLink" })</li>
    </ul>
End If

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-04-25
    • 1970-01-01
    • 2016-06-26
    • 2014-09-10
    • 2019-12-02
    • 1970-01-01
    • 2022-07-28
    相关资源
    最近更新 更多