Identity UserManager.AddToRole throws exception

2019-04-15 18:09发布

问题:

As the title says I am using the new C# MVC 5 Identity doing a simple call:

UserManager.AddToRole(User.Id, User.RequestedRole);

I am doing this in a method of my ViewModel that is called from the Controller

The UserManager is created in the Base Class of my ViewModel like this:

UserManager = new UserManager<TMUser>(new UserStore<TMUser>(new TMContext()));

When I make the above call to AddToRole method, I get this Inner Exception (The outer one is generic/useless):

{"A relationship from the 'Ledger_User' AssociationSet is in the 'Deleted' state. Given multiplicity constraints, a corresponding 'Ledger_User_Source' must also in the 'Deleted' state."}

I'm obviously not deleting anything at all but only adding a role to my user. I've had this exception before when I am trying to mix objects from multiple contexts...but I'm not doing that here...please help.

EDIT: I've gotten rid of the model in case it was interfering and added the following code to my controller:

    public ActionResult UpdateRoles(string id)
    {
        if (ModelState.IsValid)
        {
            var userManager = new UserManager<TMUser>(new UserStore<TMUser>(new TMContext()));
            var userRequestingRole = userManager.FindById(id);

            if (!userManager.IsInRole(userRequestingRole.Id, userRequestingRole.RequestedRole))
                userManager.AddToRole(userRequestingRole.Id, userRequestingRole.RequestedRole);

           // It is still crashing with the same error in the above AddToRole
        }

For further information, here is the structure of my TMUser and Ledger objects:

public class TMUser : IdentityUser
{
    public TMUser()
    {
        Ledger = new Ledger();

        OrderHistory = new List<Order>();

        Clients = new List<Client>();

        IsActive = true;
    }

    [DisplayName("Full Name")]
    public string FullName { get; set; }

    [DisplayName("Notification Email")]
    public string NotificationEmail { get; set; }

    public virtual Ledger Ledger { get; set; }

    public virtual List<Order> OrderHistory { get; set; }

    public virtual List<Client> Clients { get; set; }

    public string RequestedRole { get; set; }

    public virtual TMUser RoleApprovedBy { get; set; }

    public bool IsActive { get; set; }
}

public class Ledger
{
    public Ledger() 
    {
        Transactions = new List<Transaction>();
    }

    public long Id { get; set; }

    [Required]
    public virtual TMUser User { get; set; }

    public virtual List<Transaction> Transactions { get; set; }

    public decimal GetBalance()
    {
        // ...
    }

    internal void AddTransaction(decimal amount, string description, Order order)
    {
        // ...
    }
}

Another Edit: Today was another frustrating day. After making some changes in my Context it initially seemed like I fixed the problem. Here is the change I made:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);

    modelBuilder.Entity<TMUser>().HasOptional(c => c.RoleApprovedBy);

    modelBuilder.Entity<TMUser>().HasOptional(c => c.Ledger);
}

I added the above to the DB Context class, mine is: public class TMContext : IdentityDbContext<TMUser>

This worked the first time, I must have broken some kind of an association? However, when I tried again with a different user, a similar, but slightly different Exception happened:

{"A relationship from the 'TMUser_Ledger' AssociationSet is in the 'Deleted' state. Given multiplicity constraints, a corresponding 'TMUser_Ledger_Target' must also in the 'Deleted' state."}

So it feels like I am back to square one...I can keep going by removing the Ledger from the User object, but that would be cheating...I really don't want to get hacky with it...please help...

回答1:

The problem is that you create a new Ledger in the constructor of the TMUser, when you do that you will remove the current ledger for the TMUser and replace it with a new empty one. And then, EF will handle the new Ledger as new object that needs to be inserted in the database. Thats why you are getting the validation error about an entity that is en deleted state.

Another thing with creating an new Ledger in the constructor of TMUser causes the effect that every TMUser has a ledger but in your database model you have set it to nullable (bacause of the HasOptional).