当前位置: 棋牌电玩游戏平台 > 最新电玩 > 正文

C# Entity Framework并发处理

时间:2019-09-02 16:54来源:最新电玩
学习: C#综合揭秘——Entity Framework并发处理详解 帖子笔记 ,该帖子使用的是objectContext , 本文转自: 原网站:C# EntityFramework并发处理 一、并发相关概念 并发的类型: 第一种模式称为

学习:C#综合揭秘——Entity Framework 并发处理详解 帖子笔记 ,该帖子使用的是objectContext ,

本文转自:

原网站:C# Entity Framework并发处理

一、并发相关概念

并发的类型:

第一种模式称为悲观式并发,即当一个用户已经在修改某条记录时,系统将拒绝其他用户同时修改此记录。
第二种模式称为乐观式并发,即系统允许多个用户同时修改同一条记录,系统会预先定义由数据并发所引起的并发异常处理模式,去处理修改后可能发生的冲突。常用的乐观性并发处理方法有以下几种:

    1、保留最后修改的值。
    2、保留最初修改的值。
    3、合并多次修改的值。

引言

在软件开发过程中,并发控制是确保及时纠正由并发操作导致的错误的一种机制。从 ADO.NET 到 LINQ to SQL 再到如今的 ADO.NET Entity Framework,.NET 都为并发控制提供好良好的支持方案。
并发处理方式一般分为乐观必并发与悲观必并发两种,本文将为大家介绍 Entity Framework 、 LINQ to SQL 中的并发处理方式。在本文最后,将提供一个了可参考的方案,结合事务与并发控制确保安全的数据交换机制。

二、模型属性的并发处理选项

如下图模型设计器中TimeStamp字段为启用并发

图片 1

<EntityType Name="UserAccout">
          <Key>
            <PropertyRef Name="Id" />
          </Key>
          <Property Name="Id" Type="Int32" Nullable="false" annotation:StoreGeneratedPattern="Identity" />
          <Property Name="FirstName" Type="String" Nullable="false" />
          <Property Name="LastName" Type="String" Nullable="false" />
          <Property Name="AuditFileds" Type="OrderDB.AuditFields" Nullable="false" />
          <Property Name="Timestamp" Type="DateTime" Nullable="false" ConcurrencyMode="Fixed" annotation:StoreGeneratedPattern="Computed" />
        </EntityType>

并发模式:ConcurencyMode 有两个成员:

None : 在写入时从不验证此属性。 这是默认的并发模式。

Fixed: 在写入时始终验证此属性。

当模型属性为默认值 None 时,系统不会对此模型属性进行检测,当同一个时间对此属性进行修改时,系统会以数据合并方式处理输入的属性值。

当模型属性为Fixed 时,系统会对此模型属性进行检测,当同一个时间对属性进行修改时,系统就会激发OptimisticConcurrencyException 异常。

 

在软件开发过程中,并发控制是确保及时纠正由并发操作导致的错误的一种机制。从 ADO.NET 到 LINQ to SQL 再到如今的 ADO.NET Entity Framework,.NET 都为并发控制提供好良好的支持方案。
并发处理方式一般分为乐观必并发与悲观必并发两种,本文将为大家介绍 Entity Framework 、 LINQ to SQL 中的并发处理方式。在本文最后,将提供一个了可参考的方案,结合事务 Transaction 处理复杂性对象的并发。

目录

三、悲观并发

 

 

一、并发处理的定义

四、乐观并发

为了解决悲观并发所带来的问题,ADO.NET Entity Framework 提供了更为高效的乐观并发处理方式。相对于LINT to SQL , ADO.NET Entity Framework 简化了乐观并发的处理方式,它可以灵活使用合并数据、保留初次输入数据、保留最新输入数据(3种方式)等方式处理并发冲突。

目录

二、模型属性的并发处理选项

4.1 以合并方式处理并发数据

总结:当模型属性的 ConcurencyMode 为默认值 None ,一旦同一个对象属性同时被修改,系统将以合并数据的方式处理并发冲突,这也是 Entity Framework 处理并发冲突的默认方式。

