Search code examples
c#sql-servernhibernatefluent-nhibernatemany-to-many

Fluent NHibernate Many to Many insert issue


I have UserModel and RoleModel. I thinked thay may be in many to many relationship 'cause Users can have more than one role and a role can has more than one User. I make it Many to many but when i try insert a user i'm getting error like:

INSERT statement FOREIGN KEY constraint "FK6209393F7EB4E792" coincided with. "projectcoderwho" database "dbo.roles" column 'Id' There was an overlap in the table. The statement has been terminated.

I search it some but nothing usefull for me or I can't see the somethings. Now has anyone an idea about my problem?

This codes worked only one time. But now I'm getting the error which above.

UserModel.cs:

public class UserModel
{
    public virtual int Id { get; set; }
    public virtual DateTime RegisterDate { get; set; }
    public virtual string RegisterIp { get; set; }
    public virtual string EMail { get; set; }
    public virtual string Password { get; set; }
    public virtual string UserName { get; set; }
    public virtual string FirstName { get; set; }
    public virtual string LastName { get; set; }
    public virtual string WebSiteLink { get; set; }
    public virtual string FaceBookLink { get; set; }
    public virtual string TwitterLink { get; set; }
    public virtual string GooglePlusLink { get; set; }
    public virtual string PinterestLink { get; set; }
    public virtual IList<RoleModel> UserRole { get; set; }
    public virtual DateTime LastLogIn { get; set; }
    public virtual string LastLoginIP { get; set; }
    public virtual string About { get; set; }
    public virtual bool IsActive { get; set; }
    public virtual bool IsFreelancer { get; set; }
    public virtual bool IsBanned { get; set; }

    public UserModel()
    {
        UserRole = new List<RoleModel>();
    }

}

RoleModel.cs

public class RoleModel
{
    //will contains as default: Admin,Moderator,Author,User
    public virtual int Id { get; set; }
    public virtual string RoleName { get; set; }
    public virtual IList<UserModel> UsersInRole { get; set; }

    public RoleModel()
    {
        UsersInRole = new List<UserModel>();
    }
}

UserModelMap.cs:

public class UserModelMap : ClassMap<UserModel>
{
    public UserModelMap()
    {
        Table("Users");

        Id(x => x.Id);

        Map(x => x.RegisterDate);
        Map(x => x.RegisterIp);
        Map(x => x.EMail);
        Map(x => x.Password);
        Map(x => x.UserName);
        Map(x => x.FirstName);
        Map(x => x.LastName);
        Map(x => x.WebSiteLink);
        Map(x => x.FaceBookLink);
        Map(x => x.TwitterLink);
        Map(x => x.GooglePlusLink);
        Map(x => x.PinterestLink);
        Map(x => x.LastLogIn);
        Map(x => x.LastLoginIP);
        Map(x => x.About);
        Map(x => x.IsActive);
        Map(x => x.IsFreelancer);
        Map(x => x.IsBanned);

        HasManyToMany(x => x.UserRole).Table("UsersRoles")
            .ParentKeyColumn("RoleId")
            .ChildKeyColumn("UserId")
            .LazyLoad()
            .Cascade.All();
    }
}

RoleModelMap.cs:

public class RoleModelMap : ClassMap<RoleModel>
{
    public RoleModelMap()
    {
        Table("Roles");

        Id(x => x.Id);

        Map(x => x.RoleName);

        HasManyToMany(x => x.UsersInRole).Table("UsersRoles")
            .ParentKeyColumn("RoleId")
            .ChildKeyColumn("UserId")
            .LazyLoad()
            .Inverse();
    }
}

In UserApiController.cs:

[Route("User/Register")]
[HttpPost]
public string RegisterUser(object regUser)
{
    RoleModel userRole = BlogModule.getUserRole();//Getting "User" role
    UserModel newUser = JsonConvert.DeserializeObject<UserModel>(regUser.ToString());
    string hashedPassword = BlogModule.PasswordToMD5(newUser.Password);
    newUser.Password = hashedPassword;
    newUser.RegisterDate = DateTime.Now;
    newUser.UserRole.Add(userRole);
    newUser.LastLogIn = DateTime.Now;
    newUser.LastLoginIP = newUser.RegisterIp;


    DbOperations.AddNewUserToDatabase(newUser);

    string sonuc = "true";
    return sonuc;
}

AddNewUserToDatabase(UserModel newUser):

public static void AddNewUserToDatabase(UserModel newUser)
{
    using (var session = NHibernateHelper.OpenSession())
    {
        using (var transaction = session.BeginTransaction())
        {

            UserModel currentUser = session.QueryOver<UserModel>()
                .Where(x => x.UserName == newUser.UserName)
                .SingleOrDefault();
            if (currentUser == null)
            {
                session.Save(newUser);
                transaction.Commit();
            }
        }
    }
}

Solution

  • The point here is, that the HasManyToMany mapping must be reversed/upside-down when observing from each side's perspective. Instead of this:

    public UserModelMap()
    {
        ...
        // here we go wrong - parent column cannot be same 
        HasManyToMany(x => x.UserRole).Table("UsersRoles")
            .ParentKeyColumn("RoleId")                     // parent column must target User
            .ChildKeyColumn("UserId")                      //   not Role
            ...
    
    public RoleModelMap()
    {
        ...
        HasManyToMany(x => x.UsersInRole).Table("UsersRoles")
            .ParentKeyColumn("RoleId")                     // parent column must target Role
            .ChildKeyColumn("UserId")                      //   here is correct
            ...
    

    So, we have to switch the UserModel mapping of parent key column:

    public UserModelMap()
    {
        ...
        // switch them
        HasManyToMany(x => x.UserRole).Table("UsersRoles")
            .ParentKeyColumn("UserId") // instead of("RoleId")
            .ChildKeyColumn("RoleId")  // instead of ("UserId")
            ...
    
    public RoleModelMap()
    {
        ...  // stays unchanged
        HasManyToMany(x => x.UsersInRole).Table("UsersRoles")
            .ParentKeyColumn("RoleId")
            .ChildKeyColumn("UserId")
            ...