如何更新包含未更新的子实体的现有实体?

时间:2015-04-27 14:25:28

标签: c# entity-framework

我想修改实体Payment对象,包含未修改的现有Currency对象并将其保存到EF数据库:

public Payment()
{
   int Id {get;set;}
   public int Value {get;set;}
   public Currency SelectedCurrency{get;set;}
}

public Currency()
{
    int Id {get;set;}
    string Name;
}

我的编辑方法如下所示:

    public override void Edit(MwbePaymentMethod payment)
    {

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

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

关于Edit方法的一些说法:它将包含的子实体Currency更改为Unchanged,因为它不会被更新。但是当行

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

,显示错误:

  

附加类型'付款'的实体失败,因为同一类型的另一个实体已具有相同的主键值。这个可以   使用'附加'方法或设置状态   实体到'未改变'或者'修改'如果图中有任何实体   冲突的关键值。这可能是因为一些实体是新的和   尚未收到数据库生成的键值。在这种情况下使用   '添加'方法或“添加”#39;实体状态跟踪图形和   然后将非新实体的状态设置为“未更改”#39;或者'修改'如   合适的。

我也尝试使用DbSet.Attach(payment)方法,但它也会出错。

ADDED1: 这是我调用Edit方法的外部方法。它调用Context.Save并按Id读取Payment实体。

public void UpdateMwbePaymentMethod(MwbePaymentFilter filter, MwbePaymentDtoInOut mwbepayment)
        {
            var currentPayment = paymentMethodRepository.FindBy(x => x.UserData.Id == filter.userId && x.Id == filter.id);
            if (currentPayment==null || currentPayment.Count() != 1)
            {
                throw new DBConcurrencyException();
            }
            var mwbePayment = Mapper.Map<MwbePayment>(mwbepayment);

            mwbePayment.UserData = userRepository.Get(filter.userId).Data;            

            paymentRepository.Edit(mwbePayment);
            paymentRepository.SaveChanges();            
        }

ADDED2: 我将AsNoTracking添加到两个查询查询中。我被称为补救措施。但是现在标记为Detached的其他实体,当调用方法DbSet.Attach(payment)时,它会给出错误

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

增加3: 添加方法FindBy:

public IEnumerable<TEntity> FindBy(Expression<Func<TEntity, bool>> predicate, params Expression<Func<TEntity, object>>[] includes)
        {
            IQueryable<TEntity> query =  DbSet.AsNoTracking().Where(predicate);

            if (null != includes)
            {
                foreach (var include in includes)
                {
                    query = query.AsNoTracking().Include(include);
                }
            }
            IEnumerable<TEntity> result = query.AsEnumerable();
            return result;
        }

1 个答案:

答案 0 :(得分:0)

EF6通常非常擅长猜测需要提交给DB的内容以及不需要的内容。 我通常使用这样的代码:

Payment payment = context.Payment.Find( 42 ); //get payment with id 42 from db

payment.Value = 199;

context.SaveChanges(); //only changed data is saved

<强>更新 一个更详细的例子:

<强>的DbContext:

class TestContext : DbContext
{
    public TestContext()
        : base()
    {}

    public DbSet<ParentEntity> Parent { get; set; }
    public DbSet<ChildEntity> Child { get; set; }    
}

家长实体:

class ParentEntity
{
    [Key]
    public int Id { get; protected set; }

    public string Property1 { get; set; }
    public string Property2 { get; set; }

    public virtual ChildEntity child { get; set; } // virtual to enable lazy loading
}

儿童实体:

   class ChildEntity
    {
        [Key]
        public int Id { get; set; }

        public string Value1 { get; set; }    
        public string Value2 { get; set; }
}

测试代码:

   using ( TestContext context = new TestContext( ) )
    {
        context.Database.Log = Console.WriteLine;

        ParentEntity p1 = new ParentEntity( );
        p1.Property1 = "Value of P1";
        p1.Property2 = "Value of P2";

        ChildEntity c1 = new ChildEntity( );
        c1.Value1 = "V1";
        c1.Value2 = "V2";

        p1.child = c1;

        context.Parent.Add( p1 );

        context.SaveChanges( );

        myEntity = p1.Id;
    }

    using( TestContext context = new TestContext() )
    {
        context.Database.Log = Console.WriteLine;

        ParentEntity p = context.Parent.Where( x => x.Id == myEntity ).FirstOrDefault( );

        p.Property1 = "Changed";
        p.Property2 = "Value of P2";

        context.SaveChanges( );
    }

执行的sql:

  

于2015年4月28日08:49:40 +02:00开始交易

     

INSERT [dbo]。[ChildEntities]([Value1],[Value2])VALUES(@ 0,@ 1)   SELECT [Id] FROM [dbo]。[ChildEntities] WHERE @@ ROWCOUNT&gt; 0 AND [Id] =   SCOPE_IDENTITY()

     

- @ 0:'V1'(Type = String,Size = -1)    - @ 1:'V2'(Type = String,Size = -1)    - 执行时间为2015年4月28日08:49:41 +02:00    - 在3 ms内完成,结果为:SqlDataReader

     

INSERT [dbo]。[ParentEntities]([Property1],[Property2],[child_Id])   VALUES(@ 0,@ 1,@ 2)SELECT [Id] FROM [dbo]。[ParentEntities] WHERE   @@ ROWCOUNT&gt; 0 AND [Id] = scope_identity()

     

- @ 0:'P1的值'(Type = String,Size = -1)    - @ 1:'P2的值'(Type = String,Size = -1)    - @ 2:'2'(Type = Int32)    - 执行时间为2015年4月28日08:49:41 +02:00    - 在3 ms内完成,结果为:SqlDataReader

     

承诺交易于28.04.2015 08:49:41 +02:00封闭连接   于28.04.2015 08:49:41 +02:00于28.04.2015 08:49:41开通   02:00

     

选择       [Limit1]。[Id] AS [Id],       [Limit1]。[Property1] AS [Property1],       [Limit1]。[Property2] AS [Property2],       [Limit1]。[child_Id] AS [child_Id]       FROM(选择TOP(1)           [Extent1]。[Id] AS [Id],           [Extent1]。[Property1] AS [Property1],           [Extent1]。[Property2] AS [Property2],           [Extent1]。[child_Id] AS [child_Id]           FROM [dbo]。[ParentEntities] AS [Extent1]           WHERE [Extent1]。[Id] = @ p__linq__0       )AS [Limit1]

     

- p__linq__0:'2'(Type = Int32,IsNullable = false)    - 执行时间为2015年4月28日08:49:41 +02:00    - 在0 ms内完成,结果为:SqlDataReader

     

2015年4月28日08:49:41 +02:00关闭连接打开连接   28.04.2015 08:49:41 +02:00于28.04.2015 08:49:41 +02:00开始交易

     

UPDATE [dbo]。[ParentEntities] SET [Property1] = @ 0 WHERE([Id] = @ 1)

     

- @ 0:'已更改'(Type = String,Size = -1)    - @ 1:'2'(Type = Int32)    - 执行时间为2015年4月28日08:49:41 +02:00    - 在5毫秒内完成,结果:1

     

承诺交易于28.04.2015 08:49:41 +02:00封闭连接   于28.04.2015 08:49:41 +02:00

从SQL-Log可以看出,只更新了更改的属性。 注意:'Changed'也意味着分配给相同的值。因此,如果您从TextBox更新,请在分配之前检查值是否已更改。 的更新

EF6似乎真正跟踪了这些变化 - 具有相同值的赋值不会触发更新 - 请参阅更新的代码