【问题标题】:Update of complex object fails in DB context数据库上下文中复杂对象的更新失败
【发布时间】:2015-04-29 22:12:56
【问题描述】:

我只想更新对象 MwbePaymentMethod 的简单属性,而不更新复杂属性(最后 4 个属性是复杂的),因此它将这 4 个复杂属性更改为未更改。但是方法 Edit 失败了:

  Context.Entry(payment.BillingAddress).State = EntityState.Unchanged;

有错误:

附加类型为“MobileWallet.Common.Repository.MwbeAddress”的实体失败,因为同一类型的另一个实体已经具有相同的主键值。如果图中的任何实体具有冲突的键值,则在使用“附加”方法或将实体的状态设置为“未更改”或“已修改”时,可能会发生这种情况。这可能是因为某些实体是新实体,尚未收到数据库生成的键值。在这种情况下,使用“添加”方法或“已添加”实体状态来跟踪图形,然后将非新实体的状态设置为“未更改”或“已修改”。

模型对象:

 public class MwbePaymentMethod : BaseEntity
    {
        public enum MethodTypeEnum
        {
            Creditcard,
            Virtualcard,
            Wallet
        };
        public MethodTypeEnum MethodType { get; set; }
        public string Number { get; set; }
        public DateTime? ExpirationDate { get; set; }
        public double Balance { get; set; }
        public bool IsPending { get; set; }
        public bool IsDefault { get; set; }
        public MwbeAddress BillingAddress { get; set; }
        public MwbeCurrency Currency { get; set; }
        public MwbeUserData UserData { get; set; }
        public DateTime? PaymentDate { get; set; }

        [JsonIgnore]
        public virtual ICollection<MwbePayment> Payments { get; set; }
    }

编辑方法:

public override void Edit(MwbePaymentMethod payment)
        {

            if (payment.Currency != null && payment.Currency.Id != 0)
            {
                Context.Entry(payment.Currency).State = EntityState.Unchanged;
            }
            if (payment.UserData != null && payment.UserData.Id != 0)
            {
                Context.Entry(payment.UserData).State = EntityState.Unchanged;
            }

            if (payment.BillingAddress != null && payment.BillingAddress.Id != 0)
            {
                Debugger.Break(); 
                Context.Entry(payment.BillingAddress).State = EntityState.Unchanged;
            }

            Context.Entry(payment).State = EntityState.Modified;
            if (Context.Entry(payment).State == EntityState.Detached)
            {
                DbSet.Attach(payment);
            }
        }

添加1:

我稍微更改了代码,我正在读取所有导航字段/对象,特别是对于未完全填充与数据库中相同数据的帐单地址。

 public void UpdateMwbePaymentMethod(MwbePaymentMethodFilter filter, MwbePaymentMethodDtoInOut mwbepaymentmethod)
        {
            var currentPaymentMethod = paymentMethodRepository.FindBy(x => x.UserData.Id == filter.userId && x.Id == filter.id);
            if (currentPaymentMethod == null || currentPaymentMethod.Count() != 1)
            {
                throw new DBConcurrencyException();
            }
            var mwbePaymentPethod = Mapper.Map<MwbePaymentMethod>(mwbepaymentmethod);

            //load existing user data
            mwbePaymentPethod.UserData = userRepository.Get(filter.userId).Data;

            //load existing address with subproperties
            mwbePaymentPethod.BillingAddress = addressRepository.FindBy(x => x.Id == mwbePaymentPethod.BillingAddress.Id, x=>x.Merchants, x=>x.PaymentMethods, x=>x.Deliveries, x=>x.UserDatas).SingleOrDefault();

            if (mwbePaymentPethod.BillingAddress == null)
            {
                throw new DBConcurrencyException();
            }

            paymentMethodRepository.Edit(mwbePaymentPethod);
            paymentMethodRepository.SaveChanges();
        }

和编辑方法:

public override void Edit(MwbePaymentMethod payment)
    {

        if (payment.Currency != null && payment.Currency.Id != 0)
        {
            Context.Entry(payment.Currency).State = EntityState.Unchanged;
        }
        if (payment.UserData != null && payment.UserData.Id != 0)
        {
            Context.Entry(payment.UserData).State = EntityState.Unchanged;
        }

        if (payment.BillingAddress != null && payment.BillingAddress.Id != 0)
        {
            Debugger.Break(); // tutaj byl ostatnio problem
            Context.Entry(payment.BillingAddress).State = EntityState.Unchanged;
        }
        Context.Entry(payment).State = EntityState.Modified;

    }

不管

1) Context.Entry(payment.BillingAddress).State = EntityState.Unchanged;

2)Context.Entry(payment.BillingAddress).State = EntityState.Modified;

仍然显示相同的错误。对我来说,如果我没有从 db 附加所有属性,第 2 点应该可以工作,但它不起作用。

【问题讨论】:

  • 我已经回答了为什么您在尝试更改复杂类型的状态时遇到问题。我刚刚注意到您的代码存在另一个问题。当您这样做Context.Entry(payment).State = EntityState.Modified; 时,您实际上使if (Context.Entry(payment).State == EntityState.Detached) 下面的行始终为假。一个实体不能同时处于两种状态(您正在测试相同的属性,即您刚刚更改的属性)。
  • 请参阅"Should questions include “tags” in their titles?",其中的共识是“不,他们不应该”!

标签: c# entity-framework


【解决方案1】:

复杂类型不是实体,因此它们不会被跟踪。他们所属的实体是。

要理解为什么这是有意义的,请以这种方式思考: 您的 MwbePaymentMethod 有一个复杂类型的地址。在数据库中,它们将存储在一个表中,(默认)列名将是: 号码,MwbeAddress_Line1,MwbeAddress_Line2,MwbeAddress_PostalCode,...

如果更改地址的 Line1,则需要更新数据库中整个 MwbePaymentMethod 表的行。事实上,如果您更改属于实体 MwbePaymentMethod 的任何复杂属性的任何值,都会导致整个实体的更新(因为最终它们都属于数据库中的同一张表)。

这就是为什么“只更新对象 MwbePaymentMethod 的简单属性,而不[更新]复杂属性”没有意义的原因。

您只能更改上下文中 DbSets 中实体的状态 (Context.Entry(Entity).State = ...)(即 Context 类中 DbSet 中的所有 X)

this。特别是名为:复杂类型:跨多种类型拆分表

的部分

【讨论】:

    【解决方案2】:

    在编辑代码的第一行将实体附加到上下文:

    DbSet.Attach(payment);
    

    注意: DbSet 没有静态 Attach 方法。它应该是在您的上下文中定义的 DbSet&lt;MwbePaymentMethod &gt; 属性。

    完成后,将付款状态设置为已修改:

    Context.Entry(payment).State = EntityState.Modified;
    

    然后设置所有其他实体的状态。考虑到,当您将树附加到上下文时,它会以未更改的状态附加。来自 MSDN 附加文档:

    附加用于使用已知已存在于数据库中的实体重新填充上下文。 SaveChanges 因此不会尝试将附加实体插入到数据库中,因为假定它已经存在。请注意,已经在上下文中处于其他状态的实体将其状态设置为未更改。如果实体已经处于未更改状态的上下文中,则附加是无操作的。

    因此,您必须附加到上下文,然后在必要时将状态更改为其他内容,例如添加。你的做法恰恰相反。

    最后验证payment 对象的ID 是否正确。如果不存在,则必须将其包含在隐藏字段或任何其他表单输入中,以便将其发布回服务器。

    【讨论】:

    • 感谢您的回答:):1)在实际中使用内部 DbSet DbSet,因此数据库集是强类型的。 2)我不明白,我附加了修改过的对象,但错误是子对象 MwbeAddress 已经在系统中。但是我没有更改它,只是想将其更改为 Unchanged.... 不更新 DB 中的这个对象....我不明白你的解释
    猜你喜欢
    • 2016-07-06
    • 2020-12-15
    • 2023-03-09
    • 1970-01-01
    • 1970-01-01
    • 2015-05-29
    • 1970-01-01
    • 2010-09-14
    • 1970-01-01
    相关资源
    最近更新 更多