如何更新包含未更新的子实体的现有实体? [英] How to update existing entity containing not updated sub-entity?

查看:100
本文介绍了如何更新包含未更新的子实体的现有实体?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I want to modify entity Payment object, containing not-modified, existing Currency object and save it to EF database:

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

关于编辑方法的几句话:它将包含的子实体货币更改为不变 ,因为它不会被更新。但是当行

A few words about Edit method: It changes contained sub-entity Currency to Unchanged, because it;s not going to be updated. But when line

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

被调用,显示错误:


附加类型为 Payment的实体失败,因为相同类型的另一个实体已经具有相同的主键值。如果图形中的任何实体具有
冲突的键值,则使用附加方法或将
实体的状态设置为不变或已修改时,可能会发生
的情况。这可能是因为某些实体是新实体,并且
尚未收到数据库生成的键值。在这种情况下,请使用
的'Add'方法或'Added'实体状态来跟踪图形,然后
将非新实体的状态设置为$ b $来设置为'未更改'或'已修改' b合适。

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

I also tried to use DbSet.Attach(payment) method, but it gives error too.

ADDED1:
这是我调用Edit方法的外部方法。

ADDED1: This is my external method which calls Edit method. It calls Context.Save and reads Payment entity by Id.

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)时,会给出错误

ADDED2: I added AsNoTracking to both Find queries. I was resported as remedy. But now other entity which is marked as Detached, when method DbSet.Attach(payment) is called, it gives error

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

Attaching an entity of type 'MobileWallet.Common.Repository.MwbeAddress' failed because another entity of the same type already has the same primary key value. This can happen when using the 'Attach' method or setting the state of an entity to 'Unchanged' or 'Modified' if any entities in the graph have conflicting key values. This may be because some entities are new and have not yet received database-generated key values. In this case use the 'Add' method or the 'Added' entity state to track the graph and then set the state of non-new entities to 'Unchanged' or 'Modified' as appropriate.

添加3:
添加方法FindBy:

ADDED 3: Added method 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;
        }


推荐答案

EF6通常非常擅长猜测什么需要提交给数据库,什么不需要。
我通常使用这样的代码:

EF6 is usually quite good at guessing what needs to be commited to the DB and what not. I am typically using code like this:

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

payment.Value = 199;

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

更新
A更详细的示例:

UPDATE: A more elaborate example:

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:


在28.04.2015 08:49:40 +02:00

Started transaction at 28.04.2015 08:49:40 +02:00

INSERT [dbo]开始交易。[ChildEntities]([值1],[值2])值(@ 0,@ 1)
从[dbo]中选择[id]。[ChildEntities] @@ ROWCOUNT> 0 AND [Id] =
scope_identity()

INSERT [dbo].[ChildEntities]([Value1], [Value2]) VALUES (@0, @1) SELECT [Id] FROM [dbo].[ChildEntities] WHERE @@ROWCOUNT > 0 AND [Id] = scope_identity()

-@ 0:'V1'(类型=字符串,大小= -1)
-@ 1:'V2'(类型=字符串,大小= -1)
-执行于2015年4月28日08:49:41 +02:00
-在3毫秒内完成,结果为:SqlDataReader

-- @0: 'V1' (Type = String, Size = -1) -- @1: 'V2' (Type = String, Size = -1) -- Executing at 28.04.2015 08:49:41 +02:00 -- Completed in 3 ms with result: SqlDataReader

插入[dbo]。[ParentEntities]([Property1],[Property2],[child_Id])
值(@ 0,@ 1,@ 2)从[dbo]中选择[Id]。[ParentEntities ] WHERE
@@ ROWCOUNT> 0 AND [Id] = scope_identity()

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

-@ 0: P1的值(类型=字符串,大小= -1)
-@ 1:'P2的值'(类型=字符串,大小= -1)
-@ 2:'2'(类型= Int32)
- -在28.04.2015 08:49:41 +02:00
执行-在3毫秒内完成结果为:SqlDataReader

-- @0: 'Value of P1' (Type = String, Size = -1) -- @1: 'Value of P2' (Type = String, Size = -1) -- @2: '2' (Type = Int32) -- Executing at 28.04.2015 08:49:41 +02:00 -- Completed in 3 ms with result: 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

Committed transaction at 28.04.2015 08:49:41 +02:00 Closed connection at 28.04.2015 08:49:41 +02:00 Opened connection at 28.04.2015 08:49:41 +02:00

SELECT
[Limit1]。[Id] AS中打开连接[Id],
[Limit1]。[Property1] AS [Property1],
[Limit1]。[Property2] AS [Property2],
[Limit1]。[child_Id] AS [child_Id ]
FROM(选择顶部(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]

SELECT [Limit1].[Id] AS [Id], [Limit1].[Property1] AS [Property1], [Limit1].[Property2] AS [Property2], [Limit1].[child_Id] AS [child_Id] FROM ( SELECT 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)
-在28.04.2015 08:49:41 +02:00
执行-在0毫秒内完成,结果为:SqlDataReader

-- p__linq__0: '2' (Type = Int32, IsNullable = false) -- Executing at 28.04.2015 08:49:41 +02:00 -- Completed in 0 ms with result: 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开始交易

Closed connection at 28.04.2015 08:49:41 +02:00 Opened connection at 28.04.2015 08:49:41 +02:00 Started transaction at 28.04.2015 08:49:41 +02:00

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

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

-@ 0:'已更改'(类型=字符串,大小= -1)
-@ 1:'2'(类型= Int32)
-执行于2015年4月28日08: 49:41 +02:00
-在5毫秒内完成结果:1

-- @0: 'Changed' (Type = String, Size = -1) -- @1: '2' (Type = Int32) -- Executing at 28.04.2015 08:49:41 +02:00 -- Completed in 5 ms with result: 1

在28.04.2015 08:49:41 +02提交的交易: 00封闭连接
在28.04.2015 08:49:41 +02:00

Committed transaction at 28.04.2015 08:49:41 +02:00 Closed connection at 28.04.2015 08:49:41 +02:00

从SQL-Log仅更改的属性被更新。
注意:更改是指也分配了相同的值。因此,如果您从 TextBox 更新,请在分配值之前检查该值是否已更改。
更新

So as can be seen from the SQL-Log only the changed property is updated. Be careful: 'Changed' means also assigned to the same value. So if you update from a TextBox check whether the value has changed before assigning it. UPDATE

EF6似乎可以真正跟踪更改-具有相同值的赋值不会触发更新-查看更新后的代码

EF6 seems to really track the changes - an assignment with the same value does not trigger an update - see updated code

这篇关于如何更新包含未更新的子实体的现有实体?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