0

For reference, I've done what this accepted answer recommends to fix the problem.

Specifically I'm having trouble with my aspnet_Roles table when I try to add new roles. See my model and my code below:

public partial class aspnet_Roles
{
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage",
        "CA2214:DoNotCallOverridableMethodsInConstructors")]
    public aspnet_Roles()
    {
        RoleId = Guid.NewGuid();
        aspnet_Users = new HashSet<aspnet_Users>();
    }

    public Guid ApplicationId { get; set; }

    [Key]
    public Guid RoleId { get; set; }

    [Required]
    [StringLength(256)]
    public string RoleName { get; set; }

    [Required]
    [StringLength(256)]
    public string LoweredRoleName { get; set; }

    [StringLength(256)]
    public string Description { get; set; }

    public virtual aspnet_Applications aspnet_Applications { get; set; }

    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", 
        "CA2227:CollectionPropertiesShouldBeReadOnly")]
    public virtual ICollection<aspnet_Users> aspnet_Users { get; set; }
}

The model is pretty standard... I added the NewGuid assignment to the constructor since I'm building this code-first. I'm not sure if ASP Membership works with int values so I'm (grudgingly) hanging onto the Guids.

I'm using the Web.Security.Roles wrapper to add the roles via CreateRole:

protected override void Seed(Ortund.Models.OrtundDbContext context)
{
    Roles.CreateRole("global"); // Supreme ruler of all that he surveys.
    Roles.CreateRole("admin"); // Has some privileges.
    Roles.CreateRole("user"); // Nothing special happening here.
}

To my understanding, given the addition to the model of the RoleId = Guid.NewGuid() assignment, I should now be able to add these roles into the database with no errors as this is apparently meant to simulate the assignment of newid() as a default value on the database.

This isn't the case, however.

Interesting to note is that if I do update the database outside of my data model to include the newid() or newsequentialid() default value, the roles can be added with the above code and without any difficulty.

If I don't add the default values on the database, the following error appears in the Visual Studio debugger:

Cannot insert the value NULL into column 'RoleId', table 'OrtundStuff.dbo.aspnet_Roles'; column does not allow nulls. INSERT fails.

What have I missed? Why doesn't this NewGuid() thing work?

Ortund
  • 8,095
  • 18
  • 71
  • 139
  • what type is the column RoleId in database? – Jacek Jun 20 '17 at 12:36
  • @Jacek Entity Framework resolves Guids to `uniqueidentifier` SQL data types so that's what its using there – Ortund Jun 20 '17 at 12:37
  • Just prior to getting that exception, what is the value of `aspnet_Roles.RoleId`? – mjwills Jun 20 '17 at 12:42
  • Model looks like it was auto-generated. :P It looks like your default role provider doesn't use your entity type. – Jacek Jun 20 '17 at 12:47
  • @mjwills the value is *null* as there are no roles that currently exist in the database – Ortund Jun 20 '17 at 12:47
  • What if you do `aspnet_Roles.RoleId = Guid.NewGuid();` on the line before the exception occurs? Does that fix it? (Basically, the error is telling you what you have done wrong - you are trying to save a null RoleId - you need to specify one). – mjwills Jun 20 '17 at 12:49
  • @mjwills mhmm I understand what the error means, but since the addition of a role goes through the roleprovider I expected it shouldn't be a problem. Anyway, I can't call `aspnet_Roles.RoleId = Guid.NewGuid();` beforehand since the `aspnet_Roles` class would need to be instanciated first and there's no guarantee that the roleprovider would use that instance of the class. I'm not even sure there's even a possibility that it would to be honest – Ortund Jun 20 '17 at 12:57
  • Put a breakpoint in model's constructor. I bet your `Roles.CreateRole(string n)` won't call your model's constructor. IMO you should fix binding role provider to your ASP application. – Jacek Jun 20 '17 at 12:57
  • @Jacek kindly submit an answer with more detail if you don't mind and I'll give it a go :) – Ortund Jun 20 '17 at 12:58
  • Is this Code First model or edmx? I'm asking because it looks like auto generated from edmx, but at the same time there are some CoreFirst data annotations and of course the modified constructor. – Ivan Stoev Jun 20 '17 at 13:06
  • @IvanStoev its a code first model I generated from a database that already had the ASP Membership stuff in it – Ortund Jun 20 '17 at 13:43
  • Any fluent configuration or data annotation not shown here? Something is telling EF to treat that `RoleId` property as *identity* (i.e. database generated on insert), so EF is ignoring your value and sending `null` to the database. – Ivan Stoev Jun 20 '17 at 13:45
  • only the modelbuilder stuff that maps a relationship to aspnet_Users but that doesn't specify anything else about the schema for aspnet_Roles – Ortund Jun 20 '17 at 13:57
  • Well, in order to be 100% sure, add `modelBuilder.Entity().Property(e => e.RoleId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);` – Ivan Stoev Jun 20 '17 at 14:22
  • [Key] properties are by convention DatabaseGeneratedOption.Identity, so what @IvanStoev suggests is definitely a step to a solution. – DevilSuichiro Jun 20 '17 at 15:55

1 Answers1

0

Your model looks fine, but you are using System.Web.Security.Roles for managing the roles. It's okay, but don't expect framework will use your model without telling it to do so.

I'm afraid you should write your own role provider based on abstract class System.Web.Security.RoleProvider. Use your EF's context and your model in its implementation.

Then set an instance of your provider to Security.RoleProvider so every method of object Roles will use your implementation.

Jacek
  • 829
  • 12
  • 31