Skip to content

Instantly share code, notes, and snippets.

@dbones
Created July 15, 2013 19:42
Show Gist options
  • Save dbones/6002795 to your computer and use it in GitHub Desktop.
Save dbones/6002795 to your computer and use it in GitHub Desktop.
simple internal IoC container, based on the ninject one. only handles singletons.
public interface IContainer : IDisposable
{
void Add(Type contract, Type service);
object Resolve(Type contract);
}
/// <summary>
/// this contaier is a simple container which handles all contracts as singletons
/// </summary>
public class InternalContainer : IContainer
{
private readonly IDictionary<Type, Registration> _registrations = new Dictionary<Type, Registration>();
private readonly IDictionary<Type, object> _instances = new Dictionary<Type, object>();
private readonly object _lock = new object();
public void Add(Type contract, Type service)
{
lock (_lock)
{
Registration registration;
if (_registrations.TryGetValue(contract, out registration))
{
registration.Service = service;
}
else
{
registration = new Registration(contract, service);
_registrations.Add(contract, registration);
}
}
}
public object Resolve(Type contract)
{
lock (_lock)
{
Func<Registration, Type> getServiceType;
if (contract.IsGenericType)
{
//no support for list or lazy, this is a simple container
Type[] genericArguments = contract.GetGenericArguments();
getServiceType = r => r.Service.MakeGenericType(genericArguments);
contract = contract.GetGenericTypeDefinition();
}
else
{
getServiceType = r => r.Service;
}
Registration registration;
if (!_registrations.TryGetValue(contract, out registration))
{
return null;
}
var serviceType = getServiceType(registration);
object instance;
if (_instances.TryGetValue(serviceType, out instance))
{
return instance;
}
instance = CreateInstance(serviceType);
_instances.Add(serviceType, instance);
return instance;
}
}
public object CreateInstance(Type service)
{
var ctor = service.GetConstructors()
.OrderByDescending(x => x.GetParameters().Count())
.Where(x => x.GetParameters().All(p => Resolve(p.ParameterType) != null))
.FirstOrDefault();
if (ctor == null)
{
throw new Exception("cannot resolve service");
}
var ctorArgs = ctor.GetParameters().Select(x => Resolve(x.ParameterType)).ToArray();
var instance = ctor.Invoke(ctorArgs);
return instance;
}
public void Dispose()
{
lock (_lock)
{
foreach (var instance in _instances.Values)
{
var disposeOfMe = instance as IDisposable;
if (disposeOfMe != null)
{
disposeOfMe.Dispose();
}
}
_instances.Clear();
}
}
}
public static class ContainerExtensions
{
public static void Add<TContract, TService>(this IContainer container) where TService : TContract
{
container.Add(typeof(TContract), typeof(TService));
}
public static TContract Resolve<TContract>(this IContainer container)
{
return (TContract)container.Resolve(typeof(TContract));
}
}
public class Registration
{
private readonly int _hash;
private Type _service;
public Registration(Type contract, Type service)
{
IsAllowedToBeOverridden = false;
IsDefault = false;
Contract = contract;
Service = service;
_hash = contract.FullName.GetHashCode();
}
public Type Contract { get; private set; }
public Type Service
{
get { return _service; }
set
{
if (!IsAllowedToBeOverridden && IsDefault)
{
throw new Exception("you cannot override this service");
}
_service = value;
}
}
/// <summary>
/// is this set via boxes, or an extension
/// </summary>
internal bool IsDefault { get; set; }
/// <summary>
/// indicate if the component is allowed to be overridden
/// </summary>
internal bool IsAllowedToBeOverridden { get; set; }
public override int GetHashCode()
{
return _hash;
}
}
class Program
{
static void Main(string[] args)
{
var container = new InternalContainer();
//container.Add<ILogger, ConsoleLogger>();
container.Add<IAccountController, AccountController>();
container.Add(typeof(IRepository<>), typeof(MemoryRepository<>));
var accountController = container.Resolve<IAccountController>();
accountController.Deposit(123, 100);
//http://blogs.msdn.com/b/shawnfa/archive/2004/06/07/150378.aspx
//http://www.dotnetframework.org/default.aspx/4@0/4@0/untmp/DEVDIV_TFS/Dev10/Releases/RTMRel/public/internal/NDP/inc/StrongNameHelpers@cs/1305411/StrongNameHelpers@cs
}
}
public interface ILogger
{
void Log(string msg);
}
class ConsoleLogger : ILogger
{
public void Log(string msg)
{
Console.WriteLine(msg);
}
}
class NotProvidedConsoleLogger : ILogger
{
public void Log(string msg)
{
Console.WriteLine("(not injected logger) " + msg);
}
}
public interface IAccountController
{
void Deposit(int id, double amount);
}
class AccountController : IAccountController
{
private readonly IRepository<Account> _accountRepository;
private readonly ILogger _logger;
public AccountController(IRepository<Account> accountRepository, ILogger logger)
{
_accountRepository = accountRepository;
_logger = logger;
//meh
var account = new Account() {Id = 123};
_accountRepository.Save(account);
}
public AccountController(IRepository<Account> accountRepository)
: this(accountRepository, new NotProvidedConsoleLogger())
{
}
public void Deposit(int id, double amount)
{
Account account = _accountRepository.Get(id);
account.Deposit(amount);
_logger.Log(string.Format("Doposited {0} into account {1}", amount, id));
}
}
public interface IRepository<T> where T : Entity
{
T Get(int id);
void Save(T item);
}
class MemoryRepository<T> : IRepository<T> where T : Entity
{
readonly IList<T> _items = new List<T>();
public T Get(int id)
{
return _items.FirstOrDefault(x => x.Id == id);
}
public void Save(T item)
{
_items.Add(item);
}
}
public abstract class Entity
{
public int Id { get; set; }
}
public class Account : Entity
{
public double Balance { get; private set; }
public void Deposit(double amount)
{
Balance += amount;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment