首页 文章

MVC 3 EF 4.1 dbContext - 删除具有不可为空的外键关系的一对多数据对象

提问于
浏览
14

我正在使用MVC 3,EF 4.1和dbContext . 我需要知道如何使用不可为空的外键删除一对多关系中的实体 .

当我删除子实体并执行SaveChanges时,我收到错误:

操作失败:无法更改关系,因为一个或多个外键属性不可为空 . 当对关系进行更改时,相关的外键属性将设置为空值 . 如果外键不支持空值,则必须定义新关系,必须为外键属性分配另一个非空值,或者必须删除不相关的对象 .

从其他帖子中,我了解到使用Remove(entity)标记要删除的实体 . 在SaveChanges期间,EF将外键设置为Null,并发生上述错误 .

我发现一些帖子在子实体上使用DeleteObject而不是Remove;但是,由于添加了dbContext和DbSet,似乎删除了DeleteObject方法 .

我发现帖子建议将EDMX外键关系修改为Nullable . 修改EDMX很好,但每当完成数据库更新模型时,这些更改都会被修改,必须重新应用 . 不是最佳的 .

另一篇文章建议创建一个外键关系设置为Nullable的代理实体,但我不明白这种方法 . 它似乎与修改EDMX的问题相同,因为当保存对EDMX的更改时,上下文会自动更新 .

我的简化模型是:

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; }
}

userContactUserId和userContactTypeId是必需的外键 .

在dbContext容器中,Users和UserContact都是DbSet .

我有一个用户的ViewModel和一个UserContact的ViewModel,如下所示

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; }

}

保存对用户配置文件的更改时,我遍历UserContactViewModel实体列表以确定已添加,修改或删除的实体 .

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 });
                        }
                    }

我很感激任何帮助 .

4 回答

  • 1

    我设法解决了以下问题:

    首先,我能够通过将我的DbContext(例如“ctx”)转换为IObjectContextAdapter来获取ObjectContext,然后获取对ObjectContext的引用 .

    接下来,我简单地调用DeleteObject方法传递要删除的UserContact记录 .

    当SaveChanges获取数据库中的删除按预期发生时 .

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

    以下是相关代码的片段:

    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();
    

    希望这有助于将来的某些人 .

  • 0

    我在MSDN指南页面"Cascade Delete Rules on Relationships"后面解决了同样的问题http://msdn.microsoft.com/en-us/library/bb738695.aspx希望能有所帮助:D

  • 15

    好吧,实现自己的ICollection并将这些子对象标记为删除并删除它 . 然后,在您自己的SaveChanges方法覆盖中,删除这些对象 .

  • 3

    您可以将关系配置为级联...这会将删除传播到从属实体 .

    但它非常危险:)

    我更喜欢在行中设置一个标志,以防止数据层在将来的查询中包含它,大多数应用程序不需要物理删除(并且有机会撤消) .

相关问题