学习:C#综述揭秘——Entity Framework
并发管理详解
帖子笔记 ,该帖子使用的是objectContext ,
一、并发相关概念
出现的系列:
首先种格局称为悲观式并发,即当三个客户已经在修改某条记下时,系统将不容别的客商同期修改此记录。
其次种形式称为乐观式并发,即系统允繁多个顾客相同的时间修改同一条记下,系统会事先定义由数量出现所引起的产出万分管理情势,去管理修改后只怕爆发的争辨。常用的乐观性并发管理办法有以下二种:
1、保留最后修改的值。
2、保留最先修改的值。
3、合併往往修改的值。
二、模型属性的面世管理选项
一般来讲图模型设计器中TimeStamp字段为启用并发
<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)当同一时间对同样对象的例外性质作出修改,系统将保留已被涂改的属性值。下边用三个例子作出表明:
运作结果:
#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不得以:
使用Entry也报错
末了参照他事他说加以考察如下帖子
/// <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
作为例子。
代码如下:管理结果前后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;
}
并发时的不胜类型:
ID产生了变化
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
而已。在作业逻辑较为复杂的的系统个中,建议采纳此方式管理并发格外。在保存最早输入的数额修改属性后,把属性返还给顾客,让客商扩充比较后再决定下一步的管理格局。
考察测量试验结果,可知当 RefreshMode 状态为 StoreWins
时,系统将会以数据源中的数据代表上下文个中的指标属性。在作业逻辑较为复杂的的系统个中,提出选取此情势管理并发分外。
链接: https://pan.baidu.com/s/1gfu6fZl 密码: fyb3
练习的源码,有修正的谬误的对象记得分享