并发管理实战

二、模型属性的面世管理选项

葡萄娱乐场,一般来讲图模型设计器中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
相当。

 

4.1 去除与更新操作同不常候运营(非框架自动管理本领,开垦电动修改处境手动增添的)**

Entity Framework
能以健全的编制灵活管理同期更新同一对象的操作,但假若删除操作与立异操作同期运营时,就也许存在逻辑性的可怜。

比如:八个顾客端同有时候加载了同三个目的,第八个顾客端更新了数码后,把数据重复提交。但在付给前,第叁个顾客端已经把数据库中的已有多少删除。

此时,上下文中的对象处于不一致的气象下,将会掀起
OptimisticConcurrencyException 相当(ObjectContext
与DBContext两种艺术下,十分不均等,具体要基于测验结果自个儿判定)。
遇上此十分时,可以用 try(OptimisticConcurrencyException){…} catch
{…} 格局杀鸡取卵非常,然后改成对象的State 属性。把EntityState 退换为 Added
,被删除的数目便会被再次加载。若把 EntityState 更动为 Detached
时,数据便会被顺遂删除。上边把对象的 EntityState 属性退换为 Added
作为例子。

葡萄娱乐场 2

代码如下:管理结果前后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;
        }

并发时的不得了类型:

葡萄娱乐场 3

ID发生了转换

葡萄娱乐场 4

 

三、悲观并发

 

一、并发相关概念

并发的档期的顺序:

第一种方式称为悲观式并发,即当二个客户已经在退换某条记下时,系统将拒绝别的顾客同期修改此记录。
其次种方式称为乐观式并发,即系统允许三个顾客同期修改同一条记下,系统会优先定义由数据现身所引起的出现非凡处理情势,去管理修改后或许发生的争辨。常用的乐观性并发管理措施有以下三种:

    1、保留最终修改的值。
    2、保留最早修改的值。
    3、合併往往修改的值。

4.4 当产生多少出现时,保留最先(最先:最先贰遍)输入的数据

把对象属性的 ConcurencyMode 设置为 Fixed 后,同期立异该属性,将会激发
OptimisticConcurrencyException 万分。此时采纳 ObjectContext.Refresh
(RefreshMode,object) 刷新上下文中该指标的图景,当 RefreshMode 为
StoreWins 时,系统就能够把数据源中的数据代表上下文中的数目。
因为初次调用 SaveChanges,数据足以成功保存到数据库。不过在 ObjectContext
并没有释放时,再度使用 SaveChanges
异步更新数据,就能够引发OptimisticConcurrencyException 并发分外。当
RefreshMode 为 StoreWins 时,系统就能保留初次输入的数额属性。
此例子与地方的例证十二分相似,只是把 RefreshMode 改为 StoreWins
而已。在事情逻辑较为复杂的的系统个中,提出使用此办法管理并发分外。在保留最先输入的数据修改属性后,把属性返还给顾客,让客商开展对照后再决定下一步的管理方式。

葡萄娱乐场 5

葡萄娱乐场 6

 

观测测量检验结果,可知当 RefreshMode 状态为 StoreWins
时,系统将会以数据源中的数据代表上下文个中的对象属性。在事情逻辑较为复杂的的系统当中,建议使用此方法管理并发至极。


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

勤学苦练的源码,有勘误的错误的相爱的人记得分享

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

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

4.1 以联合格局管理并发数据

小结:当模型属性的 ConcurencyMode 为默许值 None
,一旦同一个对象属性同不常间被涂改,系统将以联合数据的艺术管理并发争持,那也是
Entity Framework 管理并发争辩的暗中认可情势。

统一管理格局如下:

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

(2)当同一时间对一样对象的例外属性作出修改,系统将保存已被涂改的属性值。上面用四个例证作出表达:

葡萄娱乐场 7

运作结果:

葡萄娱乐场 8

#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不得以:

葡萄娱乐场 9

使用Entry也报错

葡萄娱乐场 10

说起底参谋如下帖子

葡萄娱乐场 11

/// <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.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时,系统将会保留上下文个中的靶子属性,使用此方法能够在发生并发分外时保持最新输入的目的属性。

 

四、乐观并发

为了消除悲观并发所推动的标题,ADO.NET Entity Framework
提供了一发便捷的开阔并发管理方式。相对于LINT to SQL , ADO.NET Entity
Framework
简化了有比较大概率并发的管理格局,它能够灵活运用合併数据、保留初次输入数据、保留最新输入数据(3种艺术)等办法管理并发争执。