Skip to content

Instantly share code, notes, and snippets.

@hoangitk
Forked from khellang/Decorators.cs
Created August 16, 2016 08:25
Show Gist options
  • Save hoangitk/9d2b64ae32674f24ccf6fd77043ef362 to your computer and use it in GitHub Desktop.
Save hoangitk/9d2b64ae32674f24ccf6fd77043ef362 to your computer and use it in GitHub Desktop.
using System;
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
public class Program
{
public static void Main(string[] args)
{
var services = new ServiceCollection();
services.AddSingleton<INameValidator, NameValidator>();
// Imagine this was added by MVC
services.AddSingleton<IGreeter, Greeter>();
// Either
// services.Decorate<IGreeter>((provider, inner) => new ValidatingGreeter(inner, provider.GetRequiredService<INameValidator>()));
// services.Decorate<IGreeter>((_, inner) => new ErrorHandlingGreeter(inner));
// services.Decorate<IGreeter>((_, inner) => new LoggingGreeter(inner));
// Or
services.Decorate<IGreeter>((provider, inner) =>
new LoggingGreeter(
new ErrorHandlingGreeter(
new ValidatingGreeter(inner,
provider.GetRequiredService<INameValidator>()))));
services.BuildServiceProvider()
.GetRequiredService<IGreeter>()
.Greet("Kristian");
}
}
public static class ServiceCollectionExtensions
{
public static IServiceCollection Decorate<TService>(this IServiceCollection services, Func<IServiceProvider, TService, TService> decorator)
where TService : class
{
return services.Replace(services
.Single(descriptor => descriptor.ServiceType == typeof(TService))
.Decorate(decorator));
}
private static ServiceDescriptor Decorate<TService>(this ServiceDescriptor descriptor, Func<IServiceProvider, TService, TService> decorator)
{
return ServiceDescriptor.Describe(descriptor.ServiceType,
provider => decorator(provider, (TService) descriptor.GetInstance(provider)),
descriptor.Lifetime);
}
private static object GetInstance(this ServiceDescriptor descriptor, IServiceProvider provider)
{
return descriptor.ImplementationInstance
?? (descriptor.ImplementationType != null
? provider.GetServiceOrCreateInstance(descriptor.ImplementationType)
: descriptor.ImplementationFactory(provider));
}
private static object GetServiceOrCreateInstance(this IServiceProvider provider, Type type)
{
return ActivatorUtilities.GetServiceOrCreateInstance(provider, type);
}
}
public interface IGreeter
{
void Greet(string name);
}
public class Greeter : IGreeter
{
public void Greet(string name)
{
Console.WriteLine("--- GREETER START ---");
Console.WriteLine($"Hello {name}!");
throw new Exception("Oops! Something went wrong...");
Console.WriteLine("--- GREETER END ---");
}
}
public class LoggingGreeter : IGreeter
{
private readonly IGreeter _inner;
public LoggingGreeter(IGreeter inner)
{
_inner = inner;
}
public void Greet(string name)
{
Console.WriteLine("--- LOGGING START ---");
_inner.Greet(name);
Console.WriteLine("--- LOGGING END ---");
}
}
public interface INameValidator
{
bool IsValid(string name);
}
public class NameValidator : INameValidator
{
public bool IsValid(string name)
{
return name.StartsWith("K");
}
}
public class ValidatingGreeter : IGreeter
{
private readonly IGreeter _inner;
private readonly INameValidator _validator;
public ValidatingGreeter(IGreeter inner, INameValidator validator)
{
_inner = inner;
_validator = validator;
}
public void Greet(string name)
{
Console.WriteLine("--- VALIDATOR START ---");
if (!_validator.IsValid(name))
{
throw new InvalidOperationException("Invalid name.");
}
_inner.Greet(name);
Console.WriteLine("--- VALIDATOR END ---");
}
}
public class ErrorHandlingGreeter : IGreeter
{
private readonly IGreeter _inner;
public ErrorHandlingGreeter(IGreeter inner)
{
_inner = inner;
}
public void Greet(string name)
{
Console.WriteLine("--- ERROR HANDLER START ---");
try
{
_inner.Greet(name);
}
catch (Exception e)
{
Console.WriteLine($"ERROR: {e.Message}");
}
Console.WriteLine("--- ERROR HANDLER END ---");
}
}

Output

--- LOGGING START ---
--- ERROR HANDLER START ---
--- VALIDATOR START ---
--- GREETER START ---
Hello Kristian!
ERROR: Oops! Something went wrong...
--- ERROR HANDLER END ---
--- LOGGING END ---
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment