EntityFramework用法探索(七)线程安全实践

前文中,我们通过Unity来注册各种类型和WiringUp。

 1       IUnityContainer container = new UnityContainer()
 2         .RegisterType(typeof(IRepository<>), typeof(Repository<>), new ContainerControlledLifetimeManager())
 3         .RegisterType<IUnitOfWork, UnitOfWork>(new ContainerControlledLifetimeManager())
 4         .RegisterType<DbContext, RETAILContext>(new ContainerControlledLifetimeManager())
 5         .RegisterType<DbContextAdapter>(new ContainerControlledLifetimeManager())
 6         .RegisterType<IObjectSetFactory, DbContextAdapter>(new ContainerControlledLifetimeManager())
 7         .RegisterType<IObjectContext, DbContextAdapter>(new ContainerControlledLifetimeManager())
 8         .RegisterType<ICustomerRepository, CustomerRepository>(new ContainerControlledLifetimeManager());
 9 
10       UnityServiceLocator locator = new UnityServiceLocator(container);
11       ServiceLocator.SetLocatorProvider(() => locator);
12 
13       ICustomerRepository customerRepository = container.Resolve<ICustomerRepository>();

但选择使用了ContainerControlledLifetimeManager对象生命周期管理器,其将每个对象存储为Singleton。这导致在多线程环境下会产生异常。

例如我们尝试在多线程条件下更新Customer表:

 1       List<Task> tasks = new List<Task>();
 2       for (int i = 0; i < 16; i++)
 3       {
 4         DomainModels.Customer modifiedCustomer = Mapper.Map<DomainModels.Customer, DomainModels.Customer>(customer1);
 5         modifiedCustomer.Name = modifiedCustomer.Name + i;
 6 
 7         Task t = Task.Factory.StartNew(() =>
 8         {
 9           try
10           {
11             customerRepository.UpdateCustomer(modifiedCustomer);
12           }
13           catch (Exception ex)
14           {
15             Console.WriteLine("Exception occurred in thread " + Thread.CurrentThread.ManagedThreadId);
16             Console.WriteLine(ex.Message);
17           }
18         });
19         tasks.Add(t);
20       }
21       Task.WaitAll(tasks.ToArray());

但由于我们仍然需要EntityFramework的Local功能,即在当前线程环境下始终使用当前上下文中的对象。我们可能还无法选择其他Unity对象生命期管理模型

此时,我们考虑一种新的方法,引入线程Scope功能,即在给定线程中,使用同一个UnityContainer来维护对象,这样间接利用的EntityFramework的上下文功能。

原理很简单,就是为每个线程生成一个单独的ChildUnityContainer。

 1   public class UnityContainerScope : IDisposable
 2   {
 3     private static ConcurrentDictionary<int, bool> scopeMapping
 4       = new ConcurrentDictionary<int, bool>();
 5 
 6     protected UnityContainerScope()
 7     {
 8       ScopeId = Thread.CurrentThread.ManagedThreadId;
 9       scopeMapping.Add(ScopeId, true);
10     }
11 
12     public int ScopeId { get; private set; }
13     public static int ScopeCount { get { return scopeMapping.Count; } }
14 
15     public static UnityContainerScope NewScope()
16     {
17       return new UnityContainerScope();
18     }
19 
20     public static bool InScope(int scopeId)
21     {
22       return scopeMapping.ContainsKey(scopeId);
23     }
24 
25     public void Dispose()
26     {
27       UnityContainerDispatcher.DisposeContainer();
28       scopeMapping.Remove(ScopeId);
29     }
30   }

