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