Created
July 15, 2013 19:42
-
-
Save dbones/6002795 to your computer and use it in GitHub Desktop.
simple internal IoC container, based on the ninject one. only handles singletons.
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 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; | |
} | |
} |
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
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 | |
} | |
} |
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 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