MVC 3 EF 4.1 db Deleting one-to-many data objects with non-nullable foreign-key relationships in context

asp.net-mvc-3 entity-framework

Question

I'm using EF 4.1, dbContext, and MVC 3. I need to know how to remove a foreign key from a one-to-many connection that has an entity in it that is not nullable.

I see the following issue after running SaveChanges and removing the child entity:

The relationship could not be modified because one or more of the foreign-key attributes cannot be nullified, hence the operation failed. The associated foreign-key attribute is assigned to a null value when a relationship is changed. A new relationship must be formed, the foreign-key property must be given another non-null value, or the unrelated object must be destroyed if the foreign-key does not accept null values.

I gather from other discussions that calling Remove(entity) marks the entity for deletion. The foreign-key is set to Null by EF during SaveChanges, which results in the aforementioned problem.

However, the DeleteObject technique appears to have been abandoned in favor of the inclusion of dbContext and DbSet. I have discovered several blogs that utilize DeleteObject on the child object rather than Remove.

In certain postings, it is suggested that the EDMX foreign-key relation be changed to be Nullable. The EDMX may be modified, but any modifications must be reapply after every Update Model for Database operation. Not the best.

I don't understand the strategy that was proposed in another article to create a proxy object with the foreign-key relations set to Nullable. It seems to have the same problem as changing the EDMX in that when changes to the EDMX are saved, the context is immediately updated.

My condensed model is:

public partial class User
{
    public User()
    {
        this.UserContacts = new HashSet<UserContact>();
    }

    public long userId { get; set; }
    public string userEmail { get; set; }
    public string userPassword { get; set; }
    public string userFirstName { get; set; }
    public string userLastName { get; set; }
     . . .
    public virtual Country Country { get; set; }
    public virtual State State { get; set; }
    public virtual ICollection<UserContact> UserContacts { get; set; }
}

}

public partial class UserContact
{
    public long userContactId { get; set; }
    public long userContactUserId { get; set; }
    public long userContactTypeId { get; set; }
    public string userContactData { get; set; }

    public virtual ContactType ContactType { get; set; }
    public virtual User User { get; set; }
}

Foreign-keys userContactUserId and userContactTypeId are necessary.

Both Users and UserContact are DbSet in the dbContext container.

I have the following ViewModels for the User and UserContact.

public class UserContactViewModel
{
    [HiddenInput]
    public long UserContactId { get; set; }

    [HiddenInput]
    public long UserContactUserId { get; set; }

    [Display(Name = "Contact")]
    [Required]
    public string ContactData { get; set; }

    [Required]
    public long ContactType { get; set; }

    [HiddenInput]
    public bool isDeleted { get; set; }

}

    public class MyProfileViewModel
    {

        [HiddenInput]
        public long UserId { get; set; }

        [Required]
        [Display(Name = "First Name")]
        [StringLength(100)]
        public string FirstName { get; set; }

        [Required]
        [StringLength(100)]
        [Display(Name = "Last Name")]
        public string LastName { get; set; }
        ....
        public IEnumerable<UserContactViewModel> Contacts { get; set; }

}

I cycle over the list of UserContactViewModel entities to see which have been added, changed, or removed before storing changes to the user profile.

                    foreach (var c in model.Contacts)
                    {
                        UserContact uc = usr.UserContacts.Single(con => con.userContactId == c.UserContactId);
                        if (uc != null)
                        {
                            if (c.isDeleted == true)  // Deleted UserContact
                            {
                                ctx.UserContacts.Remove(uc);  // Remove doesn't work
                            }
                            else  //  Modified UserContact
                            {
                                uc.userContactData = c.ContactData;
                                uc.userContactTypeId = c.ContactType;
                                ctx.Entry(uc).State = EntityState.Modified;
                            }
                        }
                        else  // New UserContact
                        {
                            usr.UserContacts.Add(new UserContact { userContactUserId = model.UserId, userContactData = c.ContactData, userContactTypeId = c.ContactType });
                        }
                    }

Please provide a hand if you can.

1
14
5/4/2011 9:22:25 PM

Accepted Answer

I was able to come up with the following solution:

By first casting my DbContext (for example, "ctx") to an IObjectContextAdapter and then acquiring reference to the ObjectContext, I was able to first retrieve the ObjectContext.

The UserContact record to be removed was sent to the DeleteObject function as I just called it again.

The database deletions take place as anticipated when SaveChanges is triggered.

if (c.isDeleted == true)  // Deleted UserContact
{
    ObjectContext oc = ((IObjectContextAdapter)ctx).ObjectContext;
    oc.DeleteObject(uc)
}

A portion of the relevant code is shown here:

foreach (var c in model.Contacts)
{
    UserContact uc = null;
    if (c.UserContactId != 0)
    {
        uc = ctx.UserContacts.Find(c.UserContactId);
    }
    if (uc != null)
    {
        if (c.isDeleted == true)  // Deleted UserContact
        {
            ObjectContext oc = ((IObjectContextAdapter)ctx).ObjectContext;
            oc.DeleteObject(uc);
        }
        else  //  Modified UserContact
        {
            uc.userContactData = c.ContactData;
            uc.userContactTypeId = c.ContactType;
            ctx.Entry(uc).State = EntityState.Modified;
        }
    }
    else  // New UserContact
    {
        usr.UserContacts.Add(new UserContact { userContactData = c.ContactData, userContactTypeId = c.ContactType });
    }
}

ctx.Entry(usr).State = EntityState.Modified;
ctx.SaveChanges();

I hope this is useful in the future.

15
7/18/2011 9:00:30 PM

Popular Answer

Following the section "Cascade Delete Rules on Relationships" at the MSDN reference page here http://msdn.microsoft.com/en-us/library/bb738695.aspx, I was able to fix the same issue. I hope this is useful.



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow