EF6的多线程与分库架构设计实现

本文介绍了在项目背景中,由于业务复杂度提升,原有的基于EF++Repository+UnitOfWork的框架无法满足多线程高并发需求。作者探讨了DbContext的两种常见管理模式:单例模式和Using模式,并指出它们的局限性。接着提出了一种新的设计思路,通过维护UpdateList、InsertList和DeleteList集合,在多线程环境中处理数据库写操作。数据库上下文通过工厂模式管理,确保每个连接唯一,避免并发问题。最后,作者分享了部分实现代码,并提到会另写文章详细讨论Redis的使用。
摘要由CSDN通过智能技术生成

1.项目背景 

这里简单介绍一下项目需求背景,之前公司的项目基于EF++Repository+UnitOfWork的框架设计的,其中涉及到的技术有RabbitMq消息队列,Autofac依赖注入等常用的.net插件。由于公司的发展,业务不断更新,变得复杂起来,对于数据的实时性、存储容量要求也提高了一个新的高度。数据库上下文DbContext设计的是单例模式,基本上告别了多线程高并发的数据读写能力,看了园子里很多大神的博客,均为找到适合自己当前需求的DbContext的管理方式。总结目前主要的管理方式:

1)DbContext单例模式(长连接)。即公司之前的设计。很明显,这种设计方式无法支持多线程同步数据操作。报各种错误,最常见的,比如:集合已修改,无法进行枚举操作。---弃用

2)Using模式(短连接)。这种模式适合一些对于外键,导航属性不经常使用的场合,由于导航属性是放在上下文缓存中的,一旦上下文释放掉,导航属性就为null。当然,也尝试了其他大神的做法,比如,在上下文释放之前转换为        ToList或者使用饥饿加载的方式(ps:这种方式很不灵活,你总不可能遇到一个类类型就去利用反射加载找到它具有的导航属性吧或者直接InCluding),这些方法依旧没有办法解决目前的困境。也尝试这直接赋值给一个定义的同类      型的变量,但是对于这种带导航的导航的复杂类的深拷贝,没有找到合适的路子,有知道的可以告诉我,非常感谢!

以上两种方式及网上寻找的其他方式都没有解决我的问题。这里先上一下之前的Repository:

 1 using System.Data.Entity;
 2 using System.Data.Entity.Validation;
 3 
 4 namespace MM.Data.Library.Entityframework
 5 {
 6     public class EntityFrameworkRepositoryContext : RepositoryContext, IEntityFrameworkRepositoryContext
 7     {
 8         protected DbContext container;
 9 
10         public EntityFrameworkRepositoryContext(DbContext container)
11         {
12             this.container = container;
13         }
14 
15         public override void RegisterNew<TAggregateRoot>(TAggregateRoot obj)
16         {            
17             this.container.Set<TAggregateRoot>().Add(obj);
18             this.IsCommit = false;
19         }
20 
21         public override void RegisterModified<TAggregateRoot>(TAggregateRoot obj)
22         {
23             if (this.container.Entry<TAggregateRoot>(obj).State == EntityState.Detached)
24             {
25                 this.container.Set<TAggregateRoot>().Attach(obj);
26             }
27             this.container.Entry<TAggregateRoot>(obj).State = EntityState.Modified;
28             this.IsCommit = false;
29         }
30 
31         public override void RegisterDeleted<TAggregateRoot>(TAggregateRoot obj)
32         {
33             this.container.Set<TAggregateRoot>().Remove(obj);
34             this.IsCommit = false;
35         }
36 
37         public override void Rollback()
38         {
39             this.IsCommit = false;
40         }
41 
42         protected override void DoCommit()
43         {
44             if (!IsCommit)
45             {
46                 //var count = container.SaveChanges();
47                 //IsCommit = true;
48                 try
49                 {
50                     var count = container.SaveChanges();
51                     IsCommit = true;
52                 }
53                 catch (DbEntityValidationException dbEx)
54                 {
55                     foreach (var validationErrors in dbEx.EntityValidationErrors)
56                     {
57                         foreach (var validationError in validationErrors.ValidationErrors)
58                         { 
59                         }
60                     }
61                     IsCommit = false;
62                 }
63             }
64         }
65 
66         public System.Data.Entity.DbContext DbContext
67         {
68             get { return container; }
69         }
70 
71         public override void Dispose()
72         {
73             if (container != null)
74                 container.Dispose();
75         }
76     }
77 }
View Code

2.设计思路及方法

 从上下文的单例模式来看,所要解决的问题无非就是在多线程对数据库写操作上面。只要在这上面做手脚,问题应该就能引刃而解。我的想法是将所有的要修改的数据分别放入UpdateList,InsertList,DeleteList三个集合中去,然后提交到数据库保存。至于DbContext的管理,通过一个数据库工厂获取,保证每一个数据库的连接都是唯一的,不重复的(防止发生类似这种错误:正在创建模型,此时不可使用上下文。),用的时候直接去Factory拿。等到数据库提交成功后,清空集合数据。看起来,实现起来很容易,但是因为还涉及到其他技术,比如Redis。所以实现过程费劲。也许我的能力还差很多。总之,废话不多说,直接上部分实现代码:

数据库上下文建立工厂:

 1     /// <summary>
 2     /// 数据库建立工厂
 3     /// Modify By:
 4     /// Modify Date:
 5     /// Modify Reason:
 6     /// </summary>
 7     public sealed class DbFactory
 8     {
 9         public static IDbContext GetCurrentDbContext(string connectstring,string threadName)
10         {
11             lock (threadName)
12             {
13                 //CallContext:是线程内部唯一的独用的数据槽(一块内存空间)  
14                 //传递Context进去获取实例的信息,在这里进行强制转换。  
15                 var Context = CallContext.GetData("Context") as IDbContext;
16 
17                 if (Context == null)  //线程在内存中没有此上下文  
18                 {
19                     var Scope = UnitoonIotContainer.Container.BeginLifetimeScope();
20                     //如果不存在上下文 创建一个(自定义)EF上下文  并且放在数据内存中去  
21                     Context = Scope.Resolve<IDbContext>(new NamedParameter("connectionString", connectstring));
22                     CallContext.SetData("Context", Context);
23                 }
24                 else
25                 {
26 
27                     if (!Context.ConnectionString.Equals(connectstring))
28                     {
29<
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值