Last active
January 30, 2020 21:23
-
-
Save aweber1/a081e3a156559726de12fab05a3e9b44 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class MvcControllerServicesConfigurator : IServicesConfigurator | |
{ | |
// Methods | |
public void Configure(IServiceCollection serviceCollection) | |
{ | |
serviceCollection.AddSingleton<Func<Database, ISitecoreService>>(_ => CreateSitecoreService); | |
serviceCollection.AddTransient(_ => CreateSitecoreContextService()); | |
serviceCollection.AddTransient(_ => CreateRequestContext()); | |
serviceCollection.AddTransient(_ => CreateGlassHtml()); | |
serviceCollection.AddScoped(_ => CreateMvcContext()); | |
serviceCollection.AddSingleton<Func<ISitecoreService>>(_ => Get<ISitecoreService>); | |
serviceCollection.AddSingleton<Func<IRequestContext>>(_ => Get<IRequestContext>); | |
serviceCollection.AddSingleton<Func<IGlassHtml>>(_ => Get<IGlassHtml>); | |
serviceCollection.AddSingleton<Func<IMvcContext>>(_ => Get<IMvcContext>); | |
string[] assemblyFilters = new string[] { "Duke.*" }; | |
serviceCollection.AddMvcControllers(assemblyFilters); | |
string[] textArray2 = new string[] { "Duke.*" }; | |
serviceCollection.AddClassesWithServiceAttribute(textArray2); | |
// NEW CODE | |
serviceCollection.AddClassesWithServiceAttributeWithFuncFactory(textArray2); | |
Func<IServiceProvider, ILog> func = p => LogManager.GetLogger("Duke.ControllerLog"); | |
serviceCollection.AddTransient(func); | |
} | |
private static IGlassHtml CreateGlassHtml() => | |
new GlassHtml(Get<ISitecoreService>()); | |
private static IMvcContext CreateMvcContext() => | |
new MvcContext(Get<ISitecoreService>(), Get<IGlassHtml>()); | |
private static IRequestContext CreateRequestContext() => | |
new RequestContext(Get<ISitecoreService>()); | |
private static ISitecoreService CreateSitecoreContextService() => | |
Get<Func<Database, ISitecoreService>>()(Sitecore.Context.Database); | |
private static ISitecoreService CreateSitecoreService(Database database) | |
{ | |
if (database == null) | |
{ | |
return new SitecoreService(Sitecore.Context.Database); | |
} | |
return new SitecoreService(database); | |
} | |
private static T Get<T>() => | |
ServiceProviderServiceExtensions.GetService<T>(ServiceLocator.ServiceProvider); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public static class ServiceCollectionExtensions | |
{ | |
public static void AddClassesWithServiceAttributeWithFuncFactory(this IServiceCollection serviceCollection, params string[] assemblyFilters) | |
{ | |
var assemblies = GetAssemblies(assemblyFilters); | |
serviceCollection.AddClassesWithServiceAttributeWithFuncFactory(assemblies); | |
} | |
public static void AddClassesWithServiceAttributeWithFuncFactory(this IServiceCollection serviceCollection, params Assembly[] assemblies) | |
{ | |
foreach(var assembly in assemblies) | |
{ | |
if (assembly.IsDynamic) | |
{ | |
continue; | |
} | |
var exportedTypes = GetExportedTypes(assembly); | |
foreach(var exportedType in exportedTypes) | |
{ | |
if (exportedType.IsAbstract || exportedType.IsGenericTypeDefinition) | |
{ | |
continue; | |
} | |
//Lifetime? lifetime = null; | |
//Type serviceType; | |
var customAttribute = exportedType.GetCustomAttribute<ServiceAttribute>(); | |
if (customAttribute != null) | |
{ | |
var serviceType = customAttribute.ServiceType ?? exportedType; | |
serviceCollection.AddFuncFactory(serviceType); | |
} | |
} | |
} | |
} | |
private static IServiceCollection AddFuncFactory(this IServiceCollection serviceCollection, Type serviceType) | |
{ | |
if (serviceCollection == null) throw new ArgumentNullException(nameof(serviceCollection)); | |
// What are we doing in this method? Registering a factory for the provided `serviceType` type. | |
// It's easier to envision with a concrete example, let's use `IDisplayablePage`, in which case the code below translates into something like this: | |
// serviceCollection.AddSingleton<Func<IDisplayablePage>>(_ => ServiceLocator.ServiceProvider.GetService<IDisplayablePage>()); | |
// This means we can constructor inject `Func<IDisplayablePage>` as a dependency where needed, e.g. | |
/* | |
private Func<IDisplayablePage> _displayablePageThunk; | |
public class MyContentsResolver(Func<IDisplayablePage> displayablePageThunk) { | |
_displayablePageThunk = displayablePageThunk; | |
} | |
public object ResolveContents { | |
IDisplayablePage displayablePage = _displayablePageThunk(); | |
// do something with `displayablePage` model | |
} | |
*/ | |
// For the sake of consistency, we want consumers of the factory functions to be able to use `Func<SomeType>` as the type of dependency. | |
// However, because we're working directly with `Type`, we have to manually construct an Expression to generate a `Func<SomeType>` declaration. | |
Type funcType = typeof(Func<>).MakeGenericType(serviceType); | |
serviceCollection.AddSingleton(funcType, (IServiceProvider provider) => | |
{ | |
// NOTE: do not use the `provider` (above) passed in to this lambda function to obtain a service. | |
// Be sure to use `ServiceLocator.ServiceProvider.GetService` instead, this way we ensure that we obtain services that haven't been disposed. | |
Expression<Func<object>> expressionWithFuncOfTypeObject = () => ServiceLocator.ServiceProvider.GetService(serviceType); | |
// note we only want to the `Body` of the previous expression, i.e. the lambda function, which can then be assigned to a `Func<T>` expression. | |
UnaryExpression expressionThatEvaluatesToAnObjectOfTypeArg = Expression.Convert(expressionWithFuncOfTypeObject.Body, serviceType); | |
// Combine it all together | |
LambdaExpression expressionWithFuncOfTypeArg = Expression.Lambda(typeof(Func<>).MakeGenericType(serviceType), expressionThatEvaluatesToAnObjectOfTypeArg); | |
// Then compile the expression, the result should be `Func<MyType>`, where `MyType` is the named type of `serviceType`. | |
var compiled = expressionWithFuncOfTypeArg.Compile(); | |
return compiled; | |
}); | |
return serviceCollection; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment