EntityFramework 6 (EF6 DBcontext) 并发处理实战

学习:C#归结揭秘——Entity Framework
并发处理详解

帖子笔记 ,该帖子使用的凡objectContext ,

如出一辙、并作相关概念

出现的型:

首先栽模式称为悲观式并发,即当一个用户就于改动某修记下时,系统以不容其他用户同时修改者记录。
仲栽模式称为乐观式并发,即系统允许多个用户以修改和同长长的记下,系统会优先定义由数据出现所招的产出异常处理模式,去处理修改后或产生的撞。常用的乐观性并发处理方法来以下几种植:

    1、保留最后修改的值。
    2、保留最初修改的价值。
    3、合并往往改动的价。

仲、模型属性的面世处理选项

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

亚洲必赢bwin696.com 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 Entity Framework
提供了更加迅速的乐观并发处理方式。相对于LINT to SQL , ADO.NET Entity
Framework
简化了开阔并发的处理方式,它可以灵活利用合并数据、保留初次输入数据、保留最新输入数据(3栽方法)等方法处理并发冲突。

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

总结:当型属性之 ConcurencyMode 为默认值 None
,一旦跟一个靶属性同时深受涂改,系统将因统一数据的方处理并发冲突,这为是
Entity Framework 处理并发冲突之默认方式。

合并处理方式如下:

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

(2)当同一时间对相同对象的两样性质作出修改,系统将保留已让改动的属于性值。下面用半独例证作出说明:

亚洲必赢bwin696.com 2

运转结果:

亚洲必赢bwin696.com 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} \r\n ",
                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不可以:

亚洲必赢bwin696.com 4

使用Entry也报错

亚洲必赢bwin696.com 5

最终参考如下帖子

亚洲必赢bwin696.com 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的完整价格,这样以合并数据的主意处理并发,有或引起逻辑性的错误。此时,应该考虑因其它措施处理并发冲突。”。

外什么法也?【待上】

 

4.1 去与创新操作以运行(非框架自动处理能力,开发自动修改状态手动增加的)**

Entity Framework
能盖完美之编制灵活处理同时更新同一对象的操作,但若删除操作以及创新操作而运转时,就可能存在逻辑性的万分。

譬如说:两个客户端同时加载了和一个对象,第一单客户端更新了数后,把数据再次提交。但以付给前,第二个客户端就把数据库被的都起多少删除。

这时候,上下文中的目标处于不同之状态下,将见面吸引
OptimisticConcurrencyException 异常(ObjectContext
和DBContext两种植办法下,异常不一样,具体要基于测试结果自己看清)。
相见是颇时,可以就此 try(OptimisticConcurrencyException){…} catch
{…} 方式一网打尽异常,然后转对象的State 属性。把EntityState 更改为 Added
,被删去的数目就是会为再加载。若将 EntityState 更改为 Detached
时,数据就是会吃顺利删除。下面将目标的 EntityState 属性更改为 Added
作为例子。

亚洲必赢bwin696.com 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;
        }

连发时的要命类型:

亚洲必赢bwin696.com 8

ID亚洲必赢bwin696.com发生了变

亚洲必赢bwin696.com 9

 

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时,系统以见面保留上下文当中的目标属性,使用这办法好当发出起异常时保持最新输入的对象属性。

 

4.4 当发生数出现时,保留最早(最初:最早同坏)输入的数码

将目标属性的 ConcurencyMode 设置为 Fixed 后,同时创新该属性,将会晤刺激
OptimisticConcurrencyException 异常。此时应用 ObjectContext.Refresh
(RefreshMode,object) 刷新达成下文中该目标的状态,当 RefreshMode 为
StoreWins 时,系统就见面拿数据源中之数码代表上下文中的数额。
因为首调用 SaveChanges,数据可成功保存至数据库。但是在 ObjectContext
并未释放时,再次利用 SaveChanges
异步更新数据,就见面掀起OptimisticConcurrencyException 并发异常。当
RefreshMode 为 StoreWins 时,系统便会保留初次输入的数额性。
斯例子与方的事例十分相似,只是把 RefreshMode 改吗 StoreWins
而已。在作业逻辑较为复杂的底体系中,建议以此方法处理并发异常。在保留最初输入的数额修改属性后,把属性返还于客户,让客户进行对照后又决定下一样步之处理方式。

亚洲必赢bwin696.com 10

亚洲必赢bwin696.com 11

 

察测试结果,可见当 RefreshMode 状态为 StoreWins
时,系统以会晤以数据源中之数码代表上下文当中的目标属性。在工作逻辑较为复杂的底系中,建议采用此方法处理并发异常。


链接: https://pan.baidu.com/s/1gfu6fZl 密码: fyb3

练的源码,有正的不当的爱人记得分享

相关文章