这里同时需要一个UnityContainerDispatcher来负责为线程生成Container容器。

 1   public static class UnityContainerDispatcher
 2   {
 3     private static IUnityContainer parentContainer = null;
 4     private static ConcurrentDictionary<int, IUnityContainer> containerMapping
 5       = new ConcurrentDictionary<int, IUnityContainer>();
 6 
 7     public static void InjectParentContainer(IUnityContainer unity)
 8     {
 9       if (unity == null)
10         throw new ArgumentNullException("unity");
11 
12       parentContainer = unity;
13     }
14 
15     public static IUnityContainer GetContainer()
16     {
17       int key = Thread.CurrentThread.ManagedThreadId;
18 
19       if (!UnityContainerScope.InScope(key))
20       {
21         throw new UnityContainerNotInScopeException(
22           string.Format(CultureInfo.InvariantCulture,
23           "The specified scope id [{0}] is not in scope.", key));
24       }
25 
26       if (!containerMapping.ContainsKey(key))
27       {
28         BuildUpContainer(key);
29       }
30 
31       return containerMapping.Get(key);
32     }
33 
34     public static void DisposeContainer()
35     {
36       int key = Thread.CurrentThread.ManagedThreadId;
37       IUnityContainer container = containerMapping.Remove(key);
38       if (container != null)
39       {
40         container.Dispose();
41       }
42     }
43 
44     private static void BuildUpContainer(int key)
45     {
46       if (parentContainer == null)
47         throw new InvalidProgramException("The parent container cannot be null.");
48 
49       IUnityContainer childContainer = parentContainer.CreateChildContainer();
50       containerMapping.Add(key, childContainer);
51     }
52   }

在注入的根UnityContainer中,我们通过使用CreateChildContainer方法来获取一个新的Container,同时继承所有根容器的注册配置信息。这要求使用HierarchicalLifetimeManager生命期管理器

此时,我们的代码修改为,

 1       IUnityContainer container = new UnityContainer()
 2         .RegisterType(typeof(IRepository<>), typeof(Repository<>), new HierarchicalLifetimeManager())
 3         .RegisterType<IUnitOfWork, UnitOfWork>(new HierarchicalLifetimeManager())
 4         .RegisterType<DbContext, RETAILContext>(new HierarchicalLifetimeManager())
 5         .RegisterType<DbContextAdapter>(new HierarchicalLifetimeManager())
 6         .RegisterType<IObjectSetFactory, DbContextAdapter>(new HierarchicalLifetimeManager())
 7         .RegisterType<IObjectContext, DbContextAdapter>(new HierarchicalLifetimeManager())
 8         .RegisterType<ICustomerRepository, CustomerRepository>(new HierarchicalLifetimeManager());
 9 
10       UnityContainerDispatcher.InjectParentContainer(container);
11 
12       ICustomerRepository customerRepository = container.Resolve<ICustomerRepository>();

创建多线程测试代码,

 1       List<Task> tasks = new List<Task>();
 2       for (int i = 0; i < 16; i++)
 3       {
 4         DomainModels.Customer modifiedCustomer = Mapper.Map<DomainModels.Customer, DomainModels.Customer>(customer1);
 5         modifiedCustomer.Name = modifiedCustomer.Name + i;
 6 
 7         Task t = Task.Factory.StartNew(() =>
 8         {
 9           try
10           {
11             using (UnityContainerScope scope = UnityContainerScope.NewScope())
12             {
13               customerRepository.UpdateCustomer(modifiedCustomer);
14               Console.WriteLine("Modified " + modifiedCustomer.Name + " in thread " + Thread.CurrentThread.ManagedThreadId);
15             }
16           }
17           catch (Exception ex)
18           {
19             Console.WriteLine("Exception occurred in thread " + Thread.CurrentThread.ManagedThreadId);
20             Console.WriteLine(ex.Message);
21           }
22         });
23         tasks.Add(t);
24       }
25       Task.WaitAll(tasks.ToArray());

测试结果表明已经可以安全的在多线程条件下工作了。

完整代码和索引

EntityFramework用法探索系列

完整代码下载

转载于:https://www.cnblogs.com/gaochundong/archive/2013/06/06/entityframework_usage_thread_scope.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值