合并处理方式如下:

(1)当同一时间针对同一个对象属性作出修改,系统将保存最新输入的属性值。

(2)当同一时间对同一对象的不同属性作出修改,系统将保存已被修改的属性值。下面用两个例子作出说明:

图片 2

运行结果:

图片 3

#region (4.1)测试不设置任何并发测试时,当产生并发EF的处理方法
        delegate void MyDelegate(Address addressValue);
        public  StringBuilder sb = new StringBuilder();
        public Address GetAddress(int id)
        {
            using (OrderDBContainer context = new OrderDBContainer())
            {
                IQueryable<Address> list = context.AddressSet.Where(x => x.Id == id);
                return list.First();
            }
        }
        /// <summary>
        /// 修改方法
        /// </summary>
        /// <param name="addressValue"></param>
        public void UpdateAddress(Address addressValue)
        {
            using (OrderDBContainer context = new OrderDBContainer())
            {
                //显示输入新数据的信息
                Display("Current", addressValue);
                var obj = context.AddressSet.Where(x => x.Id == addressValue.Id).First();
                if (obj != null)
                    context.Entry(obj).CurrentValues.SetValues(addressValue);
                //虚拟操作,保证数据能同时加入到上下文当中
                Thread.Sleep(100);
                context.SaveChanges();
            }
        }        
        /// <summary>
        /// 显示实体当前属性
        /// </summary>
        /// <param name="message"></param>
        /// <param name="addressValue"></param>
        public void Display(string message, Address addressValue)
        {
            String data = string.Format("{0}n  Address Message:n    Id:{1}  Address1:{2}  "  
                "address2:{3} rn ",
                message, addressValue.Id, addressValue.Address1, addressValue.Address2 );
            sb.AppendLine(data);
        }     

        /// <summary>
        /// (1)测试使用EF默认的机制,当配置并发控制时,系统是使用的合并的方式
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button3_Click(object sender, EventArgs e)
        {
            //在更新数据前显示对象信息
            var beforeObj = GetAddress(1);
            Display("Before", beforeObj);

            //更新Person的SecondName,Age两个属性
            Address _address1 = new Address();
            _address1.Id = 1;
            _address1.Address1 = "古溪";
            _address1.Address2 = beforeObj.Address2;
            _address1.AuditFields.InsertDate = beforeObj.AuditFields.InsertDate;
            _address1.AuditFields.UpdateDate = beforeObj.AuditFields.UpdateDate;
            _address1.City = beforeObj.City;
            _address1.Zip = beforeObj.Zip;
            _address1.State = beforeObj.State;

            //更新Person的FirstName属性
            Address _address2 = new Address();
            _address2.Id = 1;
            _address2.Address1 = beforeObj.Address1;
            _address2.Address2 = "江苏";
            _address2.AuditFields.InsertDate = beforeObj.AuditFields.InsertDate;
            _address2.AuditFields.UpdateDate = beforeObj.AuditFields.UpdateDate;
            _address2.City = beforeObj.City;
            _address2.Zip = beforeObj.Zip;
            _address2.State = beforeObj.State;

            //使用异步方式同时更新数据
            MyDelegate myDelegate = new MyDelegate(UpdateAddress);
            myDelegate.BeginInvoke(_address1, null, null);
            myDelegate.BeginInvoke(_address2, null, null);

            Thread.Sleep(1000);
            //在更新数据后显示对象信息
            var afterObj = GetAddress(1);
            Display("After", afterObj);
            this.textBox1.Text = sb.ToString();
        }

        /// <summary>
        /// 先插入几条数据等着测试
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BtnSaveAddress_Click(object sender, EventArgs e)
        {
            using (OrderDBContainer db = new OrderDBContainer())
            {
                Address address = new Address();
                address.Address1 = "古溪镇";
                address.Address2 = "安镇";
                address.State = "2";
                address.City = "无锡";
                address.AuditFields.InsertDate = DateTime.Now;
                address.AuditFields.UpdateDate = DateTime.Now;
                address.Zip = "21415";
                db.AddressSet.Add(address);
                db.SaveChanges();
            }
        }

        /// <summary>
        /// 还原成初始值,准备再次测试
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button5_Click(object sender, EventArgs e)
        {
            using (OrderDBContainer db = new OrderDBContainer())
            {
                Address _address = db.AddressSet.Where(x => x.Id == 1).First();
                _address.Address1 = "aaa";
                _address.Address2 = "bbb";
                db.SaveChanges();
            }
        }
        #endregion

