5

I seem to have a hate/love relation with the Identity. I love it because it is a complete solution for most applications. But I hate it because extending is not an easy task. I feel it is more complected than it should be.

I am trying to add custom attributes and foreign keys to my user model but it seems to be a very difficult task.

I need to add a new Identity field called UserId since Id is a string which will be auto generated by the database. Then, I need to add a foreign key to Company model and another one to Location model.

I followed the instruction in the answer from this other question in an attempt to add two new foreign keys and be able to get their value from my controllers.

Here is what I have done so far. My ApplicationUser class after the modifications I made look like this

public class ApplicationUser : IdentityUser
{
    [Key]
    public int MyUserId { get; set; }

    [ForeignKey("Company")]
    public int CompanyId { get; set; }

    [ForeignKey("Location")]
    public int CurrentLocationId { get; set; }

    public virtual Company Company { get; set; }

    public virtual Location Location { get; set; }

    public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
    {
        // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
        var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
        // Add custom user claims here

        userIdentity.AddClaim(new Claim("MyUserId", this.MyUserId.ToString() ));

        userIdentity.AddClaim(new Claim("CompanyId", this.CompanyId.ToString() ));

        userIdentity.AddClaim(new Claim("CurrentLocationId", this.CurrentLocationId.ToString()));

        return userIdentity;
    }
}

I also created an extensions class that allows me to get the values from the controllers like so

public static class IdentityExtensions
{
    public static int GetComapnyId(this IIdentity identity)
    {
        var claim = ((ClaimsIdentity)identity).FindFirst("ComapnyId");
        // Test for null to avoid issues during local testing
        return (claim != null) ? Int32.Parse(claim.Value) : 0;
    }

    public static int GetCurrentLocationId(this IIdentity identity)
    {
        var claim = ((ClaimsIdentity)identity).FindFirst("CurrentLocationId");
        // Test for null to avoid issues during local testing
        return (claim != null) ? Int32.Parse(claim.Value) : 0;
    }


    public static int GetMyUserId(this IIdentity identity)
    {
        var claim = ((ClaimsIdentity)identity).FindFirst("MyUserId");
        // Test for null to avoid issues during local testing
        return (claim != null) ? Int32.Parse(claim.Value) : 0;
    }

}

But I am running into the error "listed below" when I try to add a new migration

Here is the error

One or more validation errors were detected during model generation:

ApplicationUser_Claims_Source_ApplicationUser_Claims_Target: : The types of all properties in the Dependent Role of a referential constraint must be the same as the corresponding property types in the Principal Role. The type of property 'MyUserId' on entity 'IdentityUserClaim' does not match the type of property 'MyUserId' on entity 'ApplicationUser' in the referential constraint 'ApplicationUser_Claims'. ApplicationUser_Logins_Source_ApplicationUser_Logins_Target: : The types of all properties in the Dependent Role of a referential constraint must be the same as the corresponding property types in the Principal Role. The type of property 'MyUserId' on entity 'IdentityUserLogin' does not match the type of property 'MyUserId' on entity 'ApplicationUser' in the referential constraint 'ApplicationUser_Logins'. ApplicationUser_Roles_Source_ApplicationUser_Roles_Target: : The types of all properties in the Dependent Role of a referential constraint must be the same as the corresponding property types in the Principal Role. The type of property 'MyUserId' on entity 'IdentityUserRole' does not match the type of property 'MyUserId' on entity 'ApplicationUser' in the referential constraint 'ApplicationUser_Roles'.

This is the command I used to created the InitialCreate migration

Add-Migration InitialCreate

How can I add my foreign key and be able to get them from the controllers correctly?

Community
  • 1
  • 1
Junior
  • 11,602
  • 27
  • 106
  • 212
  • How did you define `IdentityUserClaim`? – Gert Arnold Jun 12 '16 at 21:04
  • I really think better is to keep the Identity and the rest of your entities in separate contexts. The Identity has an `UserId` which you can use it later to load your User from your main context. – Adrian Iftode Jun 12 '16 at 21:05
  • @GertArnold I did not make any change to my `IdentityUserClaim` – Junior Jun 12 '16 at 21:06
  • @AdrianIftode I am not following you. All I am trying to do here is add foreign key to other models in my application – Junior Jun 12 '16 at 21:07
  • I understand and what I propose is to have a different approach. Use the Identity with its own context and have your models in your own context. You can always access the UserId using that extension method and load the User + Company and Location as you usual do. – Adrian Iftode Jun 12 '16 at 21:10
  • Sure but will that solve my problem? – Junior Jun 12 '16 at 21:11
  • It doesn't solve this problem and this is why I used the comments section. I've successfully used this approach in legacy applications where I already had `Users' or 'Customers' tables with lots a columns and foreign keys. To move them to the Identity class seemed a lot so I used the Identity to store only one foreign key to the 'Customers' or 'Users' table. – Adrian Iftode Jun 12 '16 at 21:16
  • I seperated my models from into their own context which allowed me to run the application. but I still get the error listed in my question when trying to create a user. – Junior Jun 12 '16 at 23:15
  • Why you decorate the `MyUserId` with the Key attribute? Your `ApplicationUser` is inherited from `IdentityUser` this mean that already has a Key of type Guid, with the name Id; – Lucian Bumb Jun 13 '16 at 07:02
  • Since the error message relates to IdentityUserClaim, can you please include the definition of that type in you question as Gert Arnold suggested? – Zack Jun 13 '16 at 13:38

1 Answers1

0

Identity has a way to change the primary key (the Id field) to int. It is a lot of boilerplate/empty classes but it works for us. Maybe this would help?

public class ApplicationUser : IdentityUser<int, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>
{
    [ForeignKey("Company")]
    public int CompanyId { get; set; }

    [ForeignKey("Location")]
    public int CurrentLocationId { get; set; }

    public virtual Company Company { get; set; }

    public virtual Location Location { get; set; }

    public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
    {
        // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
        var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
        // Add custom user claims here

        userIdentity.AddClaim(new Claim("CompanyId", this.CompanyId.ToString() ));

        userIdentity.AddClaim(new Claim("CurrentLocationId", this.CurrentLocationId.ToString()));

        return userIdentity;
    }
}

public class ApplicationUserLogin : IdentityUserLogin<int>
{
}

public class ApplicationUserRole : IdentityUserRole<int>
{
    [ForeignKey("RoleId")]
    public virtual ApplicationRole Role { get; set; }

    [ForeignKey("UserId")]
    public virtual ApplicationUser User { get; set; }
}

public class ApplicationRole : IdentityRole<int, UserRole>
{

}

public class ApplicationUserClaim : IdentityUserClaim<int>
{
}

then we also needed a custom ApplicationUserStore as well

public class ApplicationUserStore : UserStore<ApplicationUser, ApplicationRole, int, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>
{
    public ApplicationUserStore(DbContext context)
        : base(context)
    {
    }
}
Kevin
  • 749
  • 5
  • 10