Skip to content

Instantly share code, notes, and snippets.

@aweber1
Last active January 30, 2020 21:23
Show Gist options
  • Save aweber1/a081e3a156559726de12fab05a3e9b44 to your computer and use it in GitHub Desktop.
Save aweber1/a081e3a156559726de12fab05a3e9b44 to your computer and use it in GitHub Desktop.
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);
}
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