备注:实践过程中遇到的问题

在多线程中EF修改事件的解决方案,使用attach不可以:

图片 4

使用Entry也报错

图片 5

最终参考如下帖子

图片 6

/// <summary>
        /// 修改方法
        /// </summary>
        /// <param name="addressValue"></param>
        public void UpdateAddress(Address addressValue)
        {
            using (OrderDBContainer context = new OrderDBContainer())
            {
                //显示输入新数据的信息
                Display("Current", addressValue);
                var obj = context.AddressSet.Where(x => x.Id == addressValue.Id).First();
                if (obj != null)
                    context.Entry(obj).CurrentValues.SetValues(addressValue);
                //虚拟操作,保证数据能同时加入到上下文当中
                Thread.Sleep(100);
                context.SaveChanges();
            }
        }

引用:“以合并数据的方式处理并发冲突固然方便快节,但在业务逻辑较为复杂的系统下并不适合使用此处理方式。比如在常见的Order、OrderItem的表格中,OrderItem 的单价,数量会直接影响Order的总体价格,这样使用合并数据的方式处理并发,有可能引起逻辑性的错误。此时,应该考虑以其他方式处理并发冲突。”。

其他什么方式呢?【待补充】

 

一、并发处理的定义

三、Entity Framewrok 悲观并发

4.1 删除与更新操作同时运行(非框架自动处理能力,开发自行修改状态手动增加的)**

Entity Framework 能以完善的机制灵活处理同时更新同一对象的操作,但一旦删除操作与更新操作同时运行时,就可能存在逻辑性的异常。

例如:两个客户端同时加载了同一个对象,第一个客户端更新了数据后,把数据再次提交。但在提交前,第二个客户端已经把数据库中的已有数据删除。

此时,上下文中的对象处于不同的状态下,将会引发 OptimisticConcurrencyException 异常(ObjectContext 与DBContext两种方式下,异常不一样,具体要根据测试结果自己判断)。
遇到此异常时,可以用 try(OptimisticConcurrencyException){...} catch {...} 方式捕获异常,然后更改对象的State 属性。把EntityState 更改为 Added ,被删除的数据便会被再次加载。若把 EntityState 更改为 Detached 时,数据便会被顺利删除。下面把对象的 EntityState 属性更改为 Added 作为例子。

图片 7

