0

I have two entities, a User and a UserProfile. The PK of User is UserId, the PK of UserProfile is UserProfileId. Every time a new user is created in my app, I create a new UserProfile whose PK is the same as the PK in User. When I then try to go update properties on the UserProfile I end up getting multiplicity errors or schema invalid errors. Here are my two entities:

public class User
{
    public Guid UserId { get; set; }
    public string UserName { get; set; }
    public string Email { get; set; }
    public string Password { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int? PhoneExtension { get; set; }
    public string Comment { get; set; }
    public Boolean IsApproved { get; set; }
    public int PasswordFailuresSinceLastSuccess { get; set; }
    public DateTime? LastPasswordFailureDate { get; set; }
    public DateTime? LastActivityDate { get; set; }
    public DateTime? LastLockoutDate { get; set; }
    public DateTime? LastLoginDate { get; set; }
    public string ConfirmationToken { get; set; }
    public DateTime? CreateDate { get; set; }
    public Boolean IsLockedOut { get; set; }
    public DateTime? LastPasswordChangedDate { get; set; }
    public string PasswordVerificationToken { get; set; }
    public DateTime? PasswordVerificationTokenExpirationDate { get; set; }

    public virtual ICollection<Role> Roles { get; set; }
    public virtual UserProfile UserProfile { get; set; }
}


public class UserProfile
{
    public Guid UserProfileId { get; set; } 
    public virtual User ProfileOwner { get; set; }
    public Int64? HomePhone { get; set; }
    public Int64? MobilePhone { get; set; }

    public virtual User Manager { get; set; }
}

..and here are my only defined relationships using Fluent API.

modelBuilder.Entity<UserProfile>()
        .HasKey(e => e.UserProfileId);
modelBuilder.Entity<UserProfile>()
        .Property(e => e.UserProfileId)
        .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
modelBuilder.Entity<UserProfile>()
        .HasRequired(e => e.ProfileOwner)
        .WithRequiredDependent(r => r.UserProfile);

Finally, my UserService creates a new user and at the same time creates a new UserProfile whose Guid UserProfileId is the same as the User's Guid UserId. Right after the user and profile are created, I try to update the manager in the UserProfile with my UserProfileService using this:

public void UpdateUserProfile(UserProfile updatedUserProfile)
{
    UserProfile oldUserProfile = GetUserProfileByID(updatedUserProfile.UserProfileId);

    oldUserProfile.Manager = updatedUserProfile.Manager;
    oldUserProfile.HomePhone = updatedUserProfile.HomePhone;
    oldUserProfile.MobilePhone = updatedUserProfile.MobilePhone;

    this.SetEntityState(oldUserProfile, EntityState.Modified);
    this.UnitOfWork.SaveChanges();
}

The this.SetEntityState line throws this error:

Multiplicity constraint violated. The role 'UserProfile_ProfileOwner_Source' of the relationship 'WhelenPortal.Data.Context.UserProfile_ProfileOwner' has multiplicity 1 or 0..1.

I've been trying to get this working for TWO DAYS now, PLEASE HELP!!! Thanks in advance.

As requested, here is some additional information. I'm using the repository pattern and unit of work here. My GetUserProfileById code is below. The service uses the repository so I show both.

public UserProfile GetUserProfileByID(Guid id)
{
    if (id == null)
        throw new BusinessServicesException(Resources.UnableToRetrieveUserProfileExceptionMessage, new ArgumentNullException("id"));

    try
    {
        Model.UserProfile userProfile = _userProfileRepository.GetUserProfileByID(id);

        if (userProfile != null)
            return ToServicesUserProfile(userProfile);

        return null;
    }
    catch (InvalidOperationException ex)
    {
        throw new BusinessServicesException(Resources.UnableToRetrieveUserProfileExceptionMessage, ex);
    }
}

..and the repository:

public UserProfile GetUserProfileByID(Guid id)
{
    return this.GetDbSet<UserProfile>().Find(id);
}
BBauer42
  • 3,549
  • 10
  • 44
  • 81
  • Can you show `GetUserProfileByID` and `SetEntityState`? Do all involved methods use the same context instance? If they do, why do you set the state to `Modified` at all? Change tracking would mark the changed properties as modified automatically. – Slauma Jul 11 '12 at 18:30
  • I've updated the question to include the info you requested. – BBauer42 Jul 11 '12 at 18:37
  • Huh? `ToServicesUserProfile(userProfile)`, what happens there? – Slauma Jul 11 '12 at 18:46
  • This is an n-tier project so I have a business layer, data tier, and web layer. The ToServicesUserProfile(userProfile) essentially maps the model object to the business layer's model object. The entire project follows Project Silk's structure very closely. You can see that on the MSDN here: http://msdn.microsoft.com/en-us/library/hh396380.aspx or on codeplex here: http://silk.codeplex.com/ – BBauer42 Jul 11 '12 at 19:07
  • At a first glance your mapping looks correct in my opinion. The devil might be in some detail. The problem is that the EF details (which are important to answer your question) are very hidden under your architecture and many method calls. Can you try to extract the relevant code out of all those methods so that it's clear to see when the context is used, which queries and other DbContext and DbSet methods? – Slauma Jul 11 '12 at 19:19
  • The context is ONLY used in the repositories. For example, in the UserProfileRepository.GetUserProfileByID(Guid id) method. GetDbSet just returns this.Context.Set(), which I call .Find(id) on. Not sure what else you would need, the user is already successfully created and the context saved, the empty profile successfully created and context saved, then I try to update it and the update fails with the multiplicity error. While debugging I can use SSMSE to see the records have been created and step over the context.SaveChanges() each time. Still stuck!! – BBauer42 Jul 12 '12 at 12:20

1 Answers1

0

So after much playing around this is what ended up working for me, hopefully it can help someone else in some fashion. My User class stayed exactly the same but my UserProfile class changed to this:

public class UserProfile
{
    public Guid UserProfileId { get; set; }
    public virtual User ProfileOwner { get; set; }

    public Guid? ManagerId { get; set; }
    public virtual User Manager { get; set; }

    public Int64? HomePhone { get; set; }
    public Int64? MobilePhone { get; set; }
}

And here is the fluent mapping:

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

    modelBuilder.Entity<User>()
        .HasOptional(u => u.UserProfile)
        .WithRequired(u => u.ProfileOwner);

    modelBuilder.Entity<UserProfile>()
        .HasOptional(u => u.Manager)
        .WithMany()
        .HasForeignKey(u => u.ManagerId);
}
BBauer42
  • 3,549
  • 10
  • 44
  • 81