Skip to content

Instantly share code, notes, and snippets.

@codeimpossible
Last active January 9, 2017 18:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save codeimpossible/c7ab1d4c055fb8242eb0ddac6ec47328 to your computer and use it in GitHub Desktop.
Save codeimpossible/c7ab1d4c055fb8242eb0ddac6ec47328 to your computer and use it in GitHub Desktop.
Example of determining dependency injection based on interface attribute.

Currently we use a static factory class to create our DAOs. This isn't a bad solution but we'd like to be able to use the built-in dependency injection features within AspNetCore. However, we have a few DAOs that utilize our OSS fault tolerance library: Mjolnir. These DAOs require that we createa a proxy class to wrap the method calls so that they use Mjolnirs internals. This is currently handled seemlessly by the DAOFactory, it can create simple concretes or these Mjolnir proxies for us by detecting if the dao interface is decorated with a specific attribute.

This code lets us change what gets injected in place of IDao by changing the [InjectInstead] attribute. This is just a simple POC, our actual implementation of this would differ slightly as we'd likely inject a Func that would return the proxy class so we could defer creation until the instance is needed.

So while this solution requires us to implement a custom ServiceCollection it shouldn't be messy at all to wire something like this up at startup.

public IServiceProvider ConfigureServices(IServiceCollection services)
{
  var hudlServices = new HudlServiceCollection(); // potentially MjolnirAutoProxyServiceCollection, or something like that
  services.forEach(hudlServices.Add);
  
  // add other framework services

  return hudlServices.BuildServiceProvider();
}
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Collections;
using System.Reflection;
using System.Threading.Tasks;
namespace ConsoleApp1
{
[System.AttributeUsage(AttributeTargets.Interface, Inherited = false, AllowMultiple = false)]
sealed class InjectInsteadAttribute : Attribute
{
// This is a named argument
public Type InjectType { get; set; }
}
[InjectInstead(InjectType = typeof(StringDao))]
public interface IDao
{
List<object> GetObjects();
}
public class NumberDao : IDao
{
public List<object> GetObjects()
{
return new List<object>() { 1, 2, 3, 4 };
}
}
public class StringDao : IDao
{
public List<object> GetObjects()
{
return new List<object>() { "hello", "world" };
}
}
public class CustomServiceCollection : IServiceCollection
{
private readonly List<ServiceDescriptor> _descriptors = new List<ServiceDescriptor>();
/// <inheritdoc />
public int Count => _descriptors.Count;
/// <inheritdoc />
public bool IsReadOnly => false;
public ServiceDescriptor this[int index]
{
get
{
return _descriptors[index];
}
set
{
_descriptors[index] = value;
}
}
/// <inheritdoc />
public void Clear()
{
_descriptors.Clear();
}
/// <inheritdoc />
public bool Contains(ServiceDescriptor item)
{
return _descriptors.Contains(item);
}
/// <inheritdoc />
public void CopyTo(ServiceDescriptor[] array, int arrayIndex)
{
_descriptors.CopyTo(array, arrayIndex);
}
/// <inheritdoc />
public bool Remove(ServiceDescriptor item)
{
return _descriptors.Remove(item);
}
/// <inheritdoc />
public IEnumerator<ServiceDescriptor> GetEnumerator()
{
return _descriptors.GetEnumerator();
}
void ICollection<ServiceDescriptor>.Add(ServiceDescriptor item)
{
var svcTypeInfo = item.ServiceType.GetTypeInfo();
var implTypeInfo = item.ImplementationType.GetTypeInfo();
InjectInsteadAttribute attr = svcTypeInfo.GetCustomAttribute<InjectInsteadAttribute>();
if (attr == null)
{
attr = implTypeInfo.GetCustomAttribute<InjectInsteadAttribute>();
}
if (attr != null)
{
// we're supposed to inject something else in place of this dependency
item = new ServiceDescriptor(item.ServiceType, attr.InjectType, item.Lifetime);
}
_descriptors.Add(item);
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public int IndexOf(ServiceDescriptor item)
{
return _descriptors.IndexOf(item);
}
public void Insert(int index, ServiceDescriptor item)
{
_descriptors.Insert(index, item);
}
public void RemoveAt(int index)
{
_descriptors.RemoveAt(index);
}
}
public class App
{
private readonly IServiceProvider _services;
public App(IServiceProvider services)
{
_services = services;
}
public async Task Run()
{
await Task.Run(() =>
{
var data = _services.GetRequiredService<IDao>().GetObjects();
data.ForEach(o => Console.WriteLine(o.ToString()));
});
}
}
public class Program
{
public static void Main(string[] args)
{
var services = new CustomServiceCollection();
AddDao(services);
var app = new App(services.BuildServiceProvider());
app.Run();
Console.ReadLine();
}
private static void AddDao(IServiceCollection services)
{
services.AddTransient<IDao, NumberDao>();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment