CreationContext
Used during a component request, passed along to the whole process.
This allow some data to be passed along the process, which is used
to detected cycled dependency graphs and now it's also being used
to provide arguments to components.
using Castle.Core;
using Castle.MicroKernel.ComponentActivator;
using Castle.MicroKernel.Releasers;
using Castle.MicroKernel.SubSystems.Conversion;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
namespace Castle.MicroKernel.Context
{
[Serializable]
public class CreationContext : ISubDependencyResolver
{
public class ResolutionContext : IDisposable
{
private readonly CreationContext context;
private readonly IHandler handler;
private readonly bool requiresDecommission;
private readonly bool trackContext;
private Burden burden;
private Arguments extendedProperties;
public Burden Burden => burden;
public CreationContext Context => context;
public IHandler Handler => handler;
public ResolutionContext(CreationContext context, IHandler handler, bool requiresDecommission, bool trackContext)
{
this.context = context;
this.requiresDecommission = requiresDecommission;
this.trackContext = trackContext;
this.handler = handler;
}
public void AttachBurden(Burden burden)
{
this.burden = burden;
}
public Burden CreateBurden(bool trackedExternally)
{
burden = new Burden(handler, requiresDecommission, trackedExternally);
return burden;
}
public object GetContextualProperty(object key)
{
if (extendedProperties == null)
return null;
return extendedProperties[key];
}
public void SetContextualProperty(object key, object value)
{
if (key == null)
throw new ArgumentNullException("key");
if (extendedProperties == null)
extendedProperties = new Arguments();
extendedProperties[key] = value;
}
public void Dispose()
{
context.ExitResolutionContext(burden, trackContext);
}
}
private readonly ITypeConverter converter;
private readonly IHandler handler;
private readonly Stack<IHandler> handlerStack;
private readonly Type requestedType;
private readonly Stack<ResolutionContext> resolutionStack;
private Arguments additionalArguments;
private Arguments extendedProperties;
private Type[] genericArguments;
private bool isResolving = true;
public Arguments AdditionalArguments {
get {
if (additionalArguments == null)
additionalArguments = new Arguments();
return additionalArguments;
}
}
public Type[] GenericArguments {
get {
if (genericArguments == null)
genericArguments = ExtractGenericArguments(requestedType);
return genericArguments;
}
}
public IHandler Handler => handler;
public bool HasAdditionalArguments {
get {
if (additionalArguments != null)
return additionalArguments.Count != 0;
return false;
}
}
public virtual bool IsResolving => isResolving;
public IReleasePolicy ReleasePolicy { get; set; }
public Type RequestedType => requestedType;
public CreationContext(Type requestedType, CreationContext parentContext, bool propagateInlineDependencies)
: this(parentContext.Handler, parentContext.ReleasePolicy, requestedType, null, null, parentContext)
{
if (parentContext == null)
throw new ArgumentNullException("parentContext");
if (parentContext.extendedProperties != null)
extendedProperties = new Arguments(parentContext.extendedProperties);
if (propagateInlineDependencies && parentContext.HasAdditionalArguments)
additionalArguments = new Arguments(parentContext.additionalArguments);
}
public CreationContext(IHandler handler, IReleasePolicy releasePolicy, Type requestedType, Arguments additionalArguments, ITypeConverter converter, CreationContext parent)
{
this.requestedType = requestedType;
this.handler = handler;
ReleasePolicy = releasePolicy;
this.additionalArguments = additionalArguments;
this.converter = converter;
if (parent != null) {
resolutionStack = parent.resolutionStack;
handlerStack = parent.handlerStack;
} else {
handlerStack = new Stack<IHandler>(4);
resolutionStack = new Stack<ResolutionContext>(4);
}
}
private CreationContext()
{
ReleasePolicy = new NoTrackingReleasePolicy();
handlerStack = new Stack<IHandler>(4);
resolutionStack = new Stack<ResolutionContext>(4);
}
public void AttachExistingBurden(Burden burden)
{
ResolutionContext resolutionContext;
try {
resolutionContext = resolutionStack.Peek();
} catch (InvalidOperationException) {
throw new ComponentActivatorException("Not in a resolution context. 'AttachExistingBurden' method can only be called withing a resoltion scope. (after 'EnterResolutionContext' was called within a handler)", null);
}
resolutionContext.AttachBurden(burden);
}
public void BuildCycleMessageFor(IHandler duplicateHandler, StringBuilder message)
{
message.AppendFormat("Component '{0}'", duplicateHandler.ComponentModel.Name);
foreach (IHandler item in handlerStack) {
message.AppendFormat(" resolved as dependency of", Array.Empty<object>());
message.AppendLine();
message.AppendFormat("\tcomponent '{0}'", item.ComponentModel.Name);
}
message.AppendLine(" which is the root component being resolved.");
}
public Burden CreateBurden(IComponentActivator componentActivator, bool trackedExternally)
{
ResolutionContext resolutionContext;
try {
resolutionContext = resolutionStack.Peek();
} catch (InvalidOperationException) {
throw new ComponentActivatorException("Not in a resolution context. 'CreateBurden' method can only be called withing a resoltion scope. (after 'EnterResolutionContext' was called within a handler)", null);
}
IDependencyAwareActivator dependencyAwareActivator = componentActivator as IDependencyAwareActivator;
if (dependencyAwareActivator != null)
trackedExternally |= dependencyAwareActivator.IsManagedExternally(resolutionContext.Handler.ComponentModel);
return resolutionContext.CreateBurden(trackedExternally);
}
public ResolutionContext EnterResolutionContext(IHandler handlerBeingResolved, bool requiresDecommission)
{
return EnterResolutionContext(handlerBeingResolved, true, requiresDecommission);
}
public ResolutionContext EnterResolutionContext(IHandler handlerBeingResolved, bool trackContext, bool requiresDecommission)
{
ResolutionContext resolutionContext = new ResolutionContext(this, handlerBeingResolved, requiresDecommission, trackContext);
handlerStack.Push(handlerBeingResolved);
if (trackContext)
resolutionStack.Push(resolutionContext);
return resolutionContext;
}
public object GetContextualProperty(object key)
{
if (extendedProperties == null)
return null;
return extendedProperties[key];
}
public bool IsInResolutionContext(IHandler handler)
{
return handlerStack.Contains(handler);
}
public ResolutionContext SelectScopeRoot(Func<IHandler[], IHandler> scopeRootSelector)
{
IHandler[] arg = (from c in resolutionStack
select c.Handler).Reverse().ToArray();
IHandler selected = scopeRootSelector(arg);
if (selected != null) {
ResolutionContext resolutionContext = resolutionStack.SingleOrDefault((ResolutionContext s) => s.Handler == selected);
if (resolutionContext != null)
return resolutionContext;
}
return null;
}
public void SetContextualProperty(object key, object value)
{
if (key == null)
throw new ArgumentNullException("key");
if (extendedProperties == null)
extendedProperties = new Arguments();
extendedProperties[key] = value;
}
public virtual bool CanResolve(CreationContext context, ISubDependencyResolver contextHandlerResolver, ComponentModel model, DependencyModel dependency)
{
if (HasAdditionalArguments) {
if (!CanResolveByKey(dependency))
return CanResolveByType(dependency);
return true;
}
return false;
}
public virtual object Resolve(CreationContext context, ISubDependencyResolver contextHandlerResolver, ComponentModel model, DependencyModel dependency)
{
object obj = null;
if (dependency.DependencyKey != null)
obj = Resolve(dependency, additionalArguments[dependency.DependencyKey]);
return obj ?? Resolve(dependency, additionalArguments[dependency.TargetType]);
}
private bool CanConvertParameter(Type type)
{
if (converter != null)
return converter.CanHandleType(type);
return false;
}
private bool CanResolve(DependencyModel dependency, object inlineArgument)
{
Type targetItemType = dependency.TargetItemType;
if (inlineArgument == null || (object)targetItemType == null)
return false;
if (!TypeExtensions.IsInstanceOfType(targetItemType, inlineArgument))
return CanConvertParameter(targetItemType);
return true;
}
private bool CanResolveByKey(DependencyModel dependency)
{
if (dependency.DependencyKey == null)
return false;
return CanResolve(dependency, additionalArguments[dependency.DependencyKey]);
}
private bool CanResolveByType(DependencyModel dependency)
{
Type targetItemType = dependency.TargetItemType;
if ((object)targetItemType == null)
return false;
return CanResolve(dependency, additionalArguments[targetItemType]);
}
private void ExitResolutionContext(Burden burden, bool trackContext)
{
handlerStack.Pop();
if (trackContext)
resolutionStack.Pop();
if (burden != null && burden.Instance != null && burden.RequiresPolicyRelease && resolutionStack.Count != 0)
resolutionStack.Peek().Burden?.AddChild(burden);
}
private object Resolve(DependencyModel dependency, object inlineArgument)
{
Type targetItemType = dependency.TargetItemType;
if (inlineArgument != null) {
if (TypeExtensions.IsInstanceOfType(targetItemType, inlineArgument))
return inlineArgument;
if (CanConvertParameter(targetItemType))
return converter.PerformConversion(inlineArgument.ToString(), targetItemType);
}
return null;
}
public static CreationContext CreateEmpty()
{
return new CreationContext();
}
public static CreationContext ForDependencyInspection(IHandler handler)
{
CreationContext creationContext = CreateEmpty();
creationContext.isResolving = false;
creationContext.EnterResolutionContext(handler, false);
return creationContext;
}
private static Type[] ExtractGenericArguments(Type typeToExtractGenericArguments)
{
if (typeToExtractGenericArguments.GetTypeInfo().get_IsGenericType())
return typeToExtractGenericArguments.GetTypeInfo().GetGenericArguments();
return Type.EmptyTypes;
}
}
}