Reputation: 646
I just recently posted a question regarding adding properties to ApplicationUsers, and now this question follows from that:
So now the FirstName
and LastName
properties for ApplicationUser
are fields in the AspNetUsers table. But when I go to register as a new user, I get this error:
System.Data.SqlClient.SqlException: Cannot insert the value NULL into column 'LastName', table 'aspnet-CustomUserProps-20151019035309.dbo.AspNetUsers'; column does not allow nulls. INSERT fails.
This is my Register Post in 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)
The FirstName
and LastName
properties of user DO contain the expected values, but this error occurs on the "Dim result =
..." line.
I added fields for First Name and Last Name in the Register View like so:
<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>
And to the Register View Model like so:
<Required>
<Display(Name:="First Name")>
Public Property FirstName As String
<Required>
<Display(Name:="Last Name")>
Public Property LastName As String
What else am I missing?
Here's the top (last) several lines of the stack trace, if that helps:
[SqlException (0x80131904): Cannot insert the value NULL into column 'LastName', table 'aspnet-CustomUserProps-20151019035309.dbo.AspNetUsers'; column does not allow nulls. INSERT fails. The statement has been terminated.]
System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action'1 wrapCloseInAction) +1787814
System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean 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 dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady) +1693
System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString) +275
System.Data.SqlClient.SqlCommand.CompleteAsyncExecuteReader() +220
System.Data.SqlClient.SqlCommand.EndExecuteNonQueryInternal(IAsyncResult asyncResult) +738
System.Data.SqlClient.SqlCommand.EndExecuteNonQueryAsync(IAsyncResult asyncResult) +147
Upvotes: 0
Views: 2502
Reputation: 646
Thank you to A. Burak Erbora - your suggestion did set me on the right path. But I didn't do it exactly the same...it's a bit different:
Starting a new project from scratch, first I created a new user class and context, but without the OnModelCreating function:
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
Then I deleted IdentityModels (AppUser replaces it).
Did a Project-wide Replace All of "ApplicationUser" to "AppUser".
Then a Project-wide Replace all of "ApplicationDbContext" to "MyAppIdentityDbContext".
Made sure in Web.config the name of connectionString is same as for the new context ("IdentityConnection").
Modified RegisterViewModel, Register View and Account Controller to include the fields added to AppUser.
From there all the users stuff works the same. For example, I wanted to show the first name in the navigation bar instead of the email address when a user is logged in, so this is the _LoginPartial view:
@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
Upvotes: 0
Reputation: 1064
It's got to be the way in which you add your extra column to the Identity db.
you need to extend the IdentityUser
and redifine your DbContext
by extending from IdentityDbContext
with your new, extended IdentityUser
class as its generic parameter. Here's how you do it:
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
Note that this also allows you to change your db table names into whatever you choose - since you override with your own DbContext.
Upvotes: 1