/// <summary> /// Triggers ServiceLocator bootstrapping (scans the GAC for assemblies with a name /// that matches *.ServiceLocator.DLL, by convention). /// </summary> /// <param name="web">The context's SPWeb. Keep null if none available.</param> /// <param name="site">The context's SPSite. Keep null if none available.</param> /// <param name="webApplication">The context's SPWebApplication. Keep null if none available.</param> /// <param name="farm">The context's SPFarm. Keep null if none available.</param> private void EnsureServiceLocatorAccessor(SPWeb web, SPSite site, SPWebApplication webApplication, SPFarm farm) { if (this.locatorAccessor == null) { lock (this.lockObject) { if (this.locatorAccessor == null) { try { // 1) Scan the GAC for any DLL matching the *.ServiceLocator.DLL pattern var matchingAssemblies = GacAssemblyLocator.GetAssemblies(new List <string>() { "GAC_MSIL" }, assemblyFileName => assemblyFileName.Contains(".ServiceLocator")); Assembly serviceLocatorAssembly = null; Type accessorType = null; if (matchingAssemblies.Any()) { if (matchingAssemblies.Count > 1) { // 2) If more than one service locator is found, we must disambiguate. We have to use the // contextual SPWeb, SPSite, SPWebApp or SPFarm objects and extract the preferred service // locator assembly name setting from their property bag. // The SPWeb's property bag is inspected first, if available, then the SPSite's RootWeb property // bag, then the SPWebApp's, then the SPFarm's property bag as a last resort. string contextObjectWhereDiscriminatorWasFound; string serviceLocatorAssemblyNameDiscriminator = FindServiceLocatorAccessorTypeNameFromMostSpecificPropertyBag(web, site, webApplication, farm, out contextObjectWhereDiscriminatorWasFound); string allServiceLocatorAssemblyNames = string.Join(";", matchingAssemblies.Select(locatorAssembly => locatorAssembly.FullName).ToArray()); string basicDisambiguationErrorMessage = string.Format( CultureInfo.InvariantCulture, "Failed to disambiguate between all DLLs in the GAC that match the *.ServiceLocator.DLL filename pattern. All matching assemblies in GAC: {0}.", allServiceLocatorAssemblyNames); if (!string.IsNullOrEmpty(serviceLocatorAssemblyNameDiscriminator)) { // We found a ServiceLocator assembly name in one of the context's Property Bags. serviceLocatorAssembly = matchingAssemblies.FirstOrDefault(assembly => assembly.FullName.Contains(serviceLocatorAssemblyNameDiscriminator)); if (serviceLocatorAssembly == null) { throw new InvalidOperationException(basicDisambiguationErrorMessage + " The discriminator found in one of the context's Property Bags (value=" + serviceLocatorAssemblyNameDiscriminator + ", property bag location=" + contextObjectWhereDiscriminatorWasFound + ") did not match either of the " + matchingAssemblies.Count + " ServiceLocator DLLs available in GAC. The discriminator value should match one of the DLLs so that we can determine which to use."); } } else { // We failed to find a discriminator setting in all of the context's Property Bags throw new InvalidOperationException(basicDisambiguationErrorMessage + " You cannot begin injection from the root application container if more that one ServiceLocator assembly exists in the GAC." + " You must begin injection with one of the following methods on your ISharePointServiceLocator: BeginLifetimeScope(SPFeature) or" + " BeginLifetimeScope(SPWeb) or BeginLifetimeScope(SPSite) or BeginLifetimeScope(SPWebApplication) or BeginLifetimeScope(SPFarm)," + " depending on your context. IMPORTANT: The property bags on the context' SPWeb, SPSite, SPWebApplication and SPFarm will be inspected" + " (in that order) to find a value for the key '" + KeyServiceLocatorAssemblyName + "'. This discriminator value will indicate to Dynamite's" + " AddOnProvidedServiceLocator which concrete add-on's ServiceLocator DLL to use in the current context."); } } if (serviceLocatorAssembly != null) { // Only one matching assembly, find its accessor class accessorType = FindServiceLocatorAccessorType(serviceLocatorAssembly); } else { // Only one ServiceLocator DLL found in GAC. There is no ambiguity: use this locator. serviceLocatorAssembly = matchingAssemblies[0]; } if (serviceLocatorAssembly != null) { // At this point we figured out the right matching assembly: find its accessor class within its types accessorType = FindServiceLocatorAccessorType(serviceLocatorAssembly); } } else { // Not even one DLL in GAC matches the *.ServiceLocator.DLL pattern throw new InvalidOperationException("Failed to find any assembly in the GAC that matches the *.ServiceLocator.DLL pattern."); } if (accessorType != null) { // 3) Create the accessor instance this.locatorAccessor = (ISharePointServiceLocatorAccessor)Activator.CreateInstance(accessorType); } else { throw new InvalidOperationException("Failed to find implementation of ISharePointServiceLocatorAccessor for AddOnProvidedServiceLocator. Your ServiceLocator assembly (" + serviceLocatorAssembly.FullName + ") should expose its static container through that interface."); } } catch (InvalidOperationException exception) { var logger = new TraceLogger("GSoft.Dynamite", "GSoft.Dynamite", false); logger.Error( "AddOnProvidedServiceLocator Initialization Error - An error occured while trying to find a DLL matching the pattern *ServiceLocator.dll in the GAC. The FallbackServiceLocator will be used instead as a last resort (no AddOn registration module will be registered). Exception: {0}", exception.ToString()); // Either no assembly in the GAC matches the pattern *.ServiceLocator.DLL pattern, // or in the matching assembly that was found, no class implements ISharePointServiceLocatorAccessor. // In this case, use our default all-available-Dynamite-modules-only service locator this.locatorAccessor = new FallbackServiceLocator(); } } } } }
private static IContainer ScanGacForAutofacModulesAndCreateContainer(string appRootNamespace, Func <string, bool> assemblyFileNameMatchingPredicate) { SPMonitoredScope monitor = null; try { monitor = new SPMonitoredScope("Dynamite - Bootstrapping dependency injection container " + appRootNamespace + " and scanning GAC for Modules."); } catch (TypeInitializationException) { // Failed to initialize local diagnostics service. Fail to log monitor trace. } catch (NullReferenceException) { // Failed to initialize local diagnostics service. Fail to log monitor trace. } var containerBuilderForDynamiteComponents = new ContainerBuilder(); // Don't just scan the GAC modules, also prepare the Dynamite core utils (by passing the params in ourselves). // I.E. each container gets its own DynamiteRegistrationModule components. var dynamiteModule = new AutofacDynamiteRegistrationModule(appRootNamespace); containerBuilderForDynamiteComponents.RegisterModule(dynamiteModule); var matchingAssemblies = GacAssemblyLocator.GetAssemblies(new List <string> { AssemblyFolder }, assemblyFileNameMatchingPredicate); // Make sure we exclude all other GSoft.Dynamite DLLs (i.e. ignore other versions deployed to same GAC) // so that other AutofacDynamiteRegistrationModule instances don't get registered. var filteredMatchingAssemblies = matchingAssemblies.Where(x => !x.FullName.Contains("GSoft.Dynamite,")); // Now make sure all Dynamite component modules (i.e. all DLLs that start with GSoft.Dynamite.*) are registered BEFORE // any other modules. // This ensures that "client" modules will be able to override the Container registrations of GSoft.Dynamite.Components modules. var dynamiteComponentModuleAssemblies = filteredMatchingAssemblies.Where(assembly => assembly.FullName.StartsWith("GSoft.Dynamite.", StringComparison.OrdinalIgnoreCase)); var allTheRest = filteredMatchingAssemblies.Where(assembly => !assembly.FullName.StartsWith("GSoft.Dynamite.", StringComparison.OrdinalIgnoreCase)); // 1) Build the base container with only Dynamite-related components containerBuilderForDynamiteComponents.RegisterAssemblyModules(dynamiteComponentModuleAssemblies.ToArray()); var container = containerBuilderForDynamiteComponents.Build(); var logger = container.Resolve <ILogger>(); string dynamiteAssemblyNameEnumeration = string.Empty; dynamiteComponentModuleAssemblies.Cast <Assembly>().ToList().ForEach(a => dynamiteAssemblyNameEnumeration += a.FullName + ", "); logger.Info("Dependency injection module registration for container " + appRootNamespace + ". The following Dynamite component assemblies were scanned and any Autofac Module within was registered. The order of registrations was: " + dynamiteAssemblyNameEnumeration); // 2) Extend the original registrations with any remaining AddOns' registrations var containerBuilderForAddOns = new ContainerBuilder(); containerBuilderForAddOns.RegisterAssemblyModules(allTheRest.ToArray()); containerBuilderForAddOns.Update(container); string addOnAssemblyNameEnumeration = string.Empty; allTheRest.Cast <Assembly>().ToList().ForEach(a => addOnAssemblyNameEnumeration += a.FullName + ", "); logger.Info("Dependency injection module registration for container " + appRootNamespace + ". The following Add-On component assemblies (i.e. extensions to the core Dynamite components) were scanned and any Autofac Module within was registered. The order of registrations was: " + addOnAssemblyNameEnumeration); // Log the full component registry for easy debugging through ULS string componentRegistryAsString = string.Empty; var regAndServices = container.ComponentRegistry.Registrations.SelectMany(r => r.Services.OfType <IServiceWithType>(), (r, s) => new { r, s }); regAndServices.ToList().ForEach(regAndService => componentRegistryAsString += "[" + regAndService.s.ServiceType.FullName + "->" + regAndService.r.Activator.LimitType.FullName + "], "); logger.Info("Autofac component registry details for container " + appRootNamespace + ": " + componentRegistryAsString); if (monitor != null) { monitor.Dispose(); } return(container); }