123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243 |
- namespace Caliburn.Micro.Core {
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Linq;
- using System.Reflection;
- /// <summary>
- /// A simple IoC container.
- /// </summary>
- public class SimpleContainer {
- static readonly Type delegateType = typeof(Delegate);
- static readonly Type enumerableType = typeof(IEnumerable);
- readonly List<ContainerEntry> entries;
- /// <summary>
- /// Initializes a new instance of the <see cref = "SimpleContainer" /> class.
- /// </summary>
- public SimpleContainer() {
- entries = new List<ContainerEntry>();
- }
- SimpleContainer(IEnumerable<ContainerEntry> entries) {
- this.entries = new List<ContainerEntry>(entries);
- }
- /// <summary>
- /// Registers the instance.
- /// </summary>
- /// <param name = "service">The service.</param>
- /// <param name = "key">The key.</param>
- /// <param name = "implementation">The implementation.</param>
- public void RegisterInstance(Type service, string key, object implementation) {
- RegisterHandler(service, key, container => implementation);
- }
- /// <summary>
- /// Registers the class so that a new instance is created on every request.
- /// </summary>
- /// <param name = "service">The service.</param>
- /// <param name = "key">The key.</param>
- /// <param name = "implementation">The implementation.</param>
- public void RegisterPerRequest(Type service, string key, Type implementation) {
- RegisterHandler(service, key, container => container.BuildInstance(implementation));
- }
- /// <summary>
- /// Registers the class so that it is created once, on first request, and the same instance is returned to all requestors thereafter.
- /// </summary>
- /// <param name = "service">The service.</param>
- /// <param name = "key">The key.</param>
- /// <param name = "implementation">The implementation.</param>
- public void RegisterSingleton(Type service, string key, Type implementation) {
- object singleton = null;
- RegisterHandler(service, key, container => singleton ?? (singleton = container.BuildInstance(implementation)));
- }
- /// <summary>
- /// Registers a custom handler for serving requests from the container.
- /// </summary>
- /// <param name = "service">The service.</param>
- /// <param name = "key">The key.</param>
- /// <param name = "handler">The handler.</param>
- public void RegisterHandler(Type service, string key, Func<SimpleContainer, object> handler) {
- GetOrCreateEntry(service, key).Add(handler);
- }
- /// <summary>
- /// Unregisters any handlers for the service/key that have previously been registered.
- /// </summary>
- /// <param name = "service">The service.</param>
- /// <param name = "key">The key.</param>
- public void UnregisterHandler(Type service, string key) {
- var entry = GetEntry(service, key);
- if (entry != null) {
- entries.Remove(entry);
- }
- }
- /// <summary>
- /// Requests an instance.
- /// </summary>
- /// <param name = "service">The service.</param>
- /// <param name = "key">The key.</param>
- /// <returns>The instance, or null if a handler is not found.</returns>
- public object GetInstance(Type service, string key) {
- var entry = GetEntry(service, key);
- if (entry != null) {
- return entry.Single()(this);
- }
- if (service == null) {
- return null;
- }
- if (delegateType.IsAssignableFrom(service)) {
- var typeToCreate = service.GetGenericArguments()[0];
- var factoryFactoryType = typeof(FactoryFactory<>).MakeGenericType(typeToCreate);
- var factoryFactoryHost = Activator.CreateInstance(factoryFactoryType);
- var factoryFactoryMethod = factoryFactoryType.GetMethod("Create", new Type[] { typeof(SimpleContainer) });
- return factoryFactoryMethod.Invoke(factoryFactoryHost, new object[] { this });
- }
- if (enumerableType.IsAssignableFrom(service) && service.IsGenericType()) {
- var listType = service.GetGenericArguments()[0];
- var instances = GetAllInstances(listType).ToList();
- var array = Array.CreateInstance(listType, instances.Count);
- for (var i = 0; i < array.Length; i++) {
- array.SetValue(instances[i], i);
- }
- return array;
- }
- return null;
- }
- /// <summary>
- /// Determines if a handler for the service/key has previously been registered.
- /// </summary>
- /// <param name="service">The service.</param>
- /// <param name="key">The key.</param>
- /// <returns>True if a handler is registere; false otherwise.</returns>
- public bool HasHandler(Type service, string key) {
- return GetEntry(service, key) != null;
- }
- /// <summary>
- /// Requests all instances of a given type.
- /// </summary>
- /// <param name = "service">The service.</param>
- /// <returns>All the instances or an empty enumerable if none are found.</returns>
- public IEnumerable<object> GetAllInstances(Type service) {
- var entry = GetEntry(service, null);
- return entry != null ? entry.Select(x => x(this)) : new object[0];
- }
- /// <summary>
- /// Pushes dependencies into an existing instance based on interface properties with setters.
- /// </summary>
- /// <param name = "instance">The instance.</param>
- public void BuildUp(object instance) {
- var injectables = from property in instance.GetType().GetProperties()
- where property.CanRead && property.CanWrite && property.PropertyType.IsInterface()
- select property;
- foreach (var propertyInfo in injectables) {
- var injection = GetAllInstances(propertyInfo.PropertyType).ToArray();
- if (injection.Any()) {
- propertyInfo.SetValue(instance, injection.First(), null);
- }
- }
- }
- /// <summary>
- /// Creates a child container.
- /// </summary>
- /// <returns>A new container.</returns>
- public SimpleContainer CreateChildContainer() {
- return new SimpleContainer(entries);
- }
- ContainerEntry GetOrCreateEntry(Type service, string key) {
- var entry = GetEntry(service, key);
- if (entry == null) {
- entry = new ContainerEntry { Service = service, Key = key };
- entries.Add(entry);
- }
- return entry;
- }
- ContainerEntry GetEntry(Type service, string key) {
- if (service == null) {
- return entries.FirstOrDefault(x => x.Key == key);
- }
- if (key == null) {
- return entries.FirstOrDefault(x => x.Service == service && x.Key == null)
- ?? entries.FirstOrDefault(x => x.Service == service);
- }
- return entries.FirstOrDefault(x => x.Service == service && x.Key == key);
- }
- /// <summary>
- /// Actually does the work of creating the instance and satisfying it's constructor dependencies.
- /// </summary>
- /// <param name = "type">The type.</param>
- /// <returns></returns>
- protected object BuildInstance(Type type) {
- var args = DetermineConstructorArgs(type);
- return ActivateInstance(type, args);
- }
- /// <summary>
- /// Creates an instance of the type with the specified constructor arguments.
- /// </summary>
- /// <param name = "type">The type.</param>
- /// <param name = "args">The constructor args.</param>
- /// <returns>The created instance.</returns>
- protected virtual object ActivateInstance(Type type, object[] args) {
- var instance = args.Length > 0 ? System.Activator.CreateInstance(type, args) : System.Activator.CreateInstance(type);
- Activated(instance);
- return instance;
- }
- /// <summary>
- /// Occurs when a new instance is created.
- /// </summary>
- public event Action<object> Activated = delegate { };
- object[] DetermineConstructorArgs(Type implementation) {
- var args = new List<object>();
- var constructor = SelectEligibleConstructor(implementation);
- if (constructor != null)
- args.AddRange(constructor.GetParameters().Select(info => GetInstance(info.ParameterType, null)));
- return args.ToArray();
- }
- static ConstructorInfo SelectEligibleConstructor(Type type) {
- return (from c in type.GetConstructors().Where(c => c.IsPublic)
- orderby c.GetParameters().Length descending
- select c).FirstOrDefault();
- }
- class ContainerEntry : List<Func<SimpleContainer, object>> {
- public string Key;
- public Type Service;
- }
- class FactoryFactory<T> {
- public Func<T> Create(SimpleContainer container) {
- return () => (T)container.GetInstance(typeof(T), null);
- }
- }
- }
- }
|