在前文中,我们通过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用法探索系列