This is a personal reminder that default IdentityUser and its friends (like IdentityRole) generate the ID value in its constructor. These classes extend the behavior of the generic classes (IdentityUser implements IdentityUser<TKey, …> with IdentityUser<string, …>).
If you happen to use your own class that inherits from them, you’ll be responsible for generating that ID. Otherwise, you’ll get an error like the following:
System.Data.SqlClient.SqlException: Cannot insert the value NULL into column ‘Id’ table ‘dbo.AspNetUsers’; column does not allow nulls. INSERT fails. The statement has been terminated.
IdentityUser implementation
- IdentityUser: Microsoft Docs | Code at Github (aspnet/Identity)
- IdentityUser<TKey, TLogin, TRole, TClaim> : Microsoft Docs | Code at Github (aspnet/Identity)
namespace Microsoft.AspNet.Identity.EntityFramework
{
/// <summary>
/// Default EntityFramework IUser implementation
/// </summary>
public class IdentityUser : IdentityUser<string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>, IUser
{
/// <summary>
/// Constructor which creates a new Guid for the Id
/// </summary>
public IdentityUser()
{
Id = Guid.NewGuid().ToString();
}
// Rest of implementation
}
/// <summary>
/// Default EntityFramework IUser implementation
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TLogin"></typeparam>
/// <typeparam name="TRole"></typeparam>
/// <typeparam name="TClaim"></typeparam>
public class IdentityUser<TKey, TLogin, TRole, TClaim> : IUser<TKey>
where TLogin : IdentityUserLogin<TKey>
where TRole : IdentityUserRole<TKey>
where TClaim : IdentityUserClaim<TKey>
{
/// <summary>
/// Constructor
/// </summary>
public IdentityUser()
{
Claims = new List<TClaim>();
Roles = new List<TRole>();
Logins = new List<TLogin>();
}
// Rest of implementation
}
}
IdentityRole implementation
- IdentityRole: Microsoft Docs | Code at Github (aspnet/Identity)
- IdentityRole<TKey, IUserRole>: Microsoft Docs | Code at Github (aspnet/Identity)
namespace Microsoft.AspNet.Identity.EntityFramework
{
/// <summary>
/// Represents a Role entity
/// </summary>
public class IdentityRole : IdentityRole<string, IdentityUserRole>
{
/// <summary>
/// Constructor
/// </summary>
public IdentityRole()
{
Id = Guid.NewGuid().ToString();
}
// Rest of implementation
}
/// <summary>
/// Represents a Role entity
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TUserRole"></typeparam>
public class IdentityRole<TKey, TUserRole> : IRole<TKey> where TUserRole : IdentityUserRole<TKey>
{
/// <summary>
/// Constructor
/// </summary>
public IdentityRole()
{
Users = new List<TUserRole>();
}
// Rest of implementation
}
}
There is a pretty good guide in Microsoft Docs about replacing IdentityUser’s implementation, but the thing you’ll probably miss is the one about generating the ID value if you don’t change the ID type that by default is a string (a stringified GUID, actually).
Entity Framework Core
For EF Core, it’s the same, IdentityUser implements IdentityUser<TKey> with IdentityUser<string>.
- IdentityUser:
- IdentityUser: Microsoft Docs | Code at Github (dotnet/aspnetcore)
- IdentityUser<TKey>: Microsoft Docs | Code at Github (dotnet/aspnetcore)
- IdentityRole:
- IdentityRole: Microsoft Docs | Code at Github (dotnet/aspnetcore)
- IdentityRole<TKey>: Microsoft Docs | Code at Github (dotnet/aspnetcore)
Answer in Stack Overflow
After a while, I’ve seen an old question in SO with the same exception and an answer pointing out the same.