Entity Framework 数据并发访问错误原因分析与系统架构优化

本文详细记录了针对项目中数据访问问题的分析与解决过程,包括问题定位、架构优化和性能提升。通过分析系统三层架构及EntityFramework3.5的使用,作者发现了并发与共享Entity实例的问题,并通过修改代码实现手动Dispose逻辑。最终,识别并修正了业务层和数据层类的单例模式导致的问题,确保了Entity实例的正确管理和性能优化。

本文主要记录近两天针对项目发生的数据访问问题的分析研究过程与系统架构优化,我喜欢说通俗的白话,高手轻拍

1. 发现问题

系统新模块上线后,使用频率较高,故在实际使用和后期的问题重现测试中,产生了一下系列的数据访问错误

错误是比较常见的错误

2. 分析问题

系统的架构为前端、业务层与数据层三层架构,采用Entity Framework 3.5作为数据处理技术,采用shared context per request模式,参照的是codeplex上的一个示例。示例地址(此文通俗易懂,代码结构也很清晰,个人很喜欢)

Entity模式的代码如下:

[csharp]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
 
  1. public partial class SPMIPEntities  
  2.     {  
  3.         public static SPMIPEntities Context  
  4.         {  
  5.             get  
  6.             {  
  7.                 string objectContextKey = "MIP_" + HttpContext.Current.GetHashCode().ToString("x");  
  8.                 if (!HttpContext.Current.Items.Contains(objectContextKey))  
  9.                 {  
  10.                     HttpContext.Current.Items.Add(objectContextKey, new SPMIPEntities());  
  11.                 }  
  12.                 return HttpContext.Current.Items[objectContextKey] as SPMIPEntities;  
  13.             }  
  14.         }  
  15.     }  

基于以上,根据系统的报错位置研究Entity实例并发与共享的问题,搜索了一些entity framework相关的资料。此问题主要从两个角度去着手解决:缩短Entity实例的存在时间和降低Entity实例的共享性,并考虑性能,因为Entity需要手动Dispose。

首先添加手动Dispose逻辑,我们的项目为SharePoint应用程序,所以和一般的.net略有不同。自行开发了一个BasePage类,所有的应用程序页面都继承自该类,BasePage类又继承自LayoutsPageBase类,要做到使用完Entity后及时Dispose,最好也写在页面的类似结束请求的事件里,于是在后台敲上protected空格override空格D->发现可重写Dispose方法,大喜,代码如下:

[csharp]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
 
  1. public override void Dispose()  
  2.         {  
  3.             string objectContextKey = "MIP_" + HttpContext.Current.GetHashCode().ToString("x");  
  4.             if (HttpContext.Current.Items.Contains(objectContextKey))  
  5.             {  
  6.                 SPMIPEntities ctx = HttpContext.Current.Items[objectContextKey] as SPMIPEntities;  
  7.                 if (ctx != null)  
  8.                 {  
  9.                     ctx.Dispose();  
  10.                     HttpContext.Current.Items.Remove(objectContextKey);  
  11.                 }  
  12.             }  
  13.             base.Dispose();  
  14.         }  

修改之后部署,测试,发现报错了:

The ObjectContext instance has been disposed and can no longer be used for operations that require a connection

没理由会这样,因为每个请求都会实例化新的Entity,不可能会被提前释放。

调试代码,断点卡到第一段代码get下面。重启服务,访问系统,附加进程开始调试,第一次get进来了,new了一个Entity实例出去,页面加载了出来;刷新页面,发现没有再次中断,到这里,就非常的不合理了,肯定有忽视掉的地方,于是回过头来一层一层查看代码,发现业务层和数据层的类都被写成了单例模式,举例如下

[csharp]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
 
  1. public class DAC  
  2.     {  
  3.  
  4.         #region 变量  
  5.         // 本类对象  
  6.         private static DAC _newNewInstance;  
  7.         private SPMIPEntities ctx = null;  
  8.         #endregion  
  9.  
  10.         #region 构造函数  
  11.   
  12.         /// <summary>  
  13.         /// 私有构造函数  
  14.         /// </summary>  
  15.         private DAC()  
  16.         {  
  17.             ctx = SPMIPEntities.Context;  
  18.         }  
  19.  
  20.         #endregion  
  21.  
  22.         #region 公有方法  
  23.   
  24.         /// <summary>  
  25.         /// 本类实例对象  
  26.         /// </summary>  
  27.         /// <returns>  
  28.         /// 返回DAC对象  
  29.         /// </returns>  
  30.         public static DAC GetNewInstance()  
  31.         {  
  32.             if (_newNewInstance == null)  
  33.             {  
  34.                 _newNewInstance = new DAC();  
  35.             }  
  36.             return _newNewInstance;  
  37.         }  
  38. }  

问题一定就在这里了,将单例模式改掉,修改为如下结构

[csharp]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
 
  1. public class DAC  
  2.     {  
  3.         private SPMIPEntities ctx = null;  
  4.   
  5.         private DAC()  
  6.         {  
  7.             ctx = SPMIPEntities.Context;  
  8.         }  
  9.   
  10.         public static DAC GetNewInstance()  
  11.         {  
  12.             return new DAC();  
  13.         }  
  14. }  

修改完后部署重复上述步骤调试,预期中的结果。

下周再和团队一起系统地测试一遍,看看是不是有效果。

本来预期中的架构是没什么问题的,谁知当时又将操作类做成了单例模式而没有引起重视,为当前错误的爆发埋下了伏笔。架构还是应该多推敲,多考虑的。

要下班了,所以写的有些仓促;写得看起来很简单,但是研究的过程并不是特别简单,都是耗费时间的。特此记录。

转载于:https://www.cnblogs.com/lenther2002/p/4814944.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值