代码如下:处理结果前后ID变化了(或许这就是有些架构师使用手动创建的GUID的方式,而不使用自增的原因之一吧,因为数据删除后再创建就回不到之前的ID了,不是太灵活,使用GUID再结合数据版本(dataVison)字段,timeStamp基本上控制数据的并发已经足够啊。

//更新对象
        public int UpdateWithConcurrent(int num, Address addressValue)
        {
            int returnValue = -1;
            using (OrderDBContainer context = new OrderDBContainer())
            {
                var obj = context.AddressSet.Where(x => x.Id == addressValue.Id).First();
                //显示对象所处状态
                DisplayState("Before Update", obj);
                try
                {
                    if (obj != null)
                        context.Entry(obj).CurrentValues.SetValues(addressValue);
                    //虚拟操作,保证数据已经在数据库中被异步删除
                    Thread.Sleep(300);
                    context.SaveChanges();
                    returnValue = obj.Id;
                }
                catch (Exception)
                {
                    //针对异常要做相应的判断,因为我只测试了删除的情况,就写死直接修改成Added 了
                    //正确的是要区分到底是修改还是删除  OptimisticConcurrencyException ex
                    //把对象的状态更改为 Added
                    context.Entry(obj).State = System.Data.Entity.EntityState.Added;
                    context.SaveChanges();
                    returnValue = obj.Id;
                }
            }
            return returnValue;
        }

并发时的异常类型:

图片 8

ID发生了变化

图片 9

 

二、模型属性的并发处理选项

四、Entity Framework 乐观并发

4.3 当发生数据并发时,保留最终(最新:最后一次)输入的数据

要验证输入对象的属性,必须先把该属性的 ConcurencyMode 设置为 Fixed,这样系统就会实时检测对象属性的输入值 。
当该属性被同时更新,系统便会激发 OptimisticConcurrencyException 异常。捕获该异常后,可以使用 ObjectContext.Refresh (RefreshMode,object) 刷新上下文中该对象的状态,当 RefreshMode 为 ClientWins 时,系统将会保持上下文中的现在有数据,即保留最新输入的对象值。此时再使用ObjectContext.SaveChanges, 系统就会把最新输入的对象值加入数据库当中。

在下面的例子当,系统启动前先把 Person 的 FirstName、SecondName 两个属性的 ConcurencyMode 属性设置为Fixed,使系统能监视这两个属性的更改。所输入的数据只在FirstName、SecondName 两个值中作出修改。在数据提交前先以 DisplayProperty 方法显示数据库最初的数据属性,在数据初次更新后再次调用 DisplayProperty 显示更新后的数据属性。在第二次更新数据时,由调用ObjectContext.SaveChanges时,数据库中的数据已经被修改,与当前上下文ObjectContext 的数据存在冲突,系统将激发OptimisticConcurrencyException 异常,此时把引发异常的对象属性再次显示出来。对异常进行处理后,显示数据库中最终的对象值。

 

 

观察测试结果,可见当RefreshMode状态为ClientWins时,系统将会保存上下文当中的对象属性,使用此方法可以在发生并发异常时保持最新输入的对象属性。

 

三、Entity Framewrok 悲观并发

五、回顾 LINQ to SQL 并发处理的方式

4.4 当发生数据并发时,保留最早(最初:最早一次)输入的数据

把对象属性的 ConcurencyMode 设置为 Fixed 后,同时更新该属性,将会激发 OptimisticConcurrencyException 异常。此时使用 ObjectContext.Refresh (RefreshMode,object) 刷新上下文中该对象的状态,当 RefreshMode 为 StoreWins 时,系统就会把数据源中的数据代替上下文中的数据。
因为初次调用 SaveChanges,数据可以成功保存到数据库。但是在 ObjectContext 并未释放时,再次使用 SaveChanges 异步更新数据,就会引发OptimisticConcurrencyException 并发异常。当 RefreshMode 为 StoreWins 时,系统就会保留初次输入的数据属性。
此例子与上面的例子十分相似,只是把 RefreshMode 改为 StoreWins 而已。在业务逻辑较为复杂的的系统当中,建议使用此方式处理并发异常。在保留最初输入的数据修改属性后,把属性返还给客户,让客户进行对比后再决定下一步的处理方式。

图片 10

图片 11

 

观察测试结果,可见当 RefreshMode 状态为 StoreWins 时,系统将会以数据源中的数据代替上下文当中的对象属性。在业务逻辑较为复杂的的系统当中,建议使用此方式处理并发异常。


链接: 密码: fyb3

练习的源码,有纠正的错误的朋友记得分享

四、Entity Framework 乐观并发

六、结合事务处理并发冲突

五、回顾 LINQ to SQL 并发处理的方式

 

六、结合事务处理并发冲突

 

 

一、并发处理的定义

 

在软件开发过程中,当多个用户同时修改一条数据记录时,系统需要预先制定对并发的处理模式。并发处理模式主要分为两种:
第一种模式称为悲观式并发,即当一个用户已经在修改某条记录时,系统将拒绝其他用户同时修改此记录。
第二种模式称为乐观式并发,即系统允许多个用户同时修改同一条记录,系统会预先定义由数据并发所引起的并发异常处理模式,去处理修改后可能发生的冲突。常用的乐观性并发处理方法有以下几种:

 

  1. 保留最后修改的值。
  2. 保留最初修改的值。
  3. 合并多次修改的值。

一、并发处理的定义

相对于LINQ TO SQL 中的并发处理方式,Entity Framework 中的并发处理方式实现了不少的简化,下面为大家一一介绍。

在软件开发过程中,当多个用户同时修改一条数据记录时,系统需要预先制定对并发的处理模式。并发处理模式主要分为两种:
第一种模式称为悲观式并发,即当一个用户已经在修改某条记录时,系统将拒绝其他用户同时修改此记录。
第二种模式称为乐观式并发,即系统允许多个用户同时修改同一条记录,系统会预先定义由数据并发所引起的并发异常处理模式,去处理修改后可能发生的冲突。常用的乐观性并发处理方法有以下几种:

 

  1. 保留最后修改的值。
  2. 保留最初修改的值。
  3. 合并多次修改的值。

二、模型属性的并发处理选项

相对于LINQ TO SQL 中的并发处理方式,Entity Framework 中的并发处理方式实现了不少的简化,下面为大家一一介绍。

在System.Data.Metadata.Edm 命名空间中,存在ConcurencyMode 枚举,用于指定概念模型中的属性的并发选项。ConcurencyMode有两个成员

回到目录

成员名称  说明
        None   在写入时从不验证此属性。 这是默认的并发模式。
        Fixed 在写入时始终验证此属性。

二、模型属性的并发处理选项

当模型属性为默认值 None 时,系统不会对此模型属性进行检测,当同一个时间对此属性进行修改时,系统会以数据合并方式处理输入的属性值。
当模型属性为Fixed 时,系统会对此模型属性进行检测,当同一个时间对属性进行修改时,系统就会激发OptimisticConcurrencyException 异常。
开发人员可以为对象的每个属性定义不同的 ConcurencyMode 选项,选项可以在*.csdl 找看到:

在System.Data.Metadata.Edm 命名空间中,存在ConcurencyMode 枚举,用于指定概念模型中的属性的并发选项。
ConcurencyMode 有两个成员:

 

成员名称  说明
        None   在写入时从不验证此属性。 这是默认的并发模式。
        Fixed 在写入时始终验证此属性。
 1 <Schema>
 2    ......
 3    ......
 4   <EntityType Name="Person">
 5     <Key>
 6       <PropertyRef Name="Id" />
 7     </Key>
 8     <Property Type="Int32" Name="Id" Nullable="false" annotation:StoreGeneratedPattern="Identity" />
 9     <Property Type="String" Name="FirstName" MaxLength="50" FixedLength="false" Unicode="true" 
10           ConcurrencyMode="Fixed" />
11     <Property Type="String" Name="SecondName" MaxLength="50" FixedLength="false" Unicode="true" />
12     <Property Type="Int32" Name="Age" />
13     <Property Type="String" Name="Address" MaxLength="50" FixedLength="false" Unicode="true" />
14     <Property Type="String" Name="Telephone" MaxLength="50" FixedLength="false" Unicode="true" />
15     <Property Type="String" Name="EMail" MaxLength="50" FixedLength="false" Unicode="true" />
16   </EntityType>
17 </Schema>

当模型属性为默认值 None 时,系统不会对此模型属性进行检测,当同一个时间对此属性进行修改时,系统会以数据合并方式处理输入的属性值。
当模型属性为Fixed 时,系统会对此模型属性进行检测,当同一个时间对属性进行修改时,系统就会激发OptimisticConcurrencyException 异常。
开发人员可以为对象的每个属性定义不同的 ConcurencyMode 选项,选项可以在*.csdl 找看到:

 

图片 12

 

 1 <Schema>
 2    ......
 3    ......
 4   <EntityType Name="Person">
 5     <Key>
 6       <PropertyRef Name="Id" />
 7     </Key>
 8     <Property Type="Int32" Name="Id" Nullable="false" annotation:StoreGeneratedPattern="Identity" />
 9     <Property Type="String" Name="FirstName" MaxLength="50" FixedLength="false" Unicode="true" 
10           ConcurrencyMode="Fixed" />
11     <Property Type="String" Name="SecondName" MaxLength="50" FixedLength="false" Unicode="true" />
12     <Property Type="Int32" Name="Age" />
13     <Property Type="String" Name="Address" MaxLength="50" FixedLength="false" Unicode="true" />
14     <Property Type="String" Name="Telephone" MaxLength="50" FixedLength="false" Unicode="true" />
15     <Property Type="String" Name="EMail" MaxLength="50" FixedLength="false" Unicode="true" />
16   </EntityType>
17 </Schema>

图片 13

图片 14

 回到目录

 

 

图片 15

三、Entity Framework 悲观并发

 回到目录

在一般的开发过程中,最常用的是悲观并发处理。.NET 提供了Lock、Monitor、Interlocked 等多个锁定数据的方式,它可以保证同一个表里的对象不会同时被多个客户进行修改,避免了系统数据出现逻辑性的错误。
由于本篇文章主要介绍并发处理方式,关于锁的介绍,请参考

 

 

三、Entity Framework 悲观并发

 1          public int Update(Person person)
 2          {
 3              int n = -1;
 4              try
 5              {
 6                  using (BusinessEntities context = new BusinessEntities())
 7                  {
 8                      lock (this) 
 9                      {
10                         var obj = context.Person.Where(x => x.Id == person.Id).First();
11                          if (obj != null)
12                              context.ApplyCurrentValues("Person", person);
13                          n = context.SaveChanges();
14                      }
15                  }
16              }
17              catch (Exception ex)
18              { ...... }
19              return n;
20          }

在一般的开发过程中,最常用的是悲观并发处理。.NET 提供了Lock、Monitor、Interlocked 等多个锁定数据的方式,它可以保证同一个表里的对象不会同时被多个客户进行修改,避免了系统数据出现逻辑性的错误。
由于本篇文章主要介绍并发处理方式,关于锁的介绍,请参考

 

图片 16

使用悲观并发虽然能有效避免数据发生逻辑性的错误,但使用 lock 等方式锁定 Update 方法的操作,在用户同时更新同一数据表的数据,操作就会被延时或禁止。在千万级 PV 的大型网络系统当中使用悲观并发,有可能降低了系统的效率,此时可以考虑使用乐观并发处理。

 1           private static object o=new object();
 2 
 3           public int Update(Person person)
 4           {
 5               int n = -1;
 6               try
 7               {
 8                   lock (o) 
 9                   {
10                       using (BusinessEntities context = new BusinessEntities())
11                       {
12                           var obj = context.Person.Where(x => x.Id == person.Id).First();
13                           if (obj != null)
14                               context.ApplyCurrentValues("Person", person);
15                           n = context.SaveChanges();
16                       }
17                   }
18               }
19               catch (Exception ex)
20               { ...... }
21               return n;
22           }

 回到目录

图片 17

 

使用悲观并发虽然能有效避免数据发生逻辑性的错误,但使用 lock 等方式锁定 Update 方法的操作,在用户同时更新同一数据表的数据,操作就会被延时或禁止。在千万级 PV 的大型网络系统当中使用悲观并发,有可能降低了系统的效率,此时可以考虑使用乐观并发处理。

四、Entity Framework 乐观并发

 回到目录

为了解决悲观并发所带来的问题,ADO.NET Entity Framework 提供了更为高效的乐观并发处理方式。相对于LINT to SQL , ADO.NET Entity Framework 简化了乐观并发的处理方式,它可以灵活使用合并数据、保留初次输入数据、保留最新输入数据等方式处理并发冲突。

 

4.1 以合并方式处理并发数据

四、Entity Framework 乐观并发

当模型属性的 ConcurencyMode 为默认值 None ,一旦同一个对象属性同时被修改,系统将以合并数据的方式处理并发冲突,这也是 Entity Framework 处理并发冲突的默认方式。合并处理方式如下:当同一时间针对同一个对象属性作出修改,系统将保存最新输入的属性值。当同一时间对同一对象的不同属性作出修改,系统将保存已被修改的属性值。下面用两个例子作出说明:

编辑:最新电玩 本文来源:C# Entity Framework并发处理

关键词: .NET技术 C# 安全与 Entity Frame