diff --git a/Src/Commons.InversionOfControl.DryIoC/BoC.InversionOfControl.DryIoC.csproj b/Src/Commons.InversionOfControl.DryIoC/BoC.InversionOfControl.DryIoC.csproj
index b0fe15a..2d0eaa4 100644
--- a/Src/Commons.InversionOfControl.DryIoC/BoC.InversionOfControl.DryIoC.csproj
+++ b/Src/Commons.InversionOfControl.DryIoC/BoC.InversionOfControl.DryIoC.csproj
@@ -46,7 +46,7 @@
-
+
diff --git a/Src/Commons.InversionOfControl.DryIoC/DryIoCDependencyResolver.cs b/Src/Commons.InversionOfControl.DryIoC/DryIoCDependencyResolver.cs
index 4316bf5..bb3f21d 100644
--- a/Src/Commons.InversionOfControl.DryIoC/DryIoCDependencyResolver.cs
+++ b/Src/Commons.InversionOfControl.DryIoC/DryIoCDependencyResolver.cs
@@ -290,6 +290,8 @@ private IReuse GetReuse(LifetimeScope lifetimeScope)
return Reuse.InWebRequest;
case LifetimeScope.PerThread:
return Reuse.InThread;
+ case LifetimeScope.Singleton:
+ return Reuse.Singleton;
default:
return Reuse.Transient;
}
diff --git a/Src/Commons.InversionOfControl.DryIoC/DryIoc/AsyncExecutionFlowScopeContext.cs b/Src/Commons.InversionOfControl.DryIoC/DryIoc/AsyncExecutionFlowScopeContext.cs
index a65580d..5878a13 100644
--- a/Src/Commons.InversionOfControl.DryIoC/DryIoc/AsyncExecutionFlowScopeContext.cs
+++ b/Src/Commons.InversionOfControl.DryIoC/DryIoc/AsyncExecutionFlowScopeContext.cs
@@ -1,7 +1,7 @@
/*
The MIT License (MIT)
-Copyright (c) 2016 Maksim Volkau
+Copyright (c) 2014-2016 Maksim Volkau
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/Src/Commons.InversionOfControl.DryIoC/DryIoc/Container.cs b/Src/Commons.InversionOfControl.DryIoC/DryIoc/Container.cs
index 3c2d9d2..091da35 100644
--- a/Src/Commons.InversionOfControl.DryIoC/DryIoc/Container.cs
+++ b/Src/Commons.InversionOfControl.DryIoC/DryIoc/Container.cs
@@ -1,7 +1,7 @@
-/*
+/*
The MIT License (MIT)
-Copyright (c) 2016 Maksim Volkau
+Copyright (c) 2013-2016 Maksim Volkau
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -10,7 +10,7 @@
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
-The above copyright notice and this permission notice shall be included AddOrUpdateServiceFactory
+The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
@@ -34,16 +34,17 @@ namespace DryIoc
using System.Threading;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
+ using ImTools;
/// IoC Container. Documentation is available at https://bitbucket.org/dadhi/dryioc.
public sealed partial class Container : IContainer, IScopeAccess
{
/// Creates new container with default rules .
- public Container(): this(Rules.Default, Ref.Of(Registry.Default), new SingletonScope())
+ public Container() : this(Rules.Default, Ref.Of(Registry.Default), new SingletonScope())
{ }
/// Creates new container, optionally providing to modify default container behavior.
- /// (optional) Rules to modify container default resolution behavior.
+ /// (optional) Rules to modify container default resolution behavior.
/// If not specified, then will be used.
/// (optional) Scope context to use for , default is .
public Container(Rules rules = null, IScopeContext scopeContext = null)
@@ -61,13 +62,17 @@ public Container(Func configure, IScopeContext scopeContext = null
public override string ToString()
{
var scope = ((IScopeAccess)this).GetCurrentScope();
- if (scope != null)
- return "container with open scope: " + scope;
- return "container";
+ var scopeStr
+ = scope == null ? "Container"
+ : _scopeContext != null ? "Ambiently scoped container: " + scope
+ : "Scoped container: " + scope;
+ if (IsDisposed)
+ scopeStr = "Disposed (!) " + scopeStr;
+ return scopeStr;
}
/// Shares all of container state except Cache and specifies new rules.
- /// (optional) Configure rules, if not specified then uses Rules from current container.
+ /// (optional) Configure rules, if not specified then uses Rules from current container.
/// (optional) New scope context, if not specified then uses context from current container.
/// New container.
public IContainer With(Func configure = null, IScopeContext scopeContext = null)
@@ -76,7 +81,7 @@ public IContainer With(Func configure = null, IScopeContext scopeC
var rules = configure == null ? Rules : configure(Rules);
scopeContext = scopeContext ?? _scopeContext;
var registryWithoutCache = Ref.Of(_registry.Value.WithoutCache());
- return new Container(rules, registryWithoutCache, _singletonScope, scopeContext, _openedScope, _disposed);
+ return new Container(rules, registryWithoutCache, _singletonScope, scopeContext, _openedScope, _disposed, RootContainer);
}
/// Produces new container which prevents any further registrations.
@@ -86,7 +91,7 @@ public IContainer With(Func configure = null, IScopeContext scopeC
public IContainer WithNoMoreRegistrationAllowed(bool ignoreInsteadOfThrow = false)
{
var readonlyRegistry = Ref.Of(_registry.Value.WithNoMoreRegistrationAllowed(ignoreInsteadOfThrow));
- return new Container(Rules, readonlyRegistry, _singletonScope, _scopeContext, _openedScope, _disposed);
+ return new Container(Rules, readonlyRegistry, _singletonScope, _scopeContext, _openedScope, _disposed, RootContainer);
}
/// Returns new container with all expression, delegate, items cache removed/reset.
@@ -96,7 +101,7 @@ public IContainer WithoutCache()
{
ThrowIfContainerDisposed();
var registryWithoutCache = Ref.Of(_registry.Value.WithoutCache());
- return new Container(Rules, registryWithoutCache, _singletonScope, _scopeContext, _openedScope, _disposed);
+ return new Container(Rules, registryWithoutCache, _singletonScope, _scopeContext, _openedScope, _disposed, RootContainer);
}
/// Creates new container with state shared with original except singletons and cache.
@@ -107,7 +112,7 @@ public IContainer WithoutSingletonsAndCache()
ThrowIfContainerDisposed();
var registryWithoutCache = Ref.Of(_registry.Value.WithoutCache());
var newSingletons = new SingletonScope();
- return new Container(Rules, registryWithoutCache, newSingletons, _scopeContext, _openedScope, _disposed);
+ return new Container(Rules, registryWithoutCache, newSingletons, _scopeContext, _openedScope, _disposed, RootContainer);
}
/// Shares all parts with original container But copies registration, so the new registration
@@ -118,7 +123,7 @@ public IContainer WithRegistrationsCopy(bool preserveCache = false)
{
ThrowIfContainerDisposed();
var newRegistry = preserveCache ? _registry.NewRef() : Ref.Of(_registry.Value.WithoutCache());
- return new Container(Rules, newRegistry, _singletonScope, _scopeContext, _openedScope, _disposed);
+ return new Container(Rules, newRegistry, _singletonScope, _scopeContext, _openedScope, _disposed, RootContainer);
}
/// Returns ambient scope context associated with container.
@@ -129,7 +134,7 @@ public IContainer WithRegistrationsCopy(bool preserveCache = false)
/// In case of previous open scope, new open scope references old one as a parent.
///
/// (optional) Name for opened scope to allow reuse to identify the scope.
- /// (optional) Configure rules, if not specified then uses Rules from current container.
+ /// (optional) Configure rules, if not specified then uses Rules from current container.
/// New container with different current scope.
/// configure = n
nestedOpenedScope.ThrowIf(scope != _openedScope, Error.NotDirectScopeParent, _openedScope, scope));
var rules = configure == null ? Rules : configure(Rules);
- return new Container(rules, _registry, _singletonScope, _scopeContext, nestedOpenedScope, _disposed);
+
+ return new Container(rules, _registry, _singletonScope, _scopeContext, nestedOpenedScope, _disposed, RootContainer ?? this);
}
/// The default name of root scope without ambient context.
public static readonly object NonAmbientRootScopeName = "NonAmbientRootScope";
/// Creates container (facade) that fallbacks to this container for unresolved services.
- /// Facade is the new empty container, with the same rules and scope context as current container.
+ /// Facade is the new empty container, with the same rules and scope context as current container.
/// It could be used for instance to create Test facade over original container with replacing some services with test ones.
/// Singletons from container are not reused by facade - when you resolve singleton directly from parent and then ask for it from child, it will return another object.
/// To achieve that you may use with .
@@ -173,26 +179,45 @@ public IContainer CreateFacade()
return new Container(Rules.WithFallbackContainer(this), _scopeContext);
}
+ /// Clears cache for specified service(s).
+ /// But does not clear instances of already resolved/created singletons and scoped services!
+ /// Target service type.
+ /// (optional) If not specified, clears cache for all .
+ /// (optional) If omitted, the cache will be cleared for all resgitrations of .
+ /// True if target service was found, false - otherwise.
+ public bool ClearCache(Type serviceType, FactoryType? factoryType = null, object serviceKey = null)
+ {
+ if (factoryType != null)
+ return _registry.Value.ClearCache(serviceType, serviceKey, factoryType.Value);
+
+ var registry = _registry.Value;
+
+ var clearedServices = registry.ClearCache(serviceType, serviceKey, FactoryType.Service);
+ var clearWrapper = registry.ClearCache(serviceType, serviceKey, FactoryType.Wrapper);
+ var clearDecorator = registry.ClearCache(serviceType, serviceKey, FactoryType.Decorator);
+
+ return clearedServices || clearWrapper || clearDecorator;
+ }
+
/// Dispose either open scope, or container with singletons, if no scope opened.
public void Dispose()
{
+ if (Interlocked.CompareExchange(ref _disposed, 1, 0) != 0)
+ return;
+
// for container created with OpenScope
- if (_openedScope != null &&
+ if (_openedScope != null &&
!(Rules.ImplicitOpenedRootScope && _openedScope.Parent == null && _scopeContext == null))
{
- if (_openedScope == null)
- return;
-
- _openedScope.Dispose();
-
- if (_scopeContext != null)
- _scopeContext.SetCurrent(scope => scope == _openedScope ? scope.Parent : scope);
+ if (_openedScope != null)
+ {
+ _openedScope.Dispose();
+ if (_scopeContext != null)
+ _scopeContext.SetCurrent(scope => scope == _openedScope ? scope.Parent : scope);
+ }
}
else // whole Container with singletons.
{
- if (Interlocked.CompareExchange(ref _disposed, 1, 0) != 0)
- return;
-
if (_openedScope != null)
_openedScope.Dispose();
@@ -202,14 +227,21 @@ public void Dispose()
_singletonScope.Dispose();
_registry.Swap(Registry.Empty);
- _defaultFactoryDelegateCache = Ref.Of(ImTreeMap.Empty);
+ _defaultFactoryDelegateCache = Ref.Of(ImMap.Empty);
Rules = Rules.Default;
}
}
+ /// Scope containing container singletons.
+ public IScope SingletonScope { get { return _singletonScope; } }
+
+ /// Returns root for scoped container or null for root itself.
+ public readonly Container RootContainer;
+
#region Static state
+ // todo: v3: remove
/// State parameter expression in FactoryDelegate.
public static readonly ParameterExpression StateParamExpr =
Expression.Parameter(typeof(object[]), "state");
@@ -222,42 +254,44 @@ public void Dispose()
public static readonly Expression ResolverExpr =
Expression.Property(ResolverContextParamExpr, "Resolver");
+ /// Resolver parameter expression in FactoryDelegate.
+ public static readonly Expression RootResolverExpr =
+ Expression.Call(typeof(ResolverContext), "RootResolver", ArrayTools.Empty(), ResolverContextParamExpr);
+
+ /// Returns or based on request.
+ public static Expression GetResolverExpr(Request request)
+ {
+ return request.IsSingletonOrDependencyOfSingleton ? RootResolverExpr : ResolverExpr;
+ }
+
+ /// Resolver parameter expression in FactoryDelegate.
+ public static readonly Expression SingletonScopeExpr =
+ Expression.Call(typeof(ResolverContext), "SingletonScope", ArrayTools.Empty(), ResolverContextParamExpr);
+
/// Access to scopes in FactoryDelegate.
public static readonly Expression ScopesExpr =
Expression.Property(ResolverContextParamExpr, "Scopes");
- /// Resolution scope parameter expression in FactoryDelegate.
- public static readonly ParameterExpression ResolutionScopeParamExpr =
- Expression.Parameter(typeof(IScope), "scope");
+ /// Resolver parameter expression in FactoryDelegate.
+ public static readonly Expression RootScopesExpr =
+ Expression.Call(typeof(ResolverContext), "RootScopes", ArrayTools.Empty(), ResolverContextParamExpr);
- /// Creates state item access expression given item index.
- /// State items actually are singleton items. So that method knows about Singleton items structure.
- /// Index of item.
- /// Expression.
- public static Expression GetStateItemExpression(int itemIndex)
+ /// Returns or based on request.
+ public static Expression GetScopesExpr(Request request)
{
- var bucketSize = SingletonScope.BucketSize;
- if (itemIndex < bucketSize)
- return Expression.ArrayIndex(StateParamExpr, Expression.Constant(itemIndex, typeof(int)));
-
- // Result: ((object[][])_singletonItems[0])[itemIndex/bucketSize][itemIndex%bucketSize];
- var bucketsExpr = Expression.Convert(
- Expression.ArrayIndex(StateParamExpr, Expression.Constant(0, typeof(int))),
- typeof(object[][]));
-
- var bucketExpr = Expression.ArrayIndex(bucketsExpr,
- Expression.Constant(itemIndex / bucketSize - 1, typeof(int)));
-
- return Expression.ArrayIndex(bucketExpr,
- Expression.Constant(itemIndex % bucketSize, typeof(int)));
+ return request.IsSingletonOrDependencyOfSingleton ? RootScopesExpr : ScopesExpr;
}
+ /// Resolution scope parameter expression in FactoryDelegate.
+ public static readonly ParameterExpression ResolutionScopeParamExpr =
+ Expression.Parameter(typeof(IScope), "scope");
+
internal static Expression GetResolutionScopeExpression(Request request)
{
if (request.Scope != null)
return ResolutionScopeParamExpr;
- // the only situation when we could here is: where are in first resolution call (in the root)
+ // the only situation when we could be here: the root resolution call
// and scope was not created on the boundary of Resolve call.
var parent = request.Enumerate().Last();
@@ -267,21 +301,21 @@ internal static Expression GetResolutionScopeExpression(Request request)
var parentServiceKeyExpr = container.GetOrAddStateItemExpression(parent.ServiceKey, typeof(object));
// if assign in expression is supported then use it.
+ var scopesExpr = GetScopesExpr(request);
if (_expressionAssignMethod != null)
{
- var getOrNewScopeExpr = Expression.Call(ScopesExpr, "GetOrNewResolutionScope",
+ var getOrNewScopeExpr = Expression.Call(scopesExpr, "GetOrNewResolutionScope",
ArrayTools.Empty(), ResolutionScopeParamExpr, parentServiceTypeExpr, parentServiceKeyExpr);
var parameters = new object[] { ResolutionScopeParamExpr, getOrNewScopeExpr };
- return (Expression)_expressionAssignMethod.Invoke(null, parameters);
+ return (Expression)_expressionAssignMethod.Value.Invoke(null, parameters);
}
- return Expression.Call(ScopesExpr, "GetOrCreateResolutionScope",
+ return Expression.Call(scopesExpr, "GetOrCreateResolutionScope",
ArrayTools.Empty(), ResolutionScopeParamExpr, parentServiceTypeExpr, parentServiceKeyExpr);
}
- private static readonly MethodInfo _expressionAssignMethod =
- typeof(Expression).GetMethodOrNull("Assign", typeof(Expression), typeof(Expression));
-
+ private static readonly Lazy _expressionAssignMethod = new Lazy(() =>
+ typeof(Expression).GetMethodOrNull("Assign", typeof(Expression), typeof(Expression)));
#endregion
@@ -301,10 +335,10 @@ public IEnumerable GetServiceRegistrations()
/// Type of service to resolve later.
/// (optional) Service key of any type with and
/// implemented.
- /// (optional) Says how to handle existing registration with the same
+ /// (optional) Says how to handle existing registration with the same
/// and .
/// Confirms that service and implementation types are statically checked by compiler.
- /// True if factory was added to registry, false otherwise.
+ /// True if factory was added to registry, false otherwise.
/// False may be in case of setting and already existing factory.
public void Register(Factory factory, Type serviceType, object serviceKey, IfAlreadyRegistered ifAlreadyRegistered, bool isStaticallyChecked)
{
@@ -314,8 +348,8 @@ public void Register(Factory factory, Type serviceType, object serviceKey, IfAlr
if (ifAlreadyRegistered == IfAlreadyRegistered.AppendNotKeyed)
ifAlreadyRegistered = Rules.DefaultIfAlreadyRegistered;
- // Improves performance a bit by attempt to swapping registry while it is still unchanged,
- // if attempt fails then fallback to normal Swap with retry.
+ // Improves performance a bit by attempt to swapping registry while it is still unchanged,
+ // if attempt fails then fallback to normal Swap with retry.
var registry = _registry.Value;
if (!_registry.TrySwapIfStillCurrent(registry, registry.Register(factory, serviceType, ifAlreadyRegistered, serviceKey)))
_registry.Swap(r => r.Register(factory, serviceType, ifAlreadyRegistered, serviceKey));
@@ -330,18 +364,23 @@ private void ThrowIfInvalidRegistration(Factory factory, Type serviceType, objec
serviceType.ThrowIfNull();
factory.ThrowIfNull();
}
-
+
var setup = factory.Setup;
if (setup.FactoryType != FactoryType.Wrapper)
{
- if (factory.Reuse == null && !factory.Setup.UseParentReuse &&
- (factory.ImplementationType ?? serviceType).IsAssignableTo(typeof(IDisposable)) &&
- setup.AllowDisposableTransient == false && Rules.ThrowOnRegisteringDisposableTransient)
- Throw.It(Error.RegisteredDisposableTransientWontBeDisposedByContainer, serviceType,
- serviceKey ?? "{no key}", this);
+ if ((factory.Reuse ?? Rules.DefaultReuseInsteadOfTransient) == Reuse.Transient &&
+ !factory.Setup.UseParentReuse &&
+ !(factory.FactoryType == FactoryType.Decorator && ((Setup.DecoratorSetup)factory.Setup).UseDecorateeReuse) &&
+ (factory.ImplementationType ?? serviceType).IsAssignableTo(typeof(IDisposable)) &&
+ !setup.AllowDisposableTransient && Rules.ThrowOnRegisteringDisposableTransient)
+ {
+ Throw.It(Error.RegisteredDisposableTransientWontBeDisposedByContainer,
+ serviceType, serviceKey ?? "{no key}", factory);
+ }
}
- else if (serviceType.IsGeneric() &&
- !((Setup.WrapperSetup)setup).AlwaysWrapsRequiredServiceType)
+ else if (serviceType.IsGeneric() &&
+ !((Setup.WrapperSetup)setup).AlwaysWrapsRequiredServiceType &&
+ ((Setup.WrapperSetup)setup).Unwrap == null)
{
var typeArgCount = serviceType.GetGenericParamsAndArgs().Length;
var typeArgIndex = ((Setup.WrapperSetup)setup).WrappedServiceTypeArgIndex;
@@ -360,9 +399,11 @@ private void ThrowIfInvalidRegistration(Factory factory, Type serviceType, objec
var ctors = reflectionFactory.ImplementationType.GetPublicInstanceConstructors().ToArrayOrSelf();
if (ctors.Length != 1)
Throw.It(Error.NoDefinedMethodToSelectFromMultipleConstructors, reflectionFactory.ImplementationType, ctors);
+ else
+ reflectionFactory.SetKnownSingleCtor(ctors[0]);
}
- if (!isStaticallyChecked &&
+ if (!isStaticallyChecked &&
reflectionFactory.ImplementationType != null)
{
var implType = reflectionFactory.ImplementationType;
@@ -403,8 +444,10 @@ private void ThrowIfInvalidRegistration(Factory factory, Type serviceType, objec
implementedTypes.Where(t => t.GetGenericDefinitionOrNull() == serviceType));
}
else if (implType.IsGeneric() && serviceType.IsOpenGeneric())
+ {
Throw.It(Error.RegisteringNotAGenericTypedefServiceType,
serviceType, serviceType.GetGenericDefinitionOrNull());
+ }
else
Throw.It(Error.RegisteringOpenGenericImplWithNonGenericService, implType, serviceType);
}
@@ -424,10 +467,11 @@ private void ThrowIfInvalidRegistration(Factory factory, Type serviceType, objec
public bool IsRegistered(Type serviceType, object serviceKey, FactoryType factoryType, Func condition)
{
ThrowIfContainerDisposed();
- return _registry.Value.IsRegistered(serviceType.ThrowIfNull(), serviceKey, factoryType, condition);
+ var factories = _registry.Value.GetRegisteredFactories(serviceType.ThrowIfNull(), serviceKey, factoryType, condition);
+ return !factories.IsNullOrEmpty();
}
- /// Removes specified factory from registry.
+ /// Removes specified factory from registry.
/// Factory is removed only from registry, if there is relevant cache, it will be kept.
/// Use to remove all the cache.
/// Service type to look for.
@@ -443,6 +487,31 @@ public void Unregister(Type serviceType, object serviceKey, FactoryType factoryT
#endregion
+ #region Direct Container Resolve methods to avoid interface dispatch
+
+ /// Returns instance of type.
+ /// The type of the requested service.
+ /// The requested service instance.
+ [MethodImpl(MethodImplHints.AggressingInlining)]
+ public TService Resolve()
+ {
+ return (TService)Resolve(typeof(TService));
+ }
+
+ /// Resolves default (non-keyed) service from container and returns created service object.
+ /// Service type to search and to return.
+ /// The requested service instance.
+ [MethodImpl(MethodImplHints.AggressingInlining)]
+ public object Resolve(Type serviceType)
+ {
+ var factoryDelegate = _defaultFactoryDelegateCache.Value.GetValueOrDefault(serviceType);
+ return factoryDelegate != null
+ ? factoryDelegate(null, _thisContainerWeakRef, null)
+ : ResolveAndCacheDefaultDelegate(serviceType, false, null);
+ }
+
+ #endregion
+
#region IResolver
[MethodImpl(MethodImplHints.AggressingInlining)]
@@ -450,26 +519,28 @@ object IResolver.Resolve(Type serviceType, bool ifUnresolvedReturnDefault)
{
var factoryDelegate = _defaultFactoryDelegateCache.Value.GetValueOrDefault(serviceType);
return factoryDelegate != null
- ? factoryDelegate(_singletonItems, _containerWeakRef, null)
+ ? factoryDelegate(null, _thisContainerWeakRef, null)
: ResolveAndCacheDefaultDelegate(serviceType, ifUnresolvedReturnDefault, null);
}
- object IResolver.Resolve(Type serviceType, object serviceKey, bool ifUnresolvedReturnDefault, Type requiredServiceType, RequestInfo preResolveParent, IScope scope)
+ object IResolver.Resolve(Type serviceType, object serviceKey, bool ifUnresolvedReturnDefault, Type requiredServiceType, RequestInfo preResolveParent,
+ IScope scope)
{
preResolveParent = preResolveParent ?? RequestInfo.Empty;
- object cacheEntryKey = serviceType;
- if (serviceKey != null)
- cacheEntryKey = new KV(cacheEntryKey, serviceKey);
+ var cacheEntryKey = serviceKey == null
+ ? (object)serviceType
+ : new KV(serviceType, serviceKey);
object cacheContextKey = requiredServiceType;
if (!preResolveParent.IsEmpty)
- cacheContextKey = cacheContextKey == null ? (object)preResolveParent
- : new KV(cacheContextKey, preResolveParent);
+ cacheContextKey = cacheContextKey == null
+ ? (object)preResolveParent
+ : KV.Of(cacheContextKey, preResolveParent);
// Check cache first:
- var registryValue = _registry.Value;
- var cacheRef = registryValue.KeyedFactoryDelegateCache;
+ var registry = _registry.Value;
+ var cacheRef = registry.KeyedFactoryDelegateCache;
var cacheEntry = cacheRef.Value.GetValueOrDefault(cacheEntryKey);
if (cacheEntry != null)
{
@@ -478,32 +549,44 @@ object IResolver.Resolve(Type serviceType, object serviceKey, bool ifUnresolvedR
: (cacheEntry.Value ?? ImTreeMap.Empty).GetValueOrDefault(cacheContextKey);
if (cachedFactoryDelegate != null)
- return cachedFactoryDelegate(_singletonItems, _containerWeakRef, scope);
+ return cachedFactoryDelegate(null, _thisContainerWeakRef, scope);
}
// Cache is missed, so get the factory and put it into cache:
ThrowIfContainerDisposed();
var ifUnresolved = ifUnresolvedReturnDefault ? IfUnresolved.ReturnDefault : IfUnresolved.Throw;
- var request = _emptyRequest.Push(serviceType, serviceKey, ifUnresolved, requiredServiceType, scope, preResolveParent);
+ var request = Request.Create(this, serviceType, serviceKey, ifUnresolved, requiredServiceType, scope,
+ preResolveParent);
+
var factory = ((IContainer)this).ResolveFactory(request);
- var factoryDelegate = factory == null ? null : factory.GetDelegateOrDefault(request);
+
+ // Hack: may mutate (set) not null request service key.
+ if (serviceKey == null && request.ServiceKey != null)
+ cacheEntryKey = new KV(serviceType, request.ServiceKey);
+
+ if (factory == null)
+ return null;
+
+ var factoryDelegate = factory.GetDelegateOrDefault(request);
if (factoryDelegate == null)
return null;
- var service = factoryDelegate(_singletonItems, _containerWeakRef, scope);
+ var service = factoryDelegate(null, _thisContainerWeakRef, scope);
- if (registryValue.Services.IsEmpty)
+ if (registry.Services.IsEmpty)
return service;
- // Cache factory Only after we successfully got the service from it.
- var cachedContextFactories = (cacheEntry == null ? null : cacheEntry.Value) ?? ImTreeMap.Empty;
+ // Cache factory only when we successfully called the factory delegate, to prevent failing delegates to be cached.
+ // Additionally disable caching when:
+ // no services registered, so the service probably empty collection wrapper or alike.
+ var cachedContextFactories =
+ (cacheEntry == null ? null : cacheEntry.Value) ??
+ ImTreeMap.Empty;
+
if (cacheContextKey == null)
- cacheEntry = new KV>(
- factoryDelegate,
- cachedContextFactories);
+ cacheEntry = KV.Of(factoryDelegate, cachedContextFactories);
else
- cacheEntry = new KV>(
- cacheEntry == null ? null : cacheEntry.Key,
+ cacheEntry = KV.Of(cacheEntry == null ? null : cacheEntry.Key,
cachedContextFactories.AddOrUpdate(cacheContextKey, factoryDelegate));
var cacheVal = cacheRef.Value;
@@ -518,8 +601,8 @@ private object ResolveAndCacheDefaultDelegate(Type serviceType, bool ifUnresolve
ThrowIfContainerDisposed();
var ifUnresolved = ifUnresolvedReturnDefault ? IfUnresolved.ReturnDefault : IfUnresolved.Throw;
- var request = _emptyRequest.Push(serviceType, ifUnresolved: ifUnresolved, scope: scope);
- var factory = ((IContainer)this).ResolveFactory(request); // NOTE may mutate request
+ var request = Request.Create(this, serviceType, ifUnresolved: ifUnresolved, scope: scope);
+ var factory = ((IContainer)this).ResolveFactory(request); // HACK: may mutate request, but it should be safe
// The situation is possible for multiple default services registered.
if (request.ServiceKey != null)
@@ -530,10 +613,10 @@ private object ResolveAndCacheDefaultDelegate(Type serviceType, bool ifUnresolve
return null;
var registryValue = _registry.Value;
- var service = factoryDelegate(_singletonItems, _containerWeakRef, scope);
+ var service = factoryDelegate(null, _thisContainerWeakRef, scope);
- // Caching disabled only if:
- // - if no services were registered, so the service probably empty collection wrapper or alike.
+ // Additionally disable caching when:
+ // no services registered, so the service probably empty collection wrapper or alike.
if (!registryValue.Services.IsEmpty)
{
var cacheRef = registryValue.DefaultFactoryDelegateCache;
@@ -547,14 +630,20 @@ private object ResolveAndCacheDefaultDelegate(Type serviceType, bool ifUnresolve
// todo: v3: remove unused composite key and required type parameters
IEnumerable IResolver.ResolveMany(
- Type serviceType, object serviceKey, Type requiredServiceType,
- object compositeParentKey, Type compositeParentRequiredType,
+ Type serviceType, object serviceKey, Type requiredServiceType,
+ object compositeParentKey, Type compositeParentRequiredType,
RequestInfo preResolveParent, IScope scope)
{
- preResolveParent = preResolveParent ?? RequestInfo.Empty;
+ var requiredItemType = requiredServiceType ?? serviceType;
+
+ // Emulating the collection parent so that collection related rules and conditions were applied
+ // the same way as if resolving IEnumerable
+ if (preResolveParent == null || preResolveParent.IsEmpty)
+ preResolveParent = RequestInfo.Empty.Push(
+ typeof(IEnumerable), requiredItemType, serviceKey, IfUnresolved.Throw,
+ 0, FactoryType.Wrapper, implementationType: null, reuse: null, flags: RequestFlags.IsServiceCollection);
var container = (IContainer)this;
- var requiredItemType = requiredServiceType ?? serviceType;
var items = container.GetAllServiceFactories(requiredItemType);
IEnumerable openGenericItems = null;
@@ -565,7 +654,7 @@ private object ResolveAndCacheDefaultDelegate(Type serviceType, bool ifUnresolve
.Select(x => new ServiceRegistrationInfo(x.Value, requiredItemOpenGenericType, x.Key));
}
- // Append registered generic types with compatible variance,
+ // Append registered generic types with compatible variance,
// e.g. for IHandler - IHandler is compatible with IHandler if B : A.
IEnumerable variantGenericItems = null;
if (requiredItemType.IsGeneric() && container.Rules.VariantGenericTypesInResolvedCollection)
@@ -595,15 +684,17 @@ private object ResolveAndCacheDefaultDelegate(Type serviceType, bool ifUnresolve
variantGenericItems = variantGenericItems.Where(it => it.Factory.Setup.MatchesMetadata(metadataKey, metadata));
}
- // exclude composite parent from items
- var parent = preResolveParent.FactoryType != FactoryType.Wrapper
- ? preResolveParent : preResolveParent.Parent;
+ // Exclude composite parent service from items, skip decorators
+ var parent = preResolveParent;
+ if (parent.FactoryType != FactoryType.Service)
+ parent = parent.Enumerate().FirstOrDefault(p => p.FactoryType == FactoryType.Service) ?? RequestInfo.Empty;
+
if (!parent.IsEmpty && parent.GetActualServiceType() == requiredItemType)
{
items = items.Where(x => x.Value.FactoryID != parent.FactoryID);
if (openGenericItems != null)
- openGenericItems = openGenericItems.Where(x =>
+ openGenericItems = openGenericItems.Where(x =>
!x.Factory.FactoryGenerator.GeneratedFactories.Enumerate().Any(f =>
f.Value.FactoryID == parent.FactoryID &&
f.Key.Key == parent.ServiceType && f.Key.Value == parent.ServiceKey));
@@ -612,10 +703,9 @@ private object ResolveAndCacheDefaultDelegate(Type serviceType, bool ifUnresolve
variantGenericItems = variantGenericItems.Where(x => x.Factory.FactoryID != parent.FactoryID);
}
- IResolver resolver = this;
foreach (var item in items)
{
- var service = resolver.Resolve(serviceType, item.Key,
+ var service = container.Resolve(serviceType, item.Key,
true, requiredServiceType, preResolveParent, scope);
if (service != null) // skip unresolved items
yield return service;
@@ -625,7 +715,7 @@ private object ResolveAndCacheDefaultDelegate(Type serviceType, bool ifUnresolve
foreach (var item in openGenericItems)
{
var serviceKeyWithOpenGenericRequiredType = new[] { item.ServiceType, item.OptionalServiceKey };
- var service = resolver.Resolve(serviceType, serviceKeyWithOpenGenericRequiredType,
+ var service = container.Resolve(serviceType, serviceKeyWithOpenGenericRequiredType,
true, requiredItemType, preResolveParent, scope);
if (service != null) // skip unresolved items
yield return service;
@@ -634,7 +724,7 @@ private object ResolveAndCacheDefaultDelegate(Type serviceType, bool ifUnresolve
if (variantGenericItems != null)
foreach (var item in variantGenericItems)
{
- var service = resolver.Resolve(serviceType, item.OptionalServiceKey,
+ var service = container.Resolve(serviceType, item.OptionalServiceKey,
true, item.ServiceType, preResolveParent, scope);
if (service != null) // skip unresolved items
yield return service;
@@ -663,20 +753,14 @@ private void ThrowIfContainerDisposed()
#endregion
- #region IResolverContext
-
- /// Scope containing container singletons.
- IScope IScopeAccess.SingletonScope
- {
- get { return _singletonScope; }
- }
+ #region IScopeAccess
IScope IScopeAccess.GetCurrentScope()
{
return ((IScopeAccess)this).GetCurrentNamedScope(null, false);
}
- /// Gets current scope matching the .
+ /// Gets current scope matching the .
/// If name is null then current scope is returned, or if there is no current scope then exception thrown.
/// May be null Says to throw if no scope found.
/// Found scope or throws exception.
@@ -687,7 +771,7 @@ IScope IScopeAccess.GetCurrentNamedScope(object name, bool throwIfNotFound)
return currentScope == null
? (throwIfNotFound ? Throw.For(Error.NoCurrentScope) : null)
: GetMatchingScopeOrDefault(currentScope, name)
- ?? (throwIfNotFound ? Throw.For(Error.NoMatchedScopeFound, name) : null);
+ ?? (throwIfNotFound ? Throw.For(Error.NoMatchedScopeFound, currentScope, name) : null);
}
private static IScope GetMatchingScopeOrDefault(IScope scope, object name)
@@ -701,7 +785,7 @@ private static IScope GetMatchingScopeOrDefault(IScope scope, object name)
// note: The method required for .NET 3.5 which does not have Expression.Assign, so the need for "ref" parameter (BTW "ref" is not supported in XAMARIN)
/// Check if scope is not null, then just returns it, otherwise will create and return it.
/// May be null scope.
- /// Marking scope with resolved service type.
+ /// Marking scope with resolved service type.
/// Marking scope with resolved service key.
/// Input ensuring it is not null.
IScope IScopeAccess.GetOrCreateResolutionScope(ref IScope scope, Type serviceType, object serviceKey)
@@ -711,7 +795,7 @@ IScope IScopeAccess.GetOrCreateResolutionScope(ref IScope scope, Type serviceTyp
/// Check if scope is not null, then just returns it, otherwise will create and return it.
/// May be null scope.
- /// Marking scope with resolved service type.
+ /// Marking scope with resolved service type.
/// Marking scope with resolved service key.
/// Input ensuring it is not null.
public IScope GetOrNewResolutionScope(IScope scope, Type serviceType, object serviceKey)
@@ -719,9 +803,9 @@ public IScope GetOrNewResolutionScope(IScope scope, Type serviceType, object ser
return scope ?? new Scope(null, new KV(serviceType, serviceKey));
}
- /// If both and are null,
+ /// If both and are null,
/// then returns input .
- /// Otherwise searches scope hierarchy to find first scope with: Type assignable and
+ /// Otherwise searches scope hierarchy to find first scope with: Type assignable and
/// Key equal to .
/// Scope to start matching with Type and Key specified.
/// Type to match. Key to match.
@@ -731,12 +815,12 @@ public IScope GetOrNewResolutionScope(IScope scope, Type serviceType, object ser
IScope IScopeAccess.GetMatchingResolutionScope(IScope scope, Type assignableFromServiceType, object serviceKey,
bool outermost, bool throwIfNotFound)
{
- return GetMatchingScopeOrDefault(scope, assignableFromServiceType, serviceKey, outermost)
+ return FindMatchingResolutionScope(scope, assignableFromServiceType, serviceKey, outermost)
?? (!throwIfNotFound ? null
- : Throw.For(Error.NoMatchedScopeFound, new KV(assignableFromServiceType, serviceKey)));
+ : Throw.For(Error.NoMatchedScopeFound, scope, new KV(assignableFromServiceType, serviceKey)));
}
- private static IScope GetMatchingScopeOrDefault(IScope scope, Type assignableFromServiceType, object serviceKey,
+ private static IScope FindMatchingResolutionScope(IScope scope, Type assignableFromServiceType, object serviceKey,
bool outermost)
{
if (assignableFromServiceType == null && serviceKey == null)
@@ -746,8 +830,11 @@ public IScope GetOrNewResolutionScope(IScope scope, Type serviceType, object ser
while (scope != null)
{
var name = scope.Name as KV;
- if (name != null &&
- (assignableFromServiceType == null || name.Key.IsAssignableTo(assignableFromServiceType)) &&
+ if (name != null && (
+ assignableFromServiceType == null ||
+ name.Key.IsAssignableTo(assignableFromServiceType) ||
+ assignableFromServiceType.IsOpenGeneric() &&
+ name.Key.GetGenericDefinitionOrNull().IsAssignableTo(assignableFromServiceType)) &&
(serviceKey == null || serviceKey.Equals(name.Value)))
{
matchedScope = scope;
@@ -770,19 +857,21 @@ public IScope GetOrNewResolutionScope(IScope scope, Type serviceType, object ser
/// Indicates that container is disposed.
public bool IsDisposed
{
- get { return _disposed == 1; }
+ get { return _disposed == 1 || _singletonScope.IsDisposed; }
}
- /// Empty request bound to container. All other requests are created by pushing to empty request.
+ // todo: v3: remove
+ /// Obsolete: replaced with /.
Request IContainer.EmptyRequest
{
- get { return _emptyRequest; }
+ get { return Request.CreateEmpty(this); }
}
+ // todo: v3: Rename to ResolverContext
/// Self weak reference, with readable message when container is GCed/Disposed.
ContainerWeakRef IContainer.ContainerWeakRef
{
- get { return _containerWeakRef; }
+ get { return _thisContainerWeakRef; }
}
Factory IContainer.ResolveFactory(Request request)
@@ -801,7 +890,7 @@ Factory IContainer.ResolveFactory(Request request)
}
if (factory != null && factory.FactoryGenerator != null)
- factory = factory.FactoryGenerator.GetGeneratedFactoryOrDefault(request);
+ factory = factory.FactoryGenerator.GetGeneratedFactory(request);
if (factory == null && request.IfUnresolved == IfUnresolved.Throw)
ThrowUnableToResolve(request);
@@ -868,9 +957,19 @@ Factory IContainer.GetServiceFactoryOrDefault(Request request)
{
var openGenericEntry = serviceFactories.GetValueOrDefault(serviceType.GetGenericTypeDefinition());
if (openGenericEntry != null)
+ factories = factories.Concat(GetRegistryEntryKeyFactoryPairs(openGenericEntry));
+ }
+
+ var dynamicRegistrationProviders = Rules.DynamicRegistrationProviders;
+ if (!dynamicRegistrationProviders.IsNullOrEmpty())
+ {
+ for (var i = 0; i < dynamicRegistrationProviders.Length; i++)
{
- var openGenericFactories = GetRegistryEntryKeyFactoryPairs(openGenericEntry).ToArray();
- factories = factories.Concat(openGenericFactories);
+ var provider = dynamicRegistrationProviders[i];
+ var dynamicFactories = provider(serviceType, null, FactoryType.Service);
+ // todo: Do I need to filter for FactoryType.Service?
+ if (dynamicFactories != null)
+ factories = factories.Concat(dynamicFactories);
}
}
@@ -888,17 +987,27 @@ Expression IContainer.GetDecoratorExpressionOrDefault(Request request)
{
// return early if no decorators registered and no fallback containers to provide them
if (_registry.Value.Decorators.IsEmpty &&
- request.Container.Rules.FallbackContainers.IsNullOrEmpty())
+ request.Rules.FallbackContainers.IsNullOrEmpty())
return null;
- var serviceType = request.ServiceType;
- var container = request.Container;
+ var arrayElementType = request.ServiceType.GetArrayElementTypeOrNull();
+ if (arrayElementType != null)
+ request = request.WithChangedServiceInfo(info => info
+ .With(typeof(IEnumerable<>).MakeGenericType(arrayElementType)));
// Define the list of ids for the already applied decorators
int[] appliedDecoratorIDs = null;
- // Get decorators for the service type
+ var container = request.Container;
+
+ var serviceType = request.ServiceType;
var decorators = container.GetDecoratorFactoriesOrDefault(serviceType);
+
+ // Combine with required service type if different from service type
+ var requiredServiceType = request.GetActualServiceType();
+ if (requiredServiceType != serviceType)
+ decorators = decorators.Append(container.GetDecoratorFactoriesOrDefault(requiredServiceType));
+
if (!decorators.IsNullOrEmpty())
{
appliedDecoratorIDs = GetAppliedDecoratorIDs(request);
@@ -909,16 +1018,25 @@ Expression IContainer.GetDecoratorExpressionOrDefault(Request request)
// Append open-generic decorators
var genericDecorators = ArrayTools.Empty();
- var openGenericServiceType = serviceType.GetGenericDefinitionOrNull();
- if (openGenericServiceType != null)
- genericDecorators = container.GetDecoratorFactoriesOrDefault(openGenericServiceType);
+ var openGenServiceType = serviceType.GetGenericDefinitionOrNull();
+ if (openGenServiceType != null)
+ genericDecorators = container.GetDecoratorFactoriesOrDefault(openGenServiceType);
+
+ // Combine with open-generic required type if they are different from service type
+ if (requiredServiceType != serviceType)
+ {
+ var openGenRequiredType = requiredServiceType.GetGenericDefinitionOrNull();
+ if (openGenRequiredType != null && openGenRequiredType != openGenServiceType)
+ genericDecorators = genericDecorators.Append(container.GetDecoratorFactoriesOrDefault(openGenRequiredType));
+ }
// Append generic type argument decorators, registered as Object
// Note: the condition for type args should be checked before generating the closed generic version
var typeArgDecorators = container.GetDecoratorFactoriesOrDefault(typeof(object));
if (!typeArgDecorators.IsNullOrEmpty())
- genericDecorators = genericDecorators.Append(
- typeArgDecorators.Where(f => f.CheckCondition(request)).ToArrayOrSelf());
+ genericDecorators = genericDecorators.Append(typeArgDecorators
+ .Where(d => d.CheckCondition(request))
+ .ToArrayOrSelf());
// Filter out already applied generic decorators
// And combine with rest of decorators
@@ -927,7 +1045,7 @@ Expression IContainer.GetDecoratorExpressionOrDefault(Request request)
appliedDecoratorIDs = appliedDecoratorIDs ?? GetAppliedDecoratorIDs(request);
if (!appliedDecoratorIDs.IsNullOrEmpty())
genericDecorators = genericDecorators
- .Where(d => d.FactoryGenerator == null
+ .Where(d => d.FactoryGenerator == null
? appliedDecoratorIDs.IndexOf(d.FactoryID) == -1
: d.FactoryGenerator.GeneratedFactories.Enumerate()
.All(f => appliedDecoratorIDs.IndexOf(f.Value.FactoryID) == -1))
@@ -937,15 +1055,15 @@ Expression IContainer.GetDecoratorExpressionOrDefault(Request request)
if (!genericDecorators.IsNullOrEmpty())
{
genericDecorators = genericDecorators
- .Select(d => d.FactoryGenerator == null ? d
- : d.FactoryGenerator.GetGeneratedFactoryOrDefault(request))
+ .Select(d => d.FactoryGenerator == null ? d
+ : d.FactoryGenerator.GetGeneratedFactory(request))
.Where(d => d != null)
.ToArrayOrSelf();
decorators = decorators.Append(genericDecorators);
}
}
- // Filter out the recursive decorators:
+ // Filter out the recursive decorators:
// the decorator with the same which was applied before up to the root
if (!decorators.IsNullOrEmpty())
{
@@ -980,13 +1098,24 @@ Expression IContainer.GetDecoratorExpressionOrDefault(Request request)
.FirstOrDefault(d => d.CheckCondition(request));
}
- return decorator == null ? null : decorator.GetExpressionOrDefault(request);
+ if (decorator == null)
+ return null;
+
+ var decoratorExpr = decorator.GetExpressionOrDefault(request);
+ if (decoratorExpr == null)
+ return null;
+
+ // decorator of arrays should be converted back from IEnumerable to array.
+ if (arrayElementType != null)
+ decoratorExpr = Expression.Call(typeof(Enumerable), "ToArray", new[] { arrayElementType }, decoratorExpr);
+
+ return decoratorExpr;
}
private static int[] GetAppliedDecoratorIDs(Request request)
{
var parent = request.ParentOrWrapper;
- return parent.IsEmpty
+ return parent.IsEmpty
? ArrayTools.Empty()
: parent.Enumerate()
.TakeWhile(p => p.FactoryType != FactoryType.Service) // until the another service
@@ -997,7 +1126,7 @@ private static int[] GetAppliedDecoratorIDs(Request request)
Factory IContainer.GetWrapperFactoryOrDefault(Type serviceType)
{
- return _registry.Value.GetWrapperOrDefault(serviceType);
+ return _registry.Value.Wrappers.GetValueOrDefault(serviceType.GetGenericDefinitionOrNull() ?? serviceType);
}
Factory[] IContainer.GetDecoratorFactoriesOrDefault(Type serviceType)
@@ -1047,10 +1176,10 @@ Type IContainer.GetWrappedType(Type serviceType, Type requiredServiceType)
/// For given instance resolves and sets properties and fields.
/// Service instance with properties to resolve and initialize.
/// (optional) Function to select properties and fields, overrides all other rules if specified.
- /// If not specified then method will use container ,
+ /// If not specified then method will use container ,
/// or if not specified method fallbacks to .
/// Instance with assigned properties and fields.
- /// Different Rules could be combined together using method.
+ /// Different Rules could be combined together using method.
public object InjectPropertiesAndFields(object instance, PropertiesAndFieldsSelector propertiesAndFields)
{
propertiesAndFields = propertiesAndFields
@@ -1059,9 +1188,11 @@ public object InjectPropertiesAndFields(object instance, PropertiesAndFieldsSele
var instanceType = instance.ThrowIfNull().GetType();
- var resolver = (IResolver)this;
- var request = _emptyRequest.Push(instanceType).WithResolvedFactory(new InstanceFactory(instance, null, Setup.Default));
+ var request = Request.Create(this, instanceType)
+ .WithResolvedFactory(new UsedInstanceFactory(instanceType));
+
var requestInfo = request.RequestInfo;
+ var resolver = (IResolver)this;
foreach (var serviceInfo in propertiesAndFields(request))
if (serviceInfo != null)
@@ -1073,7 +1204,8 @@ public object InjectPropertiesAndFields(object instance, PropertiesAndFieldsSele
details.ServiceKey,
details.IfUnresolved == IfUnresolved.ReturnDefault,
details.RequiredServiceType,
- preResolveParent: requestInfo, scope: null);
+ preResolveParent: requestInfo,
+ scope: null);
if (value != null)
serviceInfo.SetValue(instance, value);
@@ -1104,31 +1236,42 @@ public Expression GetCachedFactoryExpressionOrDefault(int factoryID)
return _registry.Value.FactoryExpressionCache.Value.GetValueOrDefault(factoryID) as Expression;
}
+ // todo: v3: remove
/// State item objects which may include: singleton instances for fast access, reuses, reuse wrappers, factory delegates, etc.
public object[] ResolutionStateCache
{
- get { return _singletonItems; }
- }
-
- /// Adds item if it is not already added to singleton state, returns added or existing item index.
- /// Item to find in existing items with or add if not found.
- /// Index of found or added item.
- public int GetOrAddStateItem(object item)
- {
- return _singletonScope.GetOrAdd(item);
+ get { return null; }
}
- /// If possible wraps added item in (possible for primitive type, Type, strings),
- /// otherwise invokes and wraps access to added item (by returned index) into expression: state => state.Get(index) .
- /// Item to wrap or to add. (optional) Specific type of item, otherwise item .
- /// (optional) Enable filtering of stateful items.
+ /// Converts known items into custom expression or wraps in .
+ /// Item to convert.
+ /// (optional) Type of item, otherwise item .
+ /// (optional) Throws for non-primitive and not-recognized items,
+ /// identifying that result expression require run-time state. For compiled expression it means closure in lambda delegate.
/// Returns constant or state access expression for added items.
public Expression GetOrAddStateItemExpression(object item, Type itemType = null, bool throwIfStateRequired = false)
{
- itemType = itemType ?? (item == null ? typeof(object) : item.GetType());
- var primitiveExpr = GetPrimitiveOrArrayExprOrDefault(item, itemType);
- if (primitiveExpr != null)
- return primitiveExpr;
+ if (item == null)
+ return itemType != null
+ ? Expression.Constant(null, itemType)
+ : Expression.Constant(null);
+
+ if (item is DefaultKey)
+ return Expression.Call(typeof(DefaultKey), "Of", ArrayTools.Empty(),
+ Expression.Constant(((DefaultKey)item).RegistrationOrder));
+
+ itemType = itemType ?? item.GetType();
+ if (itemType.IsPrimitive() ||
+ itemType.IsAssignableTo(typeof(Type)))
+ return Expression.Constant(item, itemType);
+
+ if (itemType.IsArray)
+ {
+ var elemType = itemType.GetElementType().ThrowIfNull();
+ var elems = ((IEnumerable)item).Cast().Select(it => GetOrAddStateItemExpression(it, null, throwIfStateRequired));
+ var elemExprs = Expression.NewArrayInit(elemType, elems);
+ return elemExprs;
+ }
if (Rules.ItemToExpressionConverter != null)
{
@@ -1137,107 +1280,122 @@ public Expression GetOrAddStateItemExpression(object item, Type itemType = null,
return itemExpr;
}
- Throw.If(throwIfStateRequired || Rules.ThrowIfRuntimeStateRequired, Error.StateIsRequiredToUseItem, item);
+ Throw.If(throwIfStateRequired || Rules.ThrowIfRuntimeStateRequired,
+ Error.StateIsRequiredToUseItem, item);
- var itemIndex = GetOrAddStateItem(item);
- var stateItemExpr = GetStateItemExpression(itemIndex);
- return Expression.Convert(stateItemExpr, itemType);
+ return Expression.Constant(item, itemType);
}
- private static Expression GetPrimitiveOrArrayExprOrDefault(object item, Type itemType)
+ ///
+ public int GetOrAddStateItem(object item)
{
- if (item == null)
- return itemType != null
- ? Expression.Constant(null, itemType)
- : Expression.Constant(null);
-
- itemType = itemType ?? item.GetType();
-
- if (itemType == typeof(DefaultKey))
- return Expression.Call(typeof(DefaultKey), "Of", ArrayTools.Empty(),
- Expression.Constant(((DefaultKey)item).RegistrationOrder));
-
- if (itemType.IsArray)
- {
- var itType = itemType.GetElementType().ThrowIfNull();
- var items = ((IEnumerable)item).Cast().Select(it => GetPrimitiveOrArrayExprOrDefault(it, null));
- var itExprs = Expression.NewArrayInit(itType, items);
- return itExprs;
- }
-
- return itemType.IsPrimitive() || itemType.IsAssignableTo(typeof(Type))
- ? Expression.Constant(item, itemType)
- : null;
+ return -1;
}
#endregion
#region Factories Add/Get
- private sealed class FactoriesEntry
+ internal sealed class FactoriesEntry
{
public readonly DefaultKey LastDefaultKey;
public readonly ImTreeMap Factories;
+ // lastDefaultKey may be null
public FactoriesEntry(DefaultKey lastDefaultKey, ImTreeMap factories)
{
LastDefaultKey = lastDefaultKey;
Factories = factories;
}
+
+ public static readonly FactoriesEntry Empty = new FactoriesEntry(null, ImTreeMap.Empty);
+
+ public FactoriesEntry With(Factory factory, object serviceKey = null)
+ {
+ var lastDefaultKey = serviceKey != null ? LastDefaultKey // remains the same
+ : LastDefaultKey == null ? DefaultKey.Value
+ : LastDefaultKey.Next();
+
+ var factories = Factories.AddOrUpdate(serviceKey ?? lastDefaultKey, factory);
+
+ return new FactoriesEntry(lastDefaultKey, factories);
+ }
}
private Factory GetServiceFactoryOrDefault(Request request, Rules.FactorySelectorRule factorySelector)
{
- var entry = GetServiceFactoryEntryOrDefault(request);
- if (entry == null) // no entry - no factories: return earlier
+ var factoryOrFactories = GetServiceFactoryOrFactoriesOrNull(request);
+ if (factoryOrFactories == null)
return null;
- var singleFactory = entry as Factory;
+ var factory = factoryOrFactories as Factory;
if (factorySelector != null)
{
- if (singleFactory != null)
- return !singleFactory.CheckCondition(request) ? null
- : factorySelector(request, new[] {new KeyValuePair(DefaultKey.Value, singleFactory) });
+ if (factory != null)
+ return !factory.CheckCondition(request) ? null
+ : factorySelector(request, new[] { new KeyValuePair(DefaultKey.Value, factory) });
- var allFactories = ((FactoriesEntry)entry).Factories.Enumerate()
+ var selectedFactories = ((IEnumerable>)factoryOrFactories)
.Where(f => f.Value.CheckCondition(request))
.Select(f => new KeyValuePair(f.Key, f.Value))
.ToArray();
- return factorySelector(request, allFactories);
+ return factorySelector(request, selectedFactories);
}
var serviceKey = request.ServiceKey;
- if (singleFactory != null)
+ if (factory != null)
{
return (serviceKey == null || DefaultKey.Value.Equals(serviceKey))
- && singleFactory.CheckCondition(request) ? singleFactory : null;
+ && factory.CheckCondition(request)
+ ? factory : null;
}
- var factories = ((FactoriesEntry)entry).Factories;
+ var factories = (IEnumerable>)factoryOrFactories;
if (serviceKey != null)
{
- singleFactory = factories.GetValueOrDefault(serviceKey);
- return singleFactory != null && singleFactory.CheckCondition(request) ? singleFactory : null;
+ var keyFactory = factories.FirstOrDefault(f => serviceKey.Equals(f.Key));
+ return keyFactory != null
+ && keyFactory.Value.CheckCondition(request) ? keyFactory.Value : null;
}
- var matchedFactories = factories.Enumerate();
var metadataKey = request.MetadataKey;
var metadata = request.Metadata;
if (metadataKey != null || metadata != null)
- matchedFactories = matchedFactories.Where(f => f.Value.Setup.MatchesMetadata(metadataKey, metadata));
+ factories = factories.Where(f => f.Value.Setup.MatchesMetadata(metadataKey, metadata));
- var defaultFactories = matchedFactories
+ var defaultFactories = factories
.Where(f => f.Key is DefaultKey && f.Value.CheckCondition(request))
- .ToArray();
+ .ToArrayOrSelf();
+
+ // Select single scoped factory if any
+ if (defaultFactories.Length > 1)
+ {
+ if (Rules.ImplicitCheckForReuseMatchingScope && this.GetCurrentScope() != null)
+ {
+ var scopedFactories = defaultFactories.Where(f => f.Value.Reuse is CurrentScopeReuse).ToArray();
+ if (scopedFactories.Length == 1)
+ defaultFactories = scopedFactories;
+ }
+
+ // check that impl generic definition is matched to service type, and selected matched only factories.
+ // the generated factories are cached - so there should not be repeating of the check, and not match of perf decrease.
+ if (defaultFactories.Length != 0)
+ {
+ defaultFactories = defaultFactories.Where(f =>
+ f.Value.FactoryGenerator == null ||
+ f.Value.FactoryGenerator.GetGeneratedFactory(request, ifErrorReturnDefault: true) != null)
+ .ToArrayOrSelf();
+ }
+ }
if (defaultFactories.Length == 1)
{
var defaultFactory = defaultFactories[0];
+
+ // NOTE: For resolution root sets correct default key to be used in delegate cache.
if (request.IsResolutionCall)
- {
- // NOTE: For resolution root sets correct default key to be used in delegate cache.
request.ChangeServiceKey(defaultFactory.Key);
- }
+
return defaultFactory.Value;
}
@@ -1247,7 +1405,7 @@ private Factory GetServiceFactoryOrDefault(Request request, Rules.FactorySelecto
return null;
}
- private object GetServiceFactoryEntryOrDefault(Request request)
+ private object GetServiceFactoryOrFactoriesOrNull(Request request)
{
var serviceKey = request.ServiceKey;
var serviceFactories = _registry.Value.Services;
@@ -1266,10 +1424,12 @@ private object GetServiceFactoryEntryOrDefault(Request request)
if (actualServiceType.IsClosedGeneric())
{
var serviceKeyWithOpenGenericRequiredType = serviceKey as object[];
- if (serviceKeyWithOpenGenericRequiredType != null && serviceKeyWithOpenGenericRequiredType.Length == 2)
+ if (serviceKeyWithOpenGenericRequiredType != null &&
+ serviceKeyWithOpenGenericRequiredType.Length == 2)
{
var openGenericType = serviceKeyWithOpenGenericRequiredType[0] as Type;
- if (openGenericType != null && openGenericType == actualServiceType.GetGenericDefinitionOrNull())
+ if (openGenericType != null &&
+ openGenericType == actualServiceType.GetGenericDefinitionOrNull())
{
actualServiceType = openGenericType;
serviceKey = serviceKeyWithOpenGenericRequiredType[1];
@@ -1283,26 +1443,30 @@ private object GetServiceFactoryEntryOrDefault(Request request)
var entry = serviceFactories.GetValueOrDefault(actualServiceType);
- // Special case for closed-generic lookup type:
- // When entry is not found
- // Or the key in entry is not found
+ // For closed-generic lookup type:
+ // When entry is not found
+ // or the key in entry is not found
// Then go to the open-generic services
if (actualServiceType.IsClosedGeneric())
{
if (entry == null ||
serviceKey != null && (
- entry is FactoriesEntry &&
- ((FactoriesEntry)entry).Factories.GetValueOrDefault(serviceKey) == null ||
- entry is Factory &&
- !serviceKey.Equals(DefaultKey.Value)))
+ entry is Factory && !serviceKey.Equals(DefaultKey.Value) ||
+ entry is FactoriesEntry && ((FactoriesEntry)entry).Factories.GetValueOrDefault(serviceKey) == null))
{
var lookupOpenGenericType = actualServiceType.GetGenericTypeDefinition();
var openGenericEntry = serviceFactories.GetValueOrDefault(lookupOpenGenericType);
- entry = openGenericEntry ?? entry; // stay with original entry if open generic is not found;
+ if (openGenericEntry != null)
+ {
+ entry = openGenericEntry;
+ }
}
}
- return entry;
+ if (entry == null)
+ return null;
+
+ return (object)(entry as Factory) ?? ((FactoriesEntry)entry).Factories.Enumerate();
}
#endregion
@@ -1312,21 +1476,181 @@ private object GetServiceFactoryEntryOrDefault(Request request)
private int _disposed;
private readonly Ref _registry;
- private Ref> _defaultFactoryDelegateCache;
+ private Ref> _defaultFactoryDelegateCache;
- private readonly ContainerWeakRef _containerWeakRef;
- private readonly Request _emptyRequest;
+ // pre-created and stored for performance reasons
+ private readonly ContainerWeakRef _thisContainerWeakRef;
private readonly SingletonScope _singletonScope;
- private readonly object[] _singletonItems;
- private readonly IScope _openedScope;
+ internal readonly IScope _openedScope;
private readonly IScopeContext _scopeContext;
- private sealed class Registry
+ internal void UseInstanceInternal(Type serviceType, object instance, object serviceKey)
{
- public static readonly Registry Empty = new Registry();
+ ThrowIfContainerDisposed();
+
+ var scope = _openedScope ?? _singletonScope;
+ var instanceType = instance == null ? typeof(object) : instance.GetType();
+
+ _registry.Swap(r =>
+ {
+ UsedInstanceFactory newInstanceFactory;
+ var entry = r.Services.GetValueOrDefault(serviceType);
+ if (entry == null)
+ {
+ newInstanceFactory = new UsedInstanceFactory(instanceType);
+ scope.SetOrAdd(scope.GetScopedItemIdOrSelf(newInstanceFactory.FactoryID), instance);
+
+ var servicesWithFactory = serviceKey == null
+ ? r.Services.AddOrUpdate(serviceType, newInstanceFactory)
+ : r.Services.AddOrUpdate(serviceType, FactoriesEntry.Empty.With(newInstanceFactory, serviceKey));
+
+ return r.WithServices(servicesWithFactory);
+ }
+
+ var defaultFactory = entry as Factory;
+ if (defaultFactory != null)
+ {
+ // new factory is also a default one, check if we can reuse the old factory
+ if (serviceKey == null)
+ {
+ // The condition for reusing the factory:
+ var defaultInstanceFactory = defaultFactory as UsedInstanceFactory;
+ if (defaultInstanceFactory != null)
+ {
+ scope.SetOrAdd(scope.GetScopedItemIdOrSelf(defaultInstanceFactory.FactoryID), instance);
+ return r;
+ }
+ }
+
+ // otherwise register a new factory
+ newInstanceFactory = new UsedInstanceFactory(instanceType);
+ scope.SetOrAdd(scope.GetScopedItemIdOrSelf(newInstanceFactory.FactoryID), instance);
+
+ return r.WithServices(r.Services.AddOrUpdate(serviceType,
+ FactoriesEntry.Empty.With(defaultFactory).With(newInstanceFactory, serviceKey)));
+ }
+
+ // the only remaining option is multiple factories entry
+ var factoriesEntry = (FactoriesEntry)entry;
+ if (serviceKey == null)
+ {
+ // if any default factories, find the instance factory to reuse. Throw if multiple.
+ if (factoriesEntry.LastDefaultKey != null)
+ {
+ // As we always permit / re-use the Single factory only, so we cannot have more than one
+ var defaultInstanceFactory = factoriesEntry.Factories.Enumerate()
+ .FirstOrDefault(it => it.Key is DefaultKey && it.Value is UsedInstanceFactory);
+
+ if (defaultInstanceFactory != null)
+ {
+ scope.SetOrAdd(scope.GetScopedItemIdOrSelf(defaultInstanceFactory.Value.FactoryID), instance);
+ return r;
+ }
+ }
+ }
+ else // for multiple factories check for existing service key
+ {
+ var keyedFactory = factoriesEntry.Factories.GetValueOrDefault(serviceKey);
+ if (keyedFactory != null)
+ {
+ var keyedInstanceFactory = keyedFactory as UsedInstanceFactory;
+ if (keyedInstanceFactory != null)
+ {
+ scope.SetOrAdd(scope.GetScopedItemIdOrSelf(keyedInstanceFactory.FactoryID), instance);
+ return r;
+ }
+
+ Throw.It(Error.UnableToUseInstanceForExistingNonInstanceFactory,
+ KV.Of(serviceKey, instance), keyedFactory);
+ }
+ }
+
+ newInstanceFactory = new UsedInstanceFactory(instanceType);
+ scope.SetOrAdd(scope.GetScopedItemIdOrSelf(newInstanceFactory.FactoryID), instance);
+ return r.WithServices(r.Services.AddOrUpdate(serviceType,
+ factoriesEntry.With(newInstanceFactory, serviceKey)));
+ });
+ }
+
+ // Just a wrapper, with only goal to provide and expression for instance access bound to FactoryID
+ internal sealed class UsedInstanceFactory : Factory
+ {
+ public override Type ImplementationType { get { return _instanceType; } }
+ private readonly Type _instanceType;
+
+ public UsedInstanceFactory(Type instanceType)
+ {
+ _instanceType = instanceType;
+ }
+
+ /// Called for Resolution call/root.
+ public override FactoryDelegate GetDelegateOrDefault(Request request)
+ {
+ if (request.IsResolutionRoot)
+ {
+ var decoratedExpr = request.Container.GetDecoratorExpressionOrDefault(request.WithResolvedFactory(this));
+ if (decoratedExpr != null)
+ return decoratedExpr.CompileToDelegate();
+ }
+
+ return GetInstanceFromScopeChainOrSingletons;
+ }
+
+ /// Called for Injection as dependency.
+ public override Expression GetExpressionOrDefault(Request request)
+ {
+ return request.Container.GetDecoratorExpressionOrDefault(request.WithResolvedFactory(this))
+ ?? CreateExpressionOrDefault(request);
+ }
+
+ public override Expression CreateExpressionOrDefault(Request request)
+ {
+ return Resolver.CreateResolutionExpression(request, isRuntimeDependency: true);
+ }
+
+ #region Implementation
+
+ private object GetInstanceFromScopeChainOrSingletons(object[] _, IResolverContext r, IScope __)
+ {
+ var scope = r.Scopes.GetCurrentScope();
+ while (scope != null)
+ {
+ var result = GetAndUnwrapOrDefault(scope, FactoryID);
+ if (result != null)
+ return result;
+ scope = scope.Parent;
+ }
+
+ return GetAndUnwrapOrDefault(r.SingletonScope(), FactoryID);
+ }
+
+ private static object GetAndUnwrapOrDefault(IScope scope, int factoryId)
+ {
+ var id = scope.GetScopedItemIdOrSelf(factoryId);
+ var value = scope.GetOrAdd(id, () => null);
+
+ if (value == null)
+ return null;
+
+ var weaklyReferenced = value as WeakReference;
+ if (weaklyReferenced != null)
+ value = weaklyReferenced.Target.ThrowIfNull(Error.WeakRefReuseWrapperGCed);
+ var preventDisposable = value as object[];
+ if (preventDisposable != null && preventDisposable.Length == 1)
+ value = preventDisposable[0];
+
+ return value;
+ }
+
+ #endregion
+ }
+
+ internal sealed class Registry
+ {
+ public static readonly Registry Empty = new Registry();
public static readonly Registry Default = new Registry(WrappersSupport.Wrappers);
// Factories:
@@ -1335,30 +1659,31 @@ private sealed class Registry
public readonly ImTreeMap Wrappers;
// Cache:
- public readonly Ref> DefaultFactoryDelegateCache;
+ public readonly Ref FactoryExpressionCache;
+
+ public readonly Ref> DefaultFactoryDelegateCache;
// key: KV where Key is ServiceType and object is ServiceKey
- // value: FactoryDelegate or/and IntTreeMap
+ // value: FactoryDelegate or/and IntTreeMap<{requiredServicType+preResolvedParent}, FactoryDelegate>
public readonly Ref>>> KeyedFactoryDelegateCache;
- public readonly Ref FactoryExpressionCache;
-
private enum IsChangePermitted { Permitted, Error, Ignored }
private readonly IsChangePermitted _isChangePermitted;
public Registry WithoutCache()
{
return new Registry(Services, Decorators, Wrappers,
- Ref.Of(ImTreeMap.Empty),
+ Ref.Of(ImMap.Empty),
Ref.Of(ImTreeMap>>.Empty),
- Ref.Of(ImTreeMapIntToObj.Empty), _isChangePermitted);
+ Ref.Of(ImTreeMapIntToObj.Empty),
+ _isChangePermitted);
}
private Registry(ImTreeMap wrapperFactories = null)
: this(ImTreeMap.Empty,
ImTreeMap.Empty,
wrapperFactories ?? ImTreeMap.Empty,
- Ref.Of(ImTreeMap.Empty),
+ Ref.Of(ImMap.Empty),
Ref.Of(ImTreeMap>>.Empty),
Ref.Of(ImTreeMapIntToObj.Empty),
IsChangePermitted.Permitted)
@@ -1368,7 +1693,7 @@ private Registry(ImTreeMap wrapperFactories = null)
ImTreeMap services,
ImTreeMap decorators,
ImTreeMap wrappers,
- Ref> defaultFactoryDelegateCache,
+ Ref> defaultFactoryDelegateCache,
Ref>>> keyedFactoryDelegateCache,
Ref factoryExpressionCache,
IsChangePermitted isChangePermitted)
@@ -1382,7 +1707,7 @@ private Registry(ImTreeMap wrapperFactories = null)
_isChangePermitted = isChangePermitted;
}
- private Registry WithServices(ImTreeMap services)
+ internal Registry WithServices(ImTreeMap services)
{
return services == Services ? this :
new Registry(services, Decorators, Wrappers,
@@ -1395,7 +1720,7 @@ private Registry WithDecorators(ImTreeMap decorators)
return decorators == Decorators ? this :
new Registry(Services, decorators, Wrappers,
DefaultFactoryDelegateCache.NewRef(), KeyedFactoryDelegateCache.NewRef(),
- FactoryExpressionCache.NewRef(), _isChangePermitted);
+ FactoryExpressionCache.NewRef(), _isChangePermitted);
}
private Registry WithWrappers(ImTreeMap wrappers)
@@ -1439,42 +1764,76 @@ public Registry Register(Factory factory, Type serviceType, IfAlreadyRegistered
Wrappers.AddOrUpdate(serviceType, factory));
}
- public bool IsRegistered(Type serviceType, object serviceKey, FactoryType factoryType, Func condition)
+ public Factory[] GetRegisteredFactories(Type serviceType, object serviceKey, FactoryType factoryType,
+ Func condition)
{
serviceType = serviceType.ThrowIfNull();
switch (factoryType)
{
case FactoryType.Wrapper:
- var wrapper = GetWrapperOrDefault(serviceType);
- return wrapper != null && (condition == null || condition(wrapper));
+ var arrayElementType = serviceType.GetArrayElementTypeOrNull();
+ if (arrayElementType != null)
+ serviceType = typeof(IEnumerable<>).MakeGenericType(arrayElementType);
+
+ var wrapper = Wrappers.GetValueOrDefault(serviceType.GetGenericDefinitionOrNull() ?? serviceType);
+ return wrapper != null && (condition == null || condition(wrapper))
+ ? new[] { wrapper }
+ : null;
case FactoryType.Decorator:
var decorators = Decorators.GetValueOrDefault(serviceType);
- return decorators != null && decorators.Length != 0
- && (condition == null || decorators.Any(condition));
- default:
+ var openGenServiceType = serviceType.GetGenericDefinitionOrNull();
+ if (openGenServiceType != null)
+ decorators = decorators.Append(Decorators.GetValueOrDefault(openGenServiceType));
+
+ if (decorators != null && decorators.Length != 0)
+ return condition == null
+ ? decorators
+ : decorators.Where(condition).ToArray();
+ return null;
+
+ default:
var entry = Services.GetValueOrDefault(serviceType);
if (entry == null)
- return false;
+ return null;
var factory = entry as Factory;
if (factory != null)
- return (serviceKey == null || DefaultKey.Value.Equals(serviceKey))
- && (condition == null || condition(factory));
+ {
+ if (serviceKey == null || DefaultKey.Value.Equals(serviceKey))
+ return condition == null || condition(factory)
+ ? new[] { factory }
+ : null;
+ return null;
+ }
var factories = ((FactoriesEntry)entry).Factories;
if (serviceKey == null)
- return condition == null || factories.Enumerate().Any(f => condition(f.Value));
+ {
+ var selectedFactories = condition == null
+ ? factories.Enumerate()
+ : factories.Enumerate().Where(f => condition(f.Value));
+ return selectedFactories.Select(f => f.Value).ToArray();
+ }
factory = factories.GetValueOrDefault(serviceKey);
- return factory != null && (condition == null || condition(factory));
+ return factory != null && (condition == null || condition(factory))
+ ? new[] { factory }
+ : null;
}
}
- public Factory GetWrapperOrDefault(Type serviceType)
+ public bool ClearCache(Type serviceType, object serviceKey, FactoryType factoryType)
{
- return Wrappers.GetValueOrDefault(serviceType.GetGenericDefinitionOrNull() ?? serviceType);
+ var factories = GetRegisteredFactories(serviceType, serviceKey, factoryType, null);
+ if (factories.IsNullOrEmpty())
+ return false;
+
+ for (var i = 0; i < factories.Length; i++)
+ WithoutFactoryCache(factories[i], serviceType, serviceKey);
+
+ return true;
}
private Registry WithService(Factory factory, Type serviceType, object serviceKey, IfAlreadyRegistered ifAlreadyRegistered)
@@ -1493,8 +1852,7 @@ private Registry WithService(Factory factory, Type serviceType, object serviceKe
var oldFactoriesEntry = oldEntry as FactoriesEntry;
if (oldFactoriesEntry != null && oldFactoriesEntry.LastDefaultKey == null) // no default registered yet
- return new FactoriesEntry(DefaultKey.Value,
- oldFactoriesEntry.Factories.AddOrUpdate(DefaultKey.Value, newFactory));
+ return oldFactoriesEntry.With(newFactory);
var oldFactory = oldFactoriesEntry == null ? (Factory)oldEntry : null;
switch (ifAlreadyRegistered)
@@ -1536,26 +1894,27 @@ private Registry WithService(Factory factory, Type serviceType, object serviceKe
oldFactory != null && oldFactory.ImplementationType != implementationType ||
oldFactoriesEntry != null && oldFactoriesEntry.Factories.Enumerate()
.All(f => f.Value.ImplementationType != implementationType))
- return AppendNonKeyed(oldFactoriesEntry, oldFactory, newFactory);
+ {
+ return (oldFactoriesEntry ?? FactoriesEntry.Empty.With(oldFactory)).With(newFactory);
+ }
return oldEntry;
default:
- return AppendNonKeyed(oldFactoriesEntry, oldFactory, newFactory);
+ return (oldFactoriesEntry ?? FactoriesEntry.Empty.With(oldFactory)).With(newFactory);
}
});
}
else // serviceKey != null
{
- var factories = new FactoriesEntry(null, ImTreeMap.Empty.AddOrUpdate(serviceKey, factory));
+ var factories = FactoriesEntry.Empty.With(factory, serviceKey);
services = Services.AddOrUpdate(serviceType, factories, (oldEntry, newEntry) =>
{
if (oldEntry == null)
return newEntry;
if (oldEntry is Factory) // if registered is default, just add it to new entry
- return new FactoriesEntry(DefaultKey.Value,
- ((FactoriesEntry)newEntry).Factories.AddOrUpdate(DefaultKey.Value, (Factory)oldEntry));
+ return ((FactoriesEntry)newEntry).With((Factory)oldEntry);
var oldFactories = (FactoriesEntry)oldEntry;
return new FactoriesEntry(oldFactories.LastDefaultKey,
@@ -1573,8 +1932,6 @@ private Registry WithService(Factory factory, Type serviceType, object serviceKe
replacedFactory = oldFactory;
return newFactory;
- case IfAlreadyRegistered.Throw:
- case IfAlreadyRegistered.AppendNewImplementation:
default:
return Throw.For(Error.UnableToRegisterDuplicateKey, serviceType, newFactory, serviceKey, oldFactory);
}
@@ -1582,7 +1939,7 @@ private Registry WithService(Factory factory, Type serviceType, object serviceKe
});
}
- // Note: We are reusing replaced factory (with same setup and reuse) cache by inheriting its ID.
+ // Note: We are reusing replaced factory (with same setup and reuse) by using the ID.
// It is possible because cache depends only on ID.
var reuseReplacedInstanceFactory = false;
if (replacedFactory != null)
@@ -1608,28 +1965,16 @@ private Registry WithService(Factory factory, Type serviceType, object serviceKe
!reuseReplacedInstanceFactory)
{
if (replacedFactory != null)
- registry = WithoutFactoryCache(registry, replacedFactory, serviceType, serviceKey);
+ registry = registry.WithoutFactoryCache(replacedFactory, serviceType, serviceKey);
else if (replacedFactories != null)
foreach (var f in replacedFactories.Enumerate())
- registry = WithoutFactoryCache(registry, f.Value, serviceType, serviceKey);
+ registry = registry.WithoutFactoryCache(f.Value, serviceType, serviceKey);
}
}
return registry;
}
- private static object AppendNonKeyed(FactoriesEntry oldFactories, Factory oldEntry, Factory newFactory)
- {
- if (oldFactories == null)
- return new FactoriesEntry(DefaultKey.Value.Next(),
- ImTreeMap.Empty
- .AddOrUpdate(DefaultKey.Value, oldEntry)
- .AddOrUpdate(DefaultKey.Value.Next(), newFactory));
-
- var nextKey = oldFactories.LastDefaultKey.Next();
- return new FactoriesEntry(nextKey, oldFactories.Factories.AddOrUpdate(nextKey, newFactory));
- }
-
public Registry Unregister(FactoryType factoryType, Type serviceType, object serviceKey, Func condition)
{
if (_isChangePermitted != IsChangePermitted.Permitted)
@@ -1649,8 +1994,7 @@ public Registry Unregister(FactoryType factoryType, Type serviceType, object ser
return null;
}));
- return removedWrapper == null ? this
- : WithoutFactoryCache(registry, removedWrapper, serviceType);
+ return removedWrapper == null ? this : registry.WithoutFactoryCache(removedWrapper, serviceType);
case FactoryType.Decorator:
Factory[] removedDecorators = null;
@@ -1665,7 +2009,8 @@ public Registry Unregister(FactoryType factoryType, Type serviceType, object ser
return this;
foreach (var removedDecorator in removedDecorators)
- registry = WithoutFactoryCache(registry, removedDecorator, serviceType);
+ registry = registry.WithoutFactoryCache(removedDecorator, serviceType);
+
return registry;
default:
@@ -1750,39 +2095,39 @@ private Registry UnregisterServiceFactory(Type serviceType, object serviceKey =
var registry = WithServices(services);
if (removed is Factory)
- return WithoutFactoryCache(registry, (Factory)removed, serviceType, serviceKey);
+ return registry.WithoutFactoryCache((Factory)removed, serviceType, serviceKey);
var removedFactories = removed as Factory[]
?? ((FactoriesEntry)removed).Factories.Enumerate().Select(f => f.Value).ToArray();
foreach (var removedFactory in removedFactories)
- registry = WithoutFactoryCache(registry, removedFactory, serviceType, serviceKey);
+ registry = registry.WithoutFactoryCache(removedFactory, serviceType, serviceKey);
return registry;
}
- private static Registry WithoutFactoryCache(Registry registry, Factory factory, Type serviceType, object serviceKey = null)
+ // Does not change registry, returning Registry just for convinience of fluent syntax
+ private Registry WithoutFactoryCache(Factory factory, Type serviceType, object serviceKey = null)
{
if (factory.FactoryGenerator != null)
{
foreach (var f in factory.FactoryGenerator.GeneratedFactories.Enumerate())
- WithoutFactoryCache(registry, f.Value, f.Key.Key, f.Key.Value);
+ WithoutFactoryCache(f.Value, f.Key.Key, f.Key.Value);
}
else
{
// clean expression cache using FactoryID as key
- registry.FactoryExpressionCache.Swap(_ => _.Update(factory.FactoryID, null));
+ FactoryExpressionCache.Swap(_ => _.Update(factory.FactoryID, null));
// clean default factory cache
- registry.DefaultFactoryDelegateCache.Swap(_ => _.Update(serviceType, null));
+ DefaultFactoryDelegateCache.Swap(_ => _.Update(serviceType, null));
// clean keyed/context cache from keyed and context based resolutions
- var keyedCacheKey = serviceKey == null ? serviceType
- : (object)new KV(serviceType, serviceKey);
- registry.KeyedFactoryDelegateCache.Swap(_ => _.Update(keyedCacheKey, null));
+ var keyedCacheKey = serviceKey == null ? (object)serviceType : new KV(serviceType, serviceKey);
+ KeyedFactoryDelegateCache.Swap(_ => _.Update(keyedCacheKey, null));
}
- return registry;
+ return this;
}
public Registry WithNoMoreRegistrationAllowed(bool ignoreInsteadOfThrow)
@@ -1794,8 +2139,9 @@ public Registry WithNoMoreRegistrationAllowed(bool ignoreInsteadOfThrow)
}
}
- private Container(Rules rules, Ref registry, SingletonScope singletonScope,
- IScopeContext scopeContext = null, IScope openedScope = null, int disposed = 0)
+ private Container(Rules rules, Ref registry, SingletonScope singletonScope,
+ IScopeContext scopeContext = null, IScope openedScope = null, int disposed = 0,
+ Container rootContainer = null)
{
_disposed = disposed;
@@ -1805,7 +2151,6 @@ public Registry WithNoMoreRegistrationAllowed(bool ignoreInsteadOfThrow)
_defaultFactoryDelegateCache = registry.Value.DefaultFactoryDelegateCache;
_singletonScope = singletonScope;
- _singletonItems = singletonScope.Items;
_scopeContext = scopeContext;
@@ -1814,8 +2159,9 @@ public Registry WithNoMoreRegistrationAllowed(bool ignoreInsteadOfThrow)
else if (scopeContext == null && rules.ImplicitOpenedRootScope) // only valid for non ambient context
_openedScope = new Scope(null, NonAmbientRootScopeName);
- _containerWeakRef = new ContainerWeakRef(this);
- _emptyRequest = Request.CreateEmpty(_containerWeakRef);
+ _thisContainerWeakRef = new ContainerWeakRef(this);
+
+ RootContainer = rootContainer;
}
#endregion
@@ -1825,15 +2171,15 @@ public Registry WithNoMoreRegistrationAllowed(bool ignoreInsteadOfThrow)
public static class ContainerTools
{
/// For given instance resolves and sets properties and fields.
- /// It respects rules set per container,
- /// or if rules are not set it uses ,
+ /// It respects rules set per container,
+ /// or if rules are not set it uses ,
/// or you can specify your own rules with parameter.
/// Input and returned instance type. Service (wrapped)
/// Usually a container instance, cause implements
/// Service instance with properties to resolve and initialize.
/// (optional) Function to select properties and fields, overrides all other rules if specified.
/// Input instance with resolved dependencies, to enable fluent method composition.
- /// Different Rules could be combined together using method.
+ /// Different Rules could be combined together using method.
public static TService InjectPropertiesAndFields(this IContainer container,
TService instance, PropertiesAndFieldsSelector propertiesAndFields = null)
{
@@ -1867,9 +2213,9 @@ public static T New(this IContainer container, Made made = null)
return (T)container.New(typeof(T), made);
}
- /// Creates service given strongly-typed creation expression.
+ /// Creates service given strongly-typed creation expression.
/// Can be used to invoke arbitrary method returning some value with injecting its parameters from container.
- /// Method or constructor result type.
+ /// Method or constructor result type.
/// Container to use for injecting dependencies.
/// Creation expression.
/// Created result.
@@ -1878,7 +2224,7 @@ public static T New(this IContainer container, Made.TypedMade made)
return (T)container.New(typeof(T), made);
}
- /// Registers new service type with factory for registered service type.
+ /// Registers new service type with factory for registered service type.
/// Throw if no such registered service type in container.
/// Container New service type.
/// Existing registered service type.
@@ -1887,14 +2233,14 @@ public static T New(this IContainer container, Made.TypedMade made)
public static void RegisterMapping(this IContainer container, Type serviceType, Type registeredServiceType,
object serviceKey = null, object registeredServiceKey = null)
{
- var request = container.EmptyRequest.Push(registeredServiceType, registeredServiceKey);
+ var request = Request.Create(container, registeredServiceType, registeredServiceKey);
var factory = container.GetServiceFactoryOrDefault(request);
factory.ThrowIfNull(Error.RegisterMappingNotFoundRegisteredService,
registeredServiceType, registeredServiceKey);
container.Register(factory, serviceType, serviceKey, IfAlreadyRegistered.Keep, false);
}
- /// Registers new service type with factory for registered service type.
+ /// Registers new service type with factory for registered service type.
/// Throw if no such registered service type in container.
/// Container
/// New service type.
@@ -1938,13 +2284,13 @@ public static T New(this IContainer container, Made.TypedMade made)
{
var types = implTypeAssemblies.ThrowIfNull()
.SelectMany(assembly => assembly.GetLoadedTypes())
- .Where(type => !type.IsAbstract() && !type.IsCompilerGenerated())
+ .Where(Registrator.IsImplementationType)
.ToArray();
return container.WithAutoFallbackResolution(types, changeDefaultReuse, condition);
}
/// Creates new container with provided parameters and properties
- /// to pass the custom dependency values for injection. The old parameters and properties are overridden,
+ /// to pass the custom dependency values for injection. The old parameters and properties are overridden,
/// but not replaced.
/// Container to work with.
/// (optional) Parameters specification, can be used to proved custom values.
@@ -1997,7 +2343,7 @@ public static T New(this IContainer container, Made.TypedMade made)
{
try
{
- var request = generatingContainer.EmptyRequest.Push(r.ServiceType, r.OptionalServiceKey);
+ var request = Request.Create(generatingContainer, r.ServiceType, r.OptionalServiceKey);
var factoryExpr = r.Factory.GetExpressionOrDefault(request).WrapInFactoryExpression();
resolutionExprList.Add(new KeyValuePair>(r, factoryExpr));
}
@@ -2014,7 +2360,7 @@ public static T New(this IContainer container, Made.TypedMade made)
}
/// Used to find potential problems when resolving the passed services .
- /// Method will collect the exceptions when resolving or injecting the specific registration.
+ /// Method will collect the exceptions when resolving or injecting the specific registration.
/// Does not create any actual service objects.
/// for container
/// (optional) Examined resolved services. If empty will try to resolve every service in container.
@@ -2022,7 +2368,7 @@ public static T New(this IContainer container, Made.TypedMade made)
public static KeyValuePair[] Validate(
this IContainer container, params Type[] resolutionRoots)
{
- return container.VerifyResolutions(resolutionRoots.IsNullOrEmpty()
+ return container.VerifyResolutions(resolutionRoots.IsNullOrEmpty()
? (Func)null
: registration => resolutionRoots.IndexOf(registration.ServiceType) != -1);
}
@@ -2058,7 +2404,7 @@ public static bool IsSupportedInjectedCustomValueType(Type customValueType)
public static Expression RequestInfoToExpression(this IContainer container, RequestInfo request)
{
if (request.IsEmpty)
- return _emptyRequestInfoExpr;
+ return _emptyRequestInfoExpr.Value;
// recursively ask for parent expression until it is empty
var parentRequestInfoExpr = container.RequestInfoToExpression(request.ParentOrWrapper);
@@ -2072,46 +2418,49 @@ public static Expression RequestInfoToExpression(this IContainer container, Requ
var metadata = request.Metadata;
var factoryType = request.FactoryType;
var ifUnresolved = request.IfUnresolved;
+ var flags = request.Flags;
var serviceTypeExpr = Expression.Constant(serviceType, typeof(Type));
var factoryIdExpr = Expression.Constant(factoryID, typeof(int));
var implTypeExpr = Expression.Constant(implementationType, typeof(Type));
- var reuseExpr = request.Reuse.ToExpression(container);
-
- // Try simplified versions of Push first, before the Push with all arguments provided:
+ var reuseExpr = request.Reuse == null
+ ? Expression.Constant(null, typeof(IReuse))
+ : ((IReuseV3)request.Reuse).ToExpression(it => container.GetOrAddStateItemExpression(it));
if (ifUnresolved == IfUnresolved.Throw &&
requiredServiceType == null && serviceKey == null && metadataKey == null && metadata == null &&
- factoryType == FactoryType.Service)
+ factoryType == FactoryType.Service && flags == default(RequestFlags))
return Expression.Call(parentRequestInfoExpr, "Push", ArrayTools.Empty(),
serviceTypeExpr, factoryIdExpr, implTypeExpr, reuseExpr);
var requiredServiceTypeExpr = Expression.Constant(requiredServiceType, typeof(Type));
var servicekeyExpr = Expression.Convert(container.GetOrAddStateItemExpression(serviceKey), typeof(object));
var factoryTypeExpr = Expression.Constant(factoryType, typeof(FactoryType));
+ var flagsExpr = Expression.Constant(flags, typeof(RequestFlags));
if (ifUnresolved == IfUnresolved.Throw &&
metadataKey == null && metadata == null)
return Expression.Call(parentRequestInfoExpr, "Push", ArrayTools.Empty(),
serviceTypeExpr, requiredServiceTypeExpr, servicekeyExpr,
- factoryIdExpr, factoryTypeExpr, implTypeExpr, reuseExpr);
+ factoryIdExpr, factoryTypeExpr, implTypeExpr, reuseExpr, flagsExpr);
var ifUnresolvedExpr = Expression.Constant(ifUnresolved, typeof(IfUnresolved));
if (metadataKey == null && metadata == null)
return Expression.Call(parentRequestInfoExpr, "Push", ArrayTools.Empty(),
serviceTypeExpr, requiredServiceTypeExpr, servicekeyExpr, ifUnresolvedExpr,
- factoryIdExpr, factoryTypeExpr, implTypeExpr, reuseExpr);
+ factoryIdExpr, factoryTypeExpr, implTypeExpr, reuseExpr, flagsExpr);
var metadataKeyExpr = Expression.Constant(metadataKey, typeof(string));
var metadataExpr = Expression.Convert(container.GetOrAddStateItemExpression(metadata), typeof(object));
- return Expression.Call(parentRequestInfoExpr, "Push", ArrayTools.Empty(),
- serviceTypeExpr, requiredServiceTypeExpr, servicekeyExpr, metadataKeyExpr, metadataExpr, ifUnresolvedExpr,
- factoryIdExpr, factoryTypeExpr, implTypeExpr, reuseExpr);
+ return Expression.Call(parentRequestInfoExpr, "Push", ArrayTools.Empty(),
+ serviceTypeExpr, requiredServiceTypeExpr, servicekeyExpr, metadataKeyExpr, metadataExpr, ifUnresolvedExpr,
+ factoryIdExpr, factoryTypeExpr, implTypeExpr, reuseExpr, flagsExpr);
}
- private static readonly Expression _emptyRequestInfoExpr = ReflectionTools.ToExpression(() => RequestInfo.Empty);
+ private static readonly Lazy _emptyRequestInfoExpr = new Lazy(() =>
+ Expression.Field(null, typeof(RequestInfo).GetFieldOrNull("Empty")));
// todo: v3: replace with more direct access
/// Returns the current scope, or null if not opened and is not set.
@@ -2123,61 +2472,44 @@ public static IScope GetCurrentScope(this IContainer container, object name = nu
{
return container.ContainerWeakRef.Scopes.GetCurrentNamedScope(name, throwIfNotFound);
}
- }
- /// Converter of given reuse to its code representation as expression tree.
- public static class ReuseToExpressionConverter
- {
- /// Converts given reuse to its code representation in expression tree.
- /// Reuse to convert
- public static Expression ToExpression(this IReuse reuse, IContainer container)
+ /// Clears delegate and expression cache for specified .
+ /// But does not clear instances of already resolved/created singletons and scoped services!
+ /// Target service or wrapper type.
+ /// Container to operate.
+ /// (optional) If not specified, clears cache for all .
+ /// (optional) If omitted, the cache will be cleared for all resgitrations of .
+ /// True if type is found in the cache and cleared - false otherwise.
+ public static bool ClearCache(this IContainer container, FactoryType? factoryType = null, object serviceKey = null)
{
- var reuseToExpr = reuse as IConvertibleToExpression;
- if (reuseToExpr != null)
- return reuseToExpr.Convert();
-
- if (reuse is SingletonReuse)
- return _singletonReuseExpr;
-
- var currentScopeReuse = reuse as CurrentScopeReuse;
- if (currentScopeReuse != null)
- {
- if (currentScopeReuse.Name == null)
- return _inCurrentScopeReuseExpr;
- return Expression.Call(typeof(Reuse), "InCurrentNamedScope", ArrayTools.Empty(),
- container.GetOrAddStateItemExpression(currentScopeReuse.Name));
- }
-
- var resolutionScopeReuse = reuse as ResolutionScopeReuse;
- if (resolutionScopeReuse != null)
- {
- if (resolutionScopeReuse.AssignableFromServiceType == null &&
- resolutionScopeReuse.ServiceKey == null &&
- resolutionScopeReuse.Outermost == false)
- return _inResolutionScopeReuseExpr;
- return Expression.Call(typeof(Reuse), "InResolutionScopeOf", ArrayTools.Empty(),
- Expression.Constant(resolutionScopeReuse.AssignableFromServiceType, typeof(Type)),
- container.GetOrAddStateItemExpression(resolutionScopeReuse.ServiceKey),
- Expression.Constant(resolutionScopeReuse.Outermost, typeof(bool)));
- }
-
- // transient or no reuse is default
- return Expression.Constant(null, typeof(IReuse));
+ return container.ClearCache(typeof(T), factoryType, serviceKey);
}
- private static readonly Expression _singletonReuseExpr = ReflectionTools.ToExpression(() => Reuse.Singleton);
- private static readonly Expression _inCurrentScopeReuseExpr = ReflectionTools.ToExpression(() => Reuse.InCurrentScope);
- private static readonly Expression _inResolutionScopeReuseExpr = ReflectionTools.ToExpression(() => Reuse.InResolutionScope);
+ /// Clears delegate and expression cache for specified service.
+ /// But does not clear instances of already resolved/created singletons and scoped services!
+ /// Container to operate.
+ /// Target service type.
+ /// (optional) If not specified, clears cache for all .
+ /// (optional) If omitted, the cache will be cleared for all resgitrations of .
+ /// True if type is found in the cache and cleared - false otherwise.
+ public static bool ClearCache(this IContainer container, Type serviceType,
+ FactoryType? factoryType = null, object serviceKey = null)
+ {
+ // todo: v3: remove cast. Move to IContainer.
+ return ((Container)container).ClearCache(serviceType, factoryType, serviceKey);
+ }
}
/// Interface used to convert reuse instance to expression.
public interface IConvertibleToExpression
{
- /// Returns expression representation. subj.
- Expression Convert();
+ /// Returns expression representation without closure.
+ /// Delegate converting of sub-items, constants to container.
+ /// Expression representation.
+ Expression ToExpression(Func fallbackConverter);
}
- /// Used to represent multiple default service keys.
+ /// Used to represent multiple default service keys.
/// Exposes to determine order of service added.
public sealed class DefaultKey
{
@@ -2228,42 +2560,80 @@ private DefaultKey(int registrationOrder)
}
}
- /// Returns reference to actual resolver implementation.
- /// Minimizes dependency to Factory Delegate on container.
+ /// Holds all required info by .
public interface IResolverContext
{
- /// Provides access to resolver implementation.
+ /// Provides access to current / scoped resolver.
IResolver Resolver { get; }
- /// Scopes access.
+ /// Access to the singleton and current scopes.
IScopeAccess Scopes { get; }
}
- /// Guards access to WeakReference target with more DryIoc specific exceptions.
+ /// Provides the shortcuts and sugar based onto
+ /// to be consumed in
+ public static class ResolverContext
+ {
+ /// Returns subj.
+ ///
+ public static IResolver RootResolver(this IResolverContext ctx)
+ {
+ return ctx.RootContainer();
+ }
+
+ /// Returns subj.
+ ///
+ public static IScopeAccess RootScopes(this IResolverContext ctx)
+ {
+ return ctx.RootContainer();
+ }
+
+ /// Returns subj.
+ ///
+ public static IScope SingletonScope(this IResolverContext ctx)
+ {
+ return ctx.RootContainer().SingletonScope;
+ }
+
+ private static Container RootContainer(this IResolverContext ctx)
+ {
+ var containerRef = ((ContainerWeakRef)ctx);
+ return containerRef.GetTarget(maybeDisposed: true).RootContainer
+ ?? containerRef.GetTarget();
+ }
+ }
+
+ /// Wraps access to WeakReference target with DryIoc specific exceptions.
public sealed class ContainerWeakRef : IResolverContext
{
- /// Provides access to resolver implementation.
+ /// Provides access to current / scoped resolver.
public IResolver Resolver { get { return GetTarget(); } }
- /// Scope access.
+ /// Access to the singleton and current scopes.
public IScopeAccess Scopes { get { return GetTarget(); } }
/// Container access.
public IContainer Container { get { return GetTarget(); } }
/// Returns target container when it is not null and not disposed. Otherwise throws exception.
+ /// (optional) If set will return even disposed container.
/// Target container.
- public Container GetTarget()
+ public Container GetTarget(bool maybeDisposed = false)
{
var container = _ref.Target as Container;
- return container != null && !container.IsDisposed ? container
+ return container != null && (maybeDisposed || !container.IsDisposed)
+ ? container
: container == null
? Throw.For(Error.ContainerIsGarbageCollected)
: Throw.For(Error.ContainerIsDisposed);
}
- /// Creates weak reference wrapper over passed container object. Object to wrap.
- public ContainerWeakRef(IContainer container) { _ref = new WeakReference(container); }
+ /// Creates weak reference wrapper over passed container object.
+ /// Container to reference.
+ public ContainerWeakRef(IContainer container)
+ {
+ _ref = new WeakReference(container);
+ }
private readonly WeakReference _ref;
}
@@ -2280,46 +2650,49 @@ public Container GetTarget()
public delegate object FactoryDelegate(object[] state, IResolverContext r, IScope scope);
/// Handles default conversation of expression into .
- public static partial class FactoryCompiler
+ public static partial class FastExpressionCompiler
{
+ private static readonly Type[] _factoryDelegateParamTypes =
+ { typeof(object[]), typeof(IResolverContext), typeof(IScope) };
+
+ private static readonly ParameterExpression[] _factoryDelegateParamExprs =
+ { Container.StateParamExpr, Container.ResolverContextParamExpr, Container.ResolutionScopeParamExpr };
+
/// Wraps service creation expression (body) into and returns result lambda expression.
/// Service expression (body) to wrap. Created lambda expression.
public static Expression WrapInFactoryExpression(this Expression expression)
{
- return Expression.Lambda(OptimizeExpression(expression), _factoryDelegateParamsExpr);
+ return Expression.Lambda(OptimizeExpression(expression), _factoryDelegateParamExprs);
}
- static partial void CompileToDelegate(Expression expression, ref FactoryDelegate result);
+ static partial void TryCompile(ref TDelegate compileDelegate,
+ Expression bodyExpr,
+ ParameterExpression[] paramExprs,
+ Type[] paramTypes,
+ Type returnType) where TDelegate : class;
- /// First wraps the input service creation expression into lambda expression and
- /// then compiles lambda expression to actual used for service resolution.
- /// By default it is using Expression.Compile but if corresponding rule specified (available on .Net 4.0 and higher),
- /// it will compile to DymanicMethod/Assembly.
- /// Service expression (body) to wrap.
- /// To access container state that may be required for compilation.
+ /// First wraps the input service expression into lambda expression and
+ /// then compiles lambda expression to actual used for service resolution.
+ /// Service creation expression.
/// Compiled factory delegate to use for service resolution.
- public static FactoryDelegate CompileToDelegate(this Expression expression, IContainer container)
+ public static FactoryDelegate CompileToDelegate(this Expression expression)
{
expression = OptimizeExpression(expression);
- // Optimization for fast singleton compilation
- if (expression.NodeType == ExpressionType.ArrayIndex)
+
+ // Optimize: just extract singleton from expression without compiling
+ if (expression.NodeType == ExpressionType.Constant)
{
- var arrayIndexExpr = (BinaryExpression)expression;
- if (arrayIndexExpr.Left.NodeType == ExpressionType.Parameter)
- {
- var index = (int)((ConstantExpression)arrayIndexExpr.Right).Value;
- if (index < container.ResolutionStateCache.Length)
- {
- var value = container.ResolutionStateCache[index];
- return (state, context, scope) => value;
- }
- }
+ var value = ((ConstantExpression)expression).Value;
+ return (state, context, scope) => value;
}
FactoryDelegate factoryDelegate = null;
- CompileToDelegate(expression, ref factoryDelegate);
- // ReSharper disable once ConstantNullCoalescingCondition
- return factoryDelegate ?? Expression.Lambda(expression, _factoryDelegateParamsExpr).Compile();
+ TryCompile(ref factoryDelegate, expression, _factoryDelegateParamExprs, _factoryDelegateParamTypes, typeof(object));
+
+ if (factoryDelegate != null)
+ return factoryDelegate;
+
+ return Expression.Lambda(expression, _factoryDelegateParamExprs).Compile();
}
private static Expression OptimizeExpression(Expression expression)
@@ -2330,9 +2703,6 @@ private static Expression OptimizeExpression(Expression expression)
expression = Expression.Convert(expression, typeof(object));
return expression;
}
-
- private static readonly ParameterExpression[] _factoryDelegateParamsExpr =
- { Container.StateParamExpr, Container.ResolverContextParamExpr, Container.ResolutionScopeParamExpr };
}
/// Adds to Container support for:
@@ -2345,15 +2715,22 @@ private static Expression OptimizeExpression(Expression expression)
///
public static class WrappersSupport
{
- /// Supported Func types up to 4 input parameters.
+ /// Supported Func types.
public static readonly Type[] FuncTypes =
{
typeof(Func<>), typeof(Func<,>), typeof(Func<,,>), typeof(Func<,,,>), typeof(Func<,,,,>),
typeof(Func<,,,,,>), typeof(Func<,,,,,,>), typeof(Func<,,,,,,,>)
};
+ /// Supported Action types.
+ public static readonly Type[] ActionTypes =
+ {
+ typeof(Action), typeof(Action<>), typeof(Action<,>), typeof(Action<,,>), typeof(Action<,,,>),
+ typeof(Action<,,,,>), typeof(Action<,,,,,>), typeof(Action<,,,,,,>)
+ };
+
/// Supported open-generic collection types.
- public static readonly IEnumerable ArrayInterfaces =
+ public static readonly IEnumerable ArrayInterfaces =
typeof(object[]).GetImplementedInterfaces()
.Where(t => t.IsGeneric())
.Select(t => t.GetGenericTypeDefinition())
@@ -2400,7 +2777,7 @@ public static bool IsFuncWithArgs(this Type type)
new ExpressionFactory(GetLazyEnumerableExpressionOrDefault, setup: Setup.Wrapper));
wrappers = wrappers.AddOrUpdate(typeof(Lazy<>),
- new ExpressionFactory(GetLazyExpressionOrDefault, setup: Setup.Wrapper));
+ new ExpressionFactory(r => GetLazyExpressionOrDefault(r), setup: Setup.Wrapper));
wrappers = wrappers.AddOrUpdate(typeof(KeyValuePair<,>),
new ExpressionFactory(GetKeyValuePairExpressionOrDefault, setup: Setup.WrapperWith(1)));
@@ -2415,11 +2792,16 @@ public static bool IsFuncWithArgs(this Type type)
new ExpressionFactory(GetLambdaExpressionExpressionOrDefault, setup: Setup.Wrapper));
wrappers = wrappers.AddOrUpdate(typeof(Func<>),
- new ExpressionFactory(GetFuncExpressionOrDefault, setup: Setup.Wrapper));
+ new ExpressionFactory(GetFuncOrActionExpressionOrDefault, setup: Setup.Wrapper));
for (var i = 0; i < FuncTypes.Length; i++)
wrappers = wrappers.AddOrUpdate(FuncTypes[i],
- new ExpressionFactory(GetFuncExpressionOrDefault, setup: Setup.WrapperWith(i)));
+ new ExpressionFactory(GetFuncOrActionExpressionOrDefault, setup: Setup.WrapperWith(i)));
+
+ for (var i = 0; i < ActionTypes.Length; i++)
+ wrappers = wrappers.AddOrUpdate(ActionTypes[i],
+ new ExpressionFactory(GetFuncOrActionExpressionOrDefault,
+ setup: Setup.WrapperWith(unwrap: _ => typeof(void))));
wrappers = AddContainerInterfacesAndDisposableScope(wrappers);
@@ -2428,21 +2810,26 @@ public static bool IsFuncWithArgs(this Type type)
private static ImTreeMap AddContainerInterfacesAndDisposableScope(ImTreeMap wrappers)
{
- wrappers = wrappers.AddOrUpdate(typeof(IResolver),
- new ExpressionFactory(_ => Container.ResolverExpr, setup: Setup.Wrapper));
+ wrappers = wrappers.AddOrUpdate(typeof(IResolver),
+ new ExpressionFactory(Container.GetResolverExpr, setup: Setup.Wrapper));
+
+ // todo: replace convert with exposed Container property on ResolverContext.
+ var containerFactory = new ExpressionFactory(r =>
+ Expression.Convert(Container.GetResolverExpr(r), r.ServiceType),
+ setup: Setup.Wrapper);
- var containerFactory = new ExpressionFactory(r =>
- Expression.Convert(Container.ResolverExpr, r.ServiceType), setup: Setup.Wrapper);
- wrappers = wrappers.AddOrUpdate(typeof(IRegistrator), containerFactory);
- wrappers = wrappers.AddOrUpdate(typeof(IContainer), containerFactory);
+ wrappers = wrappers
+ .AddOrUpdate(typeof(IRegistrator), containerFactory)
+ .AddOrUpdate(typeof(IContainer), containerFactory);
- wrappers = wrappers.AddOrUpdate(typeof(IDisposable),
+ wrappers = wrappers.AddOrUpdate(typeof(IDisposable),
new ExpressionFactory(r => r.IsResolutionRoot ? null : Container.GetResolutionScopeExpression(r),
setup: Setup.Wrapper));
return wrappers;
}
+ // todo: Probably move to container to consolidate work with factories.
/// Returns wrapper factory. For open-generic wrapper - generated closed factory first.
/// Wrapper request.
/// Found wrapper factory or default null otherwise.
@@ -2456,7 +2843,11 @@ public static Factory ResolveWrapperOrGetDefault(Request request)
var factory = request.Container.GetWrapperFactoryOrDefault(actualServiceType);
if (factory != null && factory.FactoryGenerator != null)
- factory = factory.FactoryGenerator.GetGeneratedFactoryOrDefault(request);
+ factory = factory.FactoryGenerator.GetGeneratedFactory(request);
+
+ if (factory != null && factory.Setup.Condition != null &&
+ !factory.Setup.Condition(request.RequestInfo))
+ return null;
return factory;
}
@@ -2467,12 +2858,16 @@ private static Expression GetArrayExpression(Request request)
var container = request.Container;
var rules = container.Rules;
- if (rules.ResolveIEnumerableAsLazyEnumerable &&
- collectionType.GetGenericDefinitionOrNull() == typeof(IEnumerable<>))
- return GetLazyEnumerableExpressionOrDefault(request);
-
var itemType = collectionType.GetArrayElementTypeOrNull() ?? collectionType.GetGenericParamsAndArgs()[0];
+ if (rules.ResolveIEnumerableAsLazyEnumerable)
+ {
+ var lazyEnumerableExpr = GetLazyEnumerableExpressionOrDefault(request);
+ if (collectionType.GetGenericDefinitionOrNull() != typeof(IEnumerable<>))
+ return Expression.Call(typeof(Enumerable), "ToArray", new[] { itemType }, lazyEnumerableExpr);
+ return lazyEnumerableExpr;
+ }
+
var requiredItemType = container.GetWrappedType(itemType, request.RequiredServiceType);
var items = container.GetAllServiceFactories(requiredItemType)
@@ -2483,15 +2878,15 @@ private static Expression GetArrayExpression(Request request)
{
var requiredItemOpenGenericType = requiredItemType.GetGenericDefinitionOrNull();
var openGenericItems = container.GetAllServiceFactories(requiredItemOpenGenericType)
- .Select(f => new ServiceRegistrationInfo(f.Value,
+ .Select(f => new ServiceRegistrationInfo(f.Value,
requiredItemType,
// NOTE: Special service key with info about open-generic factory service type
- new[] { requiredItemOpenGenericType, f.Key }))
+ new[] { requiredItemOpenGenericType, f.Key }))
.ToArray();
items = items.Append(openGenericItems);
}
- // Append registered generic types with compatible variance,
+ // Append registered generic types with compatible variance,
// e.g. for IHandler - IHandler is compatible with IHandler if B : A.
var includeVariantGenericItems = requiredItemType.IsGeneric() && rules.VariantGenericTypesInResolvedCollection;
if (includeVariantGenericItems)
@@ -2506,10 +2901,12 @@ private static Expression GetArrayExpression(Request request)
items = items.Append(variantGenericItems);
}
- // Composite pattern support: filter out composite root in available keys
+ // Composite pattern support: filter out composite parent service skip wrappers and decorators
var parent = request.Parent;
- if (!parent.IsEmpty &&
- parent.GetActualServiceType() == requiredItemType) // check fast for the parent of the same type
+ if (parent.FactoryType != FactoryType.Service)
+ parent = parent.Enumerate().FirstOrDefault(p => p.FactoryType == FactoryType.Service) ?? RequestInfo.Empty;
+
+ if (!parent.IsEmpty && parent.GetActualServiceType() == requiredItemType) // check fast for the parent of the same type
{
items = items.Where(x => x.Factory.FactoryID != parent.FactoryID &&
(x.Factory.FactoryGenerator == null || !x.Factory.FactoryGenerator.GeneratedFactories.Enumerate().Any(f =>
@@ -2577,7 +2974,8 @@ private static Expression GetLazyEnumerableExpressionOrDefault(Request request)
Throw.It(Error.NotPossibleToResolveLazyEnumerableInsideFuncWithArgs, request);
var container = request.Container;
- var itemType = request.ServiceType.GetGenericParamsAndArgs()[0];
+ var collectionType = request.ServiceType;
+ var itemType = collectionType.GetArrayElementTypeOrNull() ?? collectionType.GetGenericParamsAndArgs()[0];
var requiredItemType = container.GetWrappedType(itemType, request.RequiredServiceType);
// Composite pattern support: find composite parent key to exclude from result.
@@ -2590,16 +2988,18 @@ private static Expression GetLazyEnumerableExpressionOrDefault(Request request)
compositeParentRequiredType = parent.RequiredServiceType;
}
- var preresolveParent = container.RequestInfoToExpression(request.RequestInfo);
+ var resolverExpr = Container.GetResolverExpr(request);
+ var resolutionScopeExpr = Container.GetResolutionScopeExpression(request);
+ var preResolveParentExpr = container.RequestInfoToExpression(request.RequestInfo);
- var callResolveManyExpr = Expression.Call(Container.ResolverExpr, _resolveManyMethod,
+ var callResolveManyExpr = Expression.Call(resolverExpr, _resolveManyMethod,
Expression.Constant(itemType),
container.GetOrAddStateItemExpression(request.ServiceKey),
Expression.Constant(requiredItemType),
container.GetOrAddStateItemExpression(compositeParentKey),
Expression.Constant(compositeParentRequiredType, typeof(Type)),
- preresolveParent,
- Container.GetResolutionScopeExpression(request));
+ preResolveParentExpr,
+ resolutionScopeExpr);
if (itemType != typeof(object)) // cast to object is not required cause Resolve already return IEnumerable
callResolveManyExpr = Expression.Call(typeof(Enumerable), "Cast", new[] { itemType }, callResolveManyExpr);
@@ -2608,8 +3008,11 @@ private static Expression GetLazyEnumerableExpressionOrDefault(Request request)
return Expression.New(lazyEnumerableCtor, callResolveManyExpr);
}
- // Result: r => new Lazy(() => r.Resolver.Resolve(key, ifUnresolved, requiredType));
- private static Expression GetLazyExpressionOrDefault(Request request)
+ /// Gets the expression for wrapper.
+ /// The resolution request.
+ /// if set to true then check for service registration before creating resolution expression.
+ /// Expression: r => new Lazy{TService}(() => r.Resolver.Resolve{TService}(key, ifUnresolved, requiredType));
+ public static Expression GetLazyExpressionOrDefault(Request request, bool nullWrapperForUnresolvedService = false)
{
if (request.IsWrappedInFuncWithArgs(immediateParent: true))
Throw.It(Error.NotPossibleToResolveLazyInsideFuncWithArgs, request);
@@ -2617,6 +3020,15 @@ private static Expression GetLazyExpressionOrDefault(Request request)
var lazyType = request.GetActualServiceType();
var serviceType = lazyType.GetGenericParamsAndArgs()[0];
var serviceRequest = request.Push(serviceType);
+
+ var serviceFactory = request.Container.ResolveFactory(serviceRequest);
+ if (serviceFactory == null)
+ return request.IfUnresolved == IfUnresolved.ReturnDefault
+ ? Expression.Constant(null, lazyType)
+ : null;
+
+ serviceRequest = serviceRequest.WithResolvedFactory(serviceFactory, skipRecursiveDependencyCheck: true);
+
var serviceExpr = Resolver.CreateResolutionExpression(serviceRequest);
// Note: the conversion is required in .NET 3.5 to handle lack of covariance for Func
@@ -2630,31 +3042,56 @@ private static Expression GetLazyExpressionOrDefault(Request request)
return Expression.New(wrapperCtor, factoryExpr);
}
- private static Expression GetFuncExpressionOrDefault(Request request)
+ private static Expression GetFuncOrActionExpressionOrDefault(Request request)
{
- var funcType = request.GetActualServiceType();
- var funcArgs = funcType.GetGenericParamsAndArgs();
- var serviceType = funcArgs[funcArgs.Length - 1];
+ var wrapperType = request.GetActualServiceType();
+ var isAction = wrapperType == typeof(Action);
+ if (!isAction)
+ {
+ var openGenericWrapperType = wrapperType.GetGenericDefinitionOrNull().ThrowIfNull();
+ var funcIndex = FuncTypes.IndexOf(openGenericWrapperType);
+ if (funcIndex == -1)
+ {
+ isAction = ActionTypes.IndexOf(openGenericWrapperType) != -1;
+ Throw.If(!isAction);
+ }
+ }
+
+ var argTypes = wrapperType.GetGenericParamsAndArgs();
+ var argCount = isAction ? argTypes.Length : argTypes.Length - 1;
+ var serviceType = isAction ? typeof(void) : argTypes[argCount];
+
+ var flags = RequestFlags.IsWrappedInFunc;
- ParameterExpression[] funcArgExprs = null;
- if (funcArgs.Length > 1)
+ var argExprs = new ParameterExpression[argCount]; // may be empty, that's OK
+ if (argCount != 0)
{
- request = request.WithFuncArgs(funcType);
- funcArgExprs = request.FuncArgs.Value;
+ for (var i = 0; i < argCount; ++i)
+ {
+ var argType = argTypes[i];
+ var argName = "_" + argType.Name + i; // valid unique argument names for code generation
+ argExprs[i] = Expression.Parameter(argType, argName);
+ }
+
+ request = request.WithArgs(argExprs);
+ flags |= RequestFlags.IsWrappedInFuncWithArgs;
}
- var serviceRequest = request.Push(serviceType);
+ var serviceRequest = request.Push(serviceType, flags: flags);
var serviceFactory = request.Container.ResolveFactory(serviceRequest);
- var serviceExpr = serviceFactory == null ? null : serviceFactory.GetExpressionOrDefault(serviceRequest);
+ if (serviceFactory == null)
+ return null;
+
+ var serviceExpr = serviceFactory.GetExpressionOrDefault(serviceRequest);
if (serviceExpr == null)
return null;
// Note: the conversation is required in .NET 3.5 to handle lack of covariance for Func
// So that Func may be used for Func
- if (serviceExpr.Type != serviceType)
+ if (!isAction && serviceExpr.Type != serviceType)
serviceExpr = Expression.Convert(serviceExpr, serviceType);
- return Expression.Lambda(funcType, serviceExpr, funcArgExprs);
+ return Expression.Lambda(wrapperType, serviceExpr, argExprs);
}
private static Expression GetLambdaExpressionExpressionOrDefault(Request request)
@@ -2690,16 +3127,24 @@ private static Expression GetKeyValuePairExpressionOrDefault(Request request)
return pairExpr;
}
- ///
+ /// Universal expression factory to wrap service with metadata.
+ /// Works with any generic type with first Type arg - Service type and second Type arg - Metadata type,
+ /// and constructor with Service and Metadata arguments respectively.
/// - if service key is not specified in request then method will search for all
/// registered factories with the same metadata type ignoring keys.
/// - if metadata is IDictionary{string, object},
/// then the First value matching the TMetadata type will be returned
- ///
- private static Expression GetMetaExpressionOrDefault(Request request)
+ ///
+ /// Requested service.
+ /// Wrapper creation expression.
+ public static Expression GetMetaExpressionOrDefault(Request request)
{
var metaType = request.GetActualServiceType();
var typeArgs = metaType.GetGenericParamsAndArgs();
+
+ var metaCtor = metaType.GetConstructorOrNull(args: typeArgs)
+ .ThrowIfNull(Error.NotFoundMetaCtorWithTwoArgs, typeArgs, request);
+
var metadataType = typeArgs[1];
var serviceType = typeArgs[0];
@@ -2711,6 +3156,8 @@ private static Expression GetMetaExpressionOrDefault(Request request)
if (serviceKey != null)
factories = factories.Where(f => serviceKey.Equals(f.Key));
+ // note: this may be issue potential of selecting only the first factory
+ // if the service keys for some reason are not unique
var result = factories
.FirstOrDefault(factory =>
{
@@ -2749,10 +3196,7 @@ private static Expression GetMetaExpressionOrDefault(Request request)
}
var metadataExpr = request.Container.GetOrAddStateItemExpression(resultMetadata, metadataType);
-
- var metaCtor = metaType.GetSingleConstructorOrNull().ThrowIfNull();
- var metaExpr = Expression.New(metaCtor, serviceExpr, metadataExpr);
- return metaExpr;
+ return Expression.New(metaCtor, serviceExpr, metadataExpr);
}
}
@@ -2762,10 +3206,38 @@ public sealed class Rules
/// No rules as staring point.
public static readonly Rules Default = new Rules();
- /// Dependency nesting level where to split object graph into separate Resolve call
- /// to optimize performance of large object graph.
- /// At the moment the value is predefined. Not sure if it should be user-defined.
- public readonly int LevelToSplitObjectGraphIntoResolveCalls = 6;
+ /// Default value for
+ public const int DefaultMaxObjectGraphSize = 32;
+
+ /// Max number of dependencies including nested ones,
+ /// before splitting the graph with Resolve calls.
+ public int MaxObjectGraphSize { get; private set; }
+
+ /// Sets .
+ /// To disable the limit please use
+ /// New value. Should be 1 or higher.
+ /// New rules.
+ public Rules WithMaxObjectGraphSize(int size)
+ {
+ Throw.If(size < 1);
+ var newRules = (Rules)MemberwiseClone();
+ newRules.MaxObjectGraphSize = size;
+ return newRules;
+ }
+
+ /// Disables the limitation,
+ /// so that object graph won't be split due this setting.
+ /// New rules.
+ public Rules WithoutMaxObjectGraphSize()
+ {
+ var newRules = (Rules)MemberwiseClone();
+ newRules.MaxObjectGraphSize = -1;
+ return newRules;
+ }
+
+ // todo: v3: Rename to remove
+ /// Obsolete: replaced with
+ public int LevelToSplitObjectGraphIntoResolveCalls { get; private set; }
/// Shorthand to
public FactoryMethodSelector FactoryMethod { get { return _made.FactoryMethod; } }
@@ -2790,6 +3262,7 @@ public sealed class Rules
return With(Made.Of(factoryMethod, parameters, propertiesAndFields));
}
+ // todo: may be add a override with option, e.g. to fallback from made.FactoryMethod to previous FM, used by MEF at least
/// Returns new instance of the rules with specified .
/// New Made.Of rules.
/// Instructs to override registration level Made.Of
@@ -2813,12 +3286,12 @@ public Rules With(Made made, bool overrideRegistrationMade = false)
/// Single selected factory, or null if unable to select.
public delegate Factory FactorySelectorRule(Request request, KeyValuePair[] factories);
- /// Rules to select single matched factory default and keyed registered factory/factories.
+ /// Rules to select single matched factory default and keyed registered factory/factories.
/// Selectors applied in specified array order, until first returns not null .
/// Default behavior is throw on multiple registered default factories, cause it is not obvious what to use.
public FactorySelectorRule FactorySelector { get; private set; }
- /// Sets
+ /// Sets
/// Selectors to set, could be null to use default approach. New rules.
public Rules WithFactorySelector(FactorySelectorRule rule)
{
@@ -2849,8 +3322,32 @@ public static FactorySelectorRule SelectKeyedOverDefaultFactory(object serviceKe
?? factories.FirstOrDefault(f => f.Key.Equals(null)).Value;
}
+ /// Specify the method signature for returning mutiple keyed factories. This is dynamic analog to the normal Container Registry.
+ ///
+ /// (optional) If null will request all factories of
+ /// Specifies what kind of service is requested.
+ /// Key-Factory pairs.
+ public delegate IEnumerable> DynamicRegistrationProvider(
+ Type serviceType,
+ object serviceKey,
+ FactoryType requiredFactoryType
+ // todo: other options like IfAlreadyRegistered?
+ );
+
+ /// Providers for resolving multiple not-registered services. Null by default.
+ public DynamicRegistrationProvider[] DynamicRegistrationProviders { get; private set; }
+
+ /// Appends handler to current unknown service providers.
+ /// Rules to append. New Rules.
+ public Rules WithDynamicRegistrations(params DynamicRegistrationProvider[] rules)
+ {
+ var newRules = (Rules)MemberwiseClone();
+ newRules.DynamicRegistrationProviders = newRules.DynamicRegistrationProviders.Append(rules);
+ return newRules;
+ }
+
/// Defines delegate to return factory for request not resolved by registered factories or prior rules.
- /// Applied in specified array order until return not null .
+ /// Applied in specified array order until return not null .
/// Request to return factory for Factory to resolve request, or null if unable to resolve.
public delegate Factory UnknownServiceResolver(Request request);
@@ -2867,7 +3364,7 @@ public Rules WithUnknownServiceResolvers(params UnknownServiceResolver[] rules)
}
/// Removes specified resolver from unknown service resolvers, and returns new Rules.
- /// If no resolver was found then will stay the same instance,
+ /// If no resolver was found then will stay the same instance,
/// so it could be check for remove success or fail.
/// Rule tor remove. New rules.
public Rules WithoutUnknownServiceResolver(UnknownServiceResolver rule)
@@ -2952,24 +3449,25 @@ public Rules WithoutFallbackContainer(IContainer container)
return newRules;
}
- /// .
+ /// See
public IReuse DefaultReuseInsteadOfTransient { get; private set; }
- /// Sets different default reuse per container rules. Default is Transient.
- /// Reuse value.
- /// New rules with new reuse.
- public Rules WithDefaultReuseInsteadOfTransient(IReuse defaultReuseInsteadOfTransient)
+ // todo: v3: Rename to WithDefaultReuse, because using rules.WithDefaultReuseInsteadOfTransient(Reuse.Transient)) seems off.
+ /// The reuse used in case if reuse is unspecified (null) in Register methods.
+ /// Reuse to set. If null the will be used
+ /// New rules.
+ public Rules WithDefaultReuseInsteadOfTransient(IReuse reuse)
{
var newRules = (Rules)MemberwiseClone();
- newRules.DefaultReuseInsteadOfTransient = defaultReuseInsteadOfTransient;
+ newRules.DefaultReuseInsteadOfTransient = reuse ?? Reuse.Transient;
return newRules;
}
- /// Given item object and its type should return item "pure" expression presentation,
- /// without side-effects or external dependencies.
+ /// Given item object and its type should return item "pure" expression presentation,
+ /// without side-effects or external dependencies.
/// e.g. for string "blah" Expression.Constant("blah", typeof(string))
.
/// If unable to convert should return null.
- /// Item object. Item is not null.
+ /// Item object. Item is not null.
/// Item type. Item type is not null.
/// Expression or null.
public delegate Expression ItemToExpressionConverterRule(object item, Type itemType);
@@ -2999,7 +3497,7 @@ public bool ThrowIfDependencyHasShorterReuseLifespan
public Rules WithoutThrowIfDependencyHasShorterReuseLifespan()
{
var newRules = (Rules)MemberwiseClone();
- newRules._settings ^= Settings.ThrowIfDependencyHasShorterReuseLifespan;
+ newRules._settings &= ~Settings.ThrowIfDependencyHasShorterReuseLifespan;
return newRules;
}
@@ -3017,7 +3515,7 @@ public bool ThrowOnRegisteringDisposableTransient
public Rules WithoutThrowOnRegisteringDisposableTransient()
{
var newRules = (Rules)MemberwiseClone();
- newRules._settings ^= Settings.ThrowOnRegisteringDisposableTransient;
+ newRules._settings &= ~Settings.ThrowOnRegisteringDisposableTransient;
return newRules;
}
@@ -3028,11 +3526,11 @@ public bool TrackingDisposableTransients
}
/// Turns tracking of disposable transients in dependency parent scope, or in current scope if service
- /// is resolved directly.
- ///
+ /// is resolved directly.
+ ///
/// If no open scope at the moment then resolved transient won't be tracked and it is up to you
/// to dispose it! That's is similar situation to creating service by new - you have full control.
- ///
+ ///
/// If dependency wrapped in Func somewhere in parent chain then it also won't be tracked, because
/// Func supposedly means multiple object creation and for container it is not clear what to do, so container
/// delegates that to user. Func here is the similar to Owned relationship type in Autofac library.
@@ -3042,8 +3540,8 @@ public bool TrackingDisposableTransients
public Rules WithTrackingDisposableTransients()
{
var newRules = (Rules)MemberwiseClone();
- newRules._settings |= Settings.TrackingDisposableTransients;
- newRules._settings ^= Settings.ThrowOnRegisteringDisposableTransient;
+ newRules._settings |= Settings.TrackingDisposableTransients; // turning On
+ newRules._settings &= ~Settings.ThrowOnRegisteringDisposableTransient; // turning Off
return newRules;
}
@@ -3058,7 +3556,7 @@ public bool EagerCachingSingletonForFasterAccess
public Rules WithoutEagerCachingSingletonForFasterAccess()
{
var newRules = (Rules)MemberwiseClone();
- newRules._settings ^= Settings.EagerCachingSingletonForFasterAccess;
+ newRules._settings &= ~Settings.EagerCachingSingletonForFasterAccess;
return newRules;
}
@@ -3087,7 +3585,7 @@ public bool ImplicitCheckForReuseMatchingScope
public Rules WithoutImplicitCheckForReuseMatchingScope()
{
var newRules = (Rules)MemberwiseClone();
- newRules._settings ^= Settings.ImplicitCheckForReuseMatchingScope;
+ newRules._settings &= ~Settings.ImplicitCheckForReuseMatchingScope;
return newRules;
}
@@ -3117,7 +3615,7 @@ public bool VariantGenericTypesInResolvedCollection
public Rules WithoutVariantGenericTypesInResolvedCollection()
{
var newRules = (Rules)MemberwiseClone();
- newRules._settings ^= Settings.VariantGenericTypesInResolvedCollection;
+ newRules._settings &= ~Settings.VariantGenericTypesInResolvedCollection;
return newRules;
}
@@ -3141,8 +3639,9 @@ public bool ImplicitOpenedRootScope
}
/// Specifies to open scope as soon as container is created (the same as for Singleton scope).
- /// That way you don't need to call .
- /// Implicitly opened scope will be disposed together with Singletons when container is disposed.
+ /// That way you don't need to call .
+ /// Implicitly opened scope will be disposed together with Singletons when container is disposed.
+ /// The name of root scope is .
/// The setting is only valid for container without ambient scope context.
/// Returns new rules with flag set.
public Rules WithImplicitRootOpenScope()
@@ -3174,6 +3673,8 @@ private Rules()
{
_made = Made.Default;
_settings = DEFAULT_SETTINGS;
+ DefaultReuseInsteadOfTransient = Reuse.Transient;
+ MaxObjectGraphSize = DefaultMaxObjectGraphSize;
}
private Made _made;
@@ -3181,15 +3682,15 @@ private Rules()
[Flags]
private enum Settings
{
- ThrowIfDependencyHasShorterReuseLifespan = 1 << 1,
- ThrowOnRegisteringDisposableTransient = 1 << 2,
- TrackingDisposableTransients = 1 << 3,
- ImplicitCheckForReuseMatchingScope = 1 << 4,
- VariantGenericTypesInResolvedCollection = 1 << 5,
- ResolveIEnumerableAsLazyEnumerable = 1 << 6,
- EagerCachingSingletonForFasterAccess = 1 << 7,
- ImplicitRootOpenScope = 1 << 8,
- ThrowIfRuntimeStateRequired = 1 << 9
+ ThrowIfDependencyHasShorterReuseLifespan = 1 << 1,
+ ThrowOnRegisteringDisposableTransient = 1 << 2,
+ TrackingDisposableTransients = 1 << 3,
+ ImplicitCheckForReuseMatchingScope = 1 << 4,
+ VariantGenericTypesInResolvedCollection = 1 << 5,
+ ResolveIEnumerableAsLazyEnumerable = 1 << 6,
+ EagerCachingSingletonForFasterAccess = 1 << 7,
+ ImplicitRootOpenScope = 1 << 8,
+ ThrowIfRuntimeStateRequired = 1 << 9
}
private const Settings DEFAULT_SETTINGS =
@@ -3222,6 +3723,19 @@ public static FactoryMethod Of(MemberInfo ctorOrMethodOrMember, ServiceInfo fact
return new FactoryMethod(ctorOrMethodOrMember, factoryInfo);
}
+ /// Discovers the static factory method or member by name in .
+ /// Should play nice with C# nameof operator.
+ /// Name or method or member.
+ /// Class with static member.
+ /// Factory method info.
+ public static FactoryMethod Of(string methodOrMemberName)
+ {
+ var methodOrMember = typeof(TFactory).GetAllMembers()
+ .SingleOrDefault(m => m.Name == methodOrMemberName)
+ .ThrowIfNull();
+ return Of(methodOrMember);
+ }
+
/// Pretty prints wrapped method. Printed string.
public override string ToString()
{
@@ -3229,7 +3743,11 @@ public override string ToString()
.Append("::").Append(ConstructorOrMethodOrMember).ToString();
}
- private static FactoryMethodSelector Constructor(bool mostResolvable = false, bool includeNonPublic = false)
+ // todo: may be add the @default constructor option
+ /// Easy way to specify non-public or / and most resolvable constructor.
+ ///
+ /// Constructor or null if not found.
+ public static FactoryMethodSelector Constructor(bool mostResolvable = false, bool includeNonPublic = false)
{
return request =>
{
@@ -3247,10 +3765,10 @@ private static FactoryMethodSelector Constructor(bool mostResolvable = false, bo
.Select(c => new { Ctor = c, Params = c.GetParameters() })
.OrderByDescending(x => x.Params.Length);
- var rules = request.Container.Rules;
- var selector = rules.OverrideRegistrationMade
- ? rules.Parameters.OverrideWith(request.Made.Parameters)
- : request.Made.Parameters.OverrideWith(rules.Parameters);
+ var containerRules = request.Rules;
+ var selector = containerRules.OverrideRegistrationMade
+ ? request.Made.Parameters.OverrideWith(containerRules.Parameters)
+ : containerRules.Parameters.OverrideWith(request.Made.Parameters);
var parameterSelector = selector(request);
@@ -3263,10 +3781,13 @@ private static FactoryMethodSelector Constructor(bool mostResolvable = false, bo
}
else
{
- // For Func with arguments,
- // match constructor should contain all input arguments and
+ // For Func with arguments,
+ // match constructor should contain all input arguments and
// the rest should be resolvable.
- var funcType = request.ParentOrWrapper.ServiceType;
+ var funcType = !request.RawParent.IsEmpty
+ ? request.RawParent.ServiceType
+ : request.PreResolveParent.ServiceType;
+
var funcArgs = funcType.GetGenericParamsAndArgs();
var inputArgCount = funcArgs.Length - 1;
@@ -3281,8 +3802,8 @@ private static FactoryMethodSelector Constructor(bool mostResolvable = false, bo
var inputArgIndex = funcArgs.IndexOf(p.ParameterType);
if (inputArgIndex == -1 || inputArgIndex == inputArgCount ||
(matchedIndecesMask & inputArgIndex << 1) != 0)
- // input argument was already matched by another parameter
- return false;
+ // input argument was already matched by another parameter
+ return false;
matchedIndecesMask |= inputArgIndex << 1;
return true;
})).All(p => IsResolvableParameter(p, parameterSelector, request));
@@ -3297,12 +3818,12 @@ private static FactoryMethodSelector Constructor(bool mostResolvable = false, bo
/// Searches for public constructor with most resolvable parameters or throws if not found.
/// Works both for resolving service and Func{TArgs..., TService}
- public static readonly FactoryMethodSelector ConstructorWithResolvableArguments =
+ public static readonly FactoryMethodSelector ConstructorWithResolvableArguments =
Constructor(mostResolvable: true);
/// Searches for constructor (including non public ones) with most resolvable parameters or throws if not found.
/// Works both for resolving service and Func{TArgs..., TService}
- public static readonly FactoryMethodSelector ConstructorWithResolvableArgumentsIncludingNonPublic =
+ public static readonly FactoryMethodSelector ConstructorWithResolvableArgumentsIncludingNonPublic =
Constructor(mostResolvable: true, includeNonPublic: true);
/// Checks that parameter is selected on requested path and with provided parameter selector.
@@ -3312,12 +3833,12 @@ private static FactoryMethodSelector Constructor(bool mostResolvable = false, bo
Func parameterSelector, Request request)
{
var parameterServiceInfo = parameterSelector(parameter) ?? ParameterServiceInfo.Of(parameter);
- var parameterRequest = request.Push(parameterServiceInfo.WithDetails(ServiceDetails.IfUnresolvedReturnDefault, request));
+ var parameterRequest = request.Push(parameterServiceInfo.WithDetails(ServiceDetails.IfUnresolvedReturnDefault, null));
if (parameterServiceInfo.Details.HasCustomValue)
{
var customValue = parameterServiceInfo.Details.CustomValue;
- return customValue == null
+ return customValue == null
|| customValue.GetType().IsAssignableTo(parameterRequest.ServiceType);
}
@@ -3349,7 +3870,7 @@ public class Made
/// That's mean the whole made become context based which affects caching
public bool HasCustomDependencyValue { get; private set; }
- /// Specifies how constructor parameters should be resolved:
+ /// Specifies how constructor parameters should be resolved:
/// parameter service key and type, throw or return default value if parameter is unresolved.
public ParameterSelector Parameters { get; private set; }
@@ -3383,7 +3904,7 @@ public class Made
/// Specifies injections rules for Constructor, Parameters, Properties and Fields. If no rules specified returns rules.
/// (optional) (optional) (optional)
/// New injection rules or .
- public static Made Of(FactoryMethodSelector factoryMethod = null,
+ public static Made Of(FactoryMethodSelector factoryMethod = null,
ParameterSelector parameters = null, PropertiesAndFieldsSelector propertiesAndFields = null)
{
return factoryMethod == null && parameters == null && propertiesAndFields == null
@@ -3399,7 +3920,7 @@ public class Made
{
var methodReturnType = factoryMethod.ConstructorOrMethodOrMember.GetReturnTypeOrDefault();
- // Normalizes open-generic type to open-generic definition,
+ // Normalizes open-generic type to open-generic definition,
// because for base classes and return types it may not be the case.
if (methodReturnType != null && methodReturnType.IsOpenGeneric())
methodReturnType = methodReturnType.GetGenericTypeDefinition();
@@ -3415,7 +3936,7 @@ public class Made
public static Made Of(MemberInfo factoryMethodOrMember, ServiceInfo factoryInfo = null,
ParameterSelector parameters = null, PropertiesAndFieldsSelector propertiesAndFields = null)
{
- return Of(DryIoc.FactoryMethod.Of(factoryMethodOrMember, factoryInfo));
+ return Of(DryIoc.FactoryMethod.Of(factoryMethodOrMember, factoryInfo), parameters, propertiesAndFields);
}
/// Creates factory specification with method or member selector based on request.
@@ -3443,7 +3964,7 @@ public class Made
/// Defines factory method using expression of constructor call (with properties), or static method call.
/// Type with constructor or static method.
- /// Expression tree with call to constructor with properties:
+ /// Expression tree with call to constructor with properties:
/// new Car(Arg.Of()) { Color = Arg.Of("CarColor") }]]>
/// or static method call Car.Create(Arg.Of())]]>
/// (optional) Primitive custom values for dependencies.
@@ -3458,7 +3979,7 @@ public class Made
/// Defines creation info from factory method call Expression without using strings.
/// You can supply any/default arguments to factory method, they won't be used, it is only to find the .
/// Factory type. Factory product type.
- /// Returns or resolves factory instance.
+ /// Returns or resolves factory instance.
/// Method, property or field expression returning service.
/// (optional) Primitive custom values for dependencies.
/// New Made specification.
@@ -3679,7 +4200,7 @@ public sealed class TypedMade : Made
var hasPrevArg = false;
var argExprs = methodCallExpr.Arguments;
- if (argExprs.Count == 2 &&
+ if (argExprs.Count == 2 &&
argExprs[0].Type == typeof(string) &&
argExprs[1].Type != typeof(IfUnresolved)) // matches the Of overload for metadata
{
@@ -3735,7 +4256,7 @@ private static object GetArgExpressionValueOrThrow(Expression arg)
return memberField.GetValue(memberOwner.Value);
}
}
-
+
return Throw.For(Error.UnexpectedExpressionInsteadOfConstant, arg);
}
@@ -3822,7 +4343,7 @@ public static class Arg
/// Ignored.
public static TRequired Of(TRequired defaultValue, IfUnresolved ifUnresolved, object serviceKey) { return default(TRequired); }
- /// Specifies argument index starting from 0 to use corresponding custom value factory,
+ /// Specifies argument index starting from 0 to use corresponding custom value factory,
/// similar to String.Format "{0}, {1}, etc" .
/// Type of dependency. Difference from actual parameter type is ignored.
/// Argument index starting from 0 Ignored.
@@ -3852,7 +4373,7 @@ public static class Registrator
/// Any implementation, e.g. .
/// The service type to register.
/// Implementation type. Concrete and open-generic class are supported.
- /// (optional) implementation, e.g. .
+ /// (optional) implementation, e.g. .
/// Default value means no reuse, aka Transient.
/// (optional) specifies .
/// (optional) Factory setup, by default is ( )
@@ -3933,7 +4454,7 @@ public static class Registrator
IfAlreadyRegistered ifAlreadyRegistered = IfAlreadyRegistered.AppendNotKeyed,
object serviceKey = null) where TMadeResult : TService
{
- var factory = new ReflectionFactory(null, reuse, made, setup);
+ var factory = new ReflectionFactory(default(Type), reuse, made, setup);
registrator.Register(factory, typeof(TService), serviceKey, ifAlreadyRegistered, isStaticallyChecked: true);
}
@@ -3959,9 +4480,10 @@ public static class Registrator
/// Concrete or open-generic implementation type.
public delegate void RegisterManyAction(IRegistrator r, Type[] serviceTypes, Type implType);
+ // todo: perf: Add optional @isStaticallyChecked to skip check for implemented types.
/// Registers many service types with the same implementation.
/// Registrator/Container
- /// 1 or more service types.
+ /// 1 or more service types.
/// Should implement service types. Will throw if not.
/// (optional) (optional) How to create implementation instance.
/// (optional) (optional) By default
@@ -3997,7 +4519,7 @@ public static class Registrator
/// Returns only those types that could be used as service types of . It means that
/// for open-generic its service type should supply all type arguments
/// Used by RegisterMany method.
- /// Source type: may be concrete, abstract or generic definition.
+ /// Source type: may be concrete, abstract or generic definition.
/// (optional) Include non public service types.
/// Array of types or empty.
public static Type[] GetImplementedServiceTypes(this Type type, bool nonPublicServiceTypes = false)
@@ -4006,7 +4528,7 @@ public static Type[] GetImplementedServiceTypes(this Type type, bool nonPublicSe
var selectedServiceTypes = serviceTypes.Where(t =>
(nonPublicServiceTypes || t.IsPublicOrNestedPublic()) &&
- // using Namespace+Name instead of FullName because later is null for generic type definitions
+ // using Namespace+Name instead of FullName because latter is null for generic type definitions
ExcludedGeneralPurposeServiceTypes.IndexOf((t.Namespace + "." + t.Name).Split('`')[0]) == -1);
if (type.IsGenericDefinition())
@@ -4023,14 +4545,21 @@ public static Type[] GetImplementedServiceTypes(this Type type, bool nonPublicSe
/// Types.
public static IEnumerable GetImplementationTypes(this Assembly assembly)
{
- return Portable.GetAssemblyTypes(assembly)
- .Where(type => type.IsClass() && !type.IsAbstract() && !type.IsCompilerGenerated());
+ return Portable.GetAssemblyTypes(assembly).Where(IsImplementationType);
+ }
+
+ /// Checks if type can be used as implementation type for reflection factory,
+ /// and therefore registered to container. Usually used to discover implementation types from assembly.
+ /// Type to check. True if implementation type.
+ public static bool IsImplementationType(this Type type)
+ {
+ return type.IsClass() && !type.IsAbstract() && !type.IsCompilerGenerated();
}
/// Registers many implementations with their auto-figured service types.
/// Registrator/Container to register with.
/// Implementation type provider.
- /// (optional) User specified registration action:
+ /// (optional) User specified registration action:
/// may be used to filter registrations or specify non-default registration options, e.g. Reuse or ServiceKey, etc.
/// (optional) Include non public service types.
public static void RegisterMany(this IRegistrator registrator, IEnumerable implTypes, RegisterManyAction action,
@@ -4082,7 +4611,7 @@ public static IEnumerable GetImplementationTypes(this Assembly assembly)
/// (optional) Allow to select constructor/method to create service, specify how to inject its parameters and properties/fields.
/// (optional) Factory setup, by default is , check class for available setups.
/// (optional) policy to deal with case when service with such type and name is already registered.
- /// (optional) Condition to select only specific service type to register.
+ /// (optional) Condition to select only specific service type to register.
/// (optional) Include non public service types.
/// (optional) service key (name). Could be of any of type with overridden and .
public static void RegisterMany(this IRegistrator registrator,
@@ -4108,7 +4637,7 @@ public static IEnumerable GetImplementationTypes(this Assembly assembly)
/// (optional) implementation, e.g. . Default value means no reuse, aka Transient.
/// (optional) Factory setup, by default is , check class for available setups.
/// (optional) policy to deal with case when service with such type and name is already registered.
- /// (optional) Condition to select only specific service type to register.
+ /// (optional) Condition to select only specific service type to register.
/// (optional) Include non public service types.
/// (optional) service key (name). Could be of any of type with overridden and .
public static void RegisterMany(this IRegistrator registrator, Made.TypedMade made,
@@ -4124,7 +4653,7 @@ public static IEnumerable GetImplementationTypes(this Assembly assembly)
/// Registers many implementations with their auto-figured service types.
/// Registrator/Container to register with.
/// Assemblies with implementation/service types to register.
- /// (optional) User specified registration action:
+ /// (optional) User specified registration action:
/// may be used to filter registrations or specify non-default registration options, e.g. Reuse or ServiceKey, etc.
/// (optional) Include non public service types.
public static void RegisterMany(this IRegistrator registrator, IEnumerable implTypeAssemblies,
@@ -4134,6 +4663,8 @@ public static IEnumerable GetImplementationTypes(this Assembly assembly)
registrator.RegisterMany(implTypes, action, nonPublicServiceTypes);
}
+ // todo: Add overload to specify list of service types to support case when I know contracts (service types) and provide impl locations (assemblies)
+ // and do not care about concrete implementation which is good principle.
/// Registers many implementations with their auto-figured service types.
/// Registrator/Container to register with.
/// Assemblies with implementation/service types to register.
@@ -4166,9 +4697,9 @@ public static IEnumerable GetImplementationTypes(this Assembly assembly)
/// (optional) policy to deal with case when service with such type and name is already registered.
/// (optional). Could be of any of type with overridden and .
/// IMPORTANT: The method should be used as the last resort only! Though powerful it is a black-box for container,
- /// which prevents diagnostics, plus it is easy to get memory leaks (due variables captured in delegate closure),
+ /// which prevents diagnostics, plus it is easy to get memory leaks (due variables captured in delegate closure),
/// and impossible to use in compile-time scenarios.
- /// Consider using instead:
+ /// Consider using instead:
/// (Made.Of(() => new Car(Arg.Of())))]]>
.
///
public static void RegisterDelegate(this IRegistrator registrator, Func factoryDelegate,
@@ -4190,9 +4721,9 @@ public static IEnumerable GetImplementationTypes(this Assembly assembly)
/// (optional) policy to deal with case when service with such type and name is already registered.
/// (optional) Could be of any of type with overridden and .
/// IMPORTANT: The method should be used as the last resort only! Though powerful it is a black-box for container,
- /// which prevents diagnostics, plus it is easy to get memory leaks (due variables captured in delegate closure),
+ /// which prevents diagnostics, plus it is easy to get memory leaks (due variables captured in delegate closure),
/// and impossible to use in compile-time scenarios.
- /// Consider using instead:
+ /// Consider using instead:
/// (Made.Of(() => new Car(Arg.Of())))]]>
.
///
public static void RegisterDelegate(this IRegistrator registrator, Type serviceType, Func factoryDelegate,
@@ -4221,7 +4752,7 @@ public static IEnumerable GetImplementationTypes(this Assembly assembly)
// unique key to binds decorator factory and decorator registrations
var factoryKey = new object();
- registrator.RegisterDelegate(_ =>
+ registrator.RegisterDelegate(_ =>
new DecoratorDelegateFactory(getDecorator),
serviceKey: factoryKey);
@@ -4258,10 +4789,15 @@ public TDecoratee Decorate(TDecoratee decoratee, IResolver resolver)
Throw.If(reuse is ResolutionScopeReuse, Error.ResolutionScopeIsNotSupportedForRegisterInstance, instance);
reuse = reuse ?? Reuse.Singleton;
- var setup = Setup.Default;
+ var scopedReuse = reuse as CurrentScopeReuse;
+ var scope = scopedReuse != null
+ ? ((IScopeAccess)container).GetCurrentNamedScope(scopedReuse.Name, throwIfNotFound: true)
+ : ((Container)container).SingletonScope;
+
+ var setup = _defaultInstanceSetup;
if (preventDisposal)
{
- instance = new[] {instance};
+ instance = new[] { instance };
setup = _preventDisposableInstanceSetup;
}
if (weaklyReferenced)
@@ -4289,7 +4825,7 @@ public TDecoratee Decorate(TDecoratee decoratee, IResolver resolver)
return;
}
- var canReuseAlreadyRegisteredFactory =
+ var canReuseAlreadyRegisteredFactory =
factory != null && factory.Reuse == reuse && factory.Setup == setup;
if (canReuseAlreadyRegisteredFactory)
factory.ReplaceInstance(instance);
@@ -4299,15 +4835,17 @@ public TDecoratee Decorate(TDecoratee decoratee, IResolver resolver)
if (!canReuseAlreadyRegisteredFactory)
container.Register(factory, serviceType, serviceKey, ifAlreadyRegistered, isStaticallyChecked: false);
- var request = container.EmptyRequest.Push(serviceType, serviceKey);
- reuse.GetScopeOrDefault(request)
- .ThrowIfNull(Error.NoMatchingScopeWhenRegisteringInstance, instance, reuse)
- .SetOrAdd(reuse.GetScopedItemIdOrSelf(factory.FactoryID, request), instance);
+ scope.SetOrAdd(scope.GetScopedItemIdOrSelf(factory.FactoryID), instance);
}
- private static readonly Setup _weaklyReferencedInstanceSetup = Setup.With(weaklyReferenced: true);
- private static readonly Setup _preventDisposableInstanceSetup = Setup.With(preventDisposal: true);
- private static readonly Setup _weaklyReferencedAndPreventDisposableInstanceSetup = Setup.With(weaklyReferenced: true, preventDisposal: true);
+ private static readonly Setup _defaultInstanceSetup =
+ Setup.With(asResolutionCall: true);
+ private static readonly Setup _weaklyReferencedInstanceSetup =
+ Setup.With(weaklyReferenced: true, asResolutionCall: true);
+ private static readonly Setup _preventDisposableInstanceSetup =
+ Setup.With(preventDisposal: true, asResolutionCall: true);
+ private static readonly Setup _weaklyReferencedAndPreventDisposableInstanceSetup =
+ Setup.With(weaklyReferenced: true, preventDisposal: true, asResolutionCall: true);
// todo: v3: remove
/// Obsolete: use
@@ -4319,7 +4857,7 @@ public TDecoratee Decorate(TDecoratee decoratee, IResolver resolver)
preventDisposal, weaklyReferenced, serviceKey);
}
- /// Stores the externally created instance into open scope or singleton,
+ /// Stores the externally created instance into open scope or singleton,
/// replacing the existing registration and instance if any.
/// Specified instance type. May be a base type or interface of instance actual type.
/// Container to register
@@ -4333,7 +4871,7 @@ public TDecoratee Decorate(TDecoratee decoratee, IResolver resolver)
container.UseInstance(typeof(TService), instance, preventDisposal, weaklyReferenced, serviceKey);
}
- /// Stores the externally created instance into open scope or singleton,
+ /// Stores the externally created instance into open scope or singleton,
/// replacing the existing registration and instance if any.
/// Container to register
/// Runtime service type to register instance with
@@ -4347,62 +4885,30 @@ public TDecoratee Decorate(TDecoratee decoratee, IResolver resolver)
if (instance != null)
instance.ThrowIfNotOf(serviceType, Error.RegisteringInstanceNotAssignableToServiceType);
- var scope = container.GetCurrentScope();
- var reuse = scope != null ? Reuse.InCurrentNamedScope(scope.Name) : Reuse.Singleton;
-
- var setup = Setup.Default;
if (preventDisposal)
- {
instance = new[] { instance };
- setup = _preventDisposableInstanceSetup;
- }
+
if (weaklyReferenced)
- {
instance = new WeakReference(instance);
- setup = preventDisposal
- ? _weaklyReferencedAndPreventDisposableInstanceSetup
- : _weaklyReferencedInstanceSetup;
- }
-
- var factories = container.GetAllServiceFactories(serviceType);
- if (serviceKey != null)
- factories = factories.Where(f => serviceKey.Equals(f.Key));
-
- InstanceFactory factory = null;
-
- // Replace the single factory
- var factoriesList = factories.ToArray();
- if (factoriesList.Length == 1)
- factory = factoriesList[0].Value as InstanceFactory;
-
- if (factory != null && factory.Reuse == reuse && factory.Setup == setup)
- factory.ReplaceInstance(instance);
- else
- {
- factory = new InstanceFactory(instance, reuse, setup);
- container.Register(factory, serviceType, serviceKey, IfAlreadyRegistered.Replace, isStaticallyChecked: false);
- }
- var request = container.EmptyRequest.Push(serviceType, serviceKey);
- reuse.GetScopeOrDefault(request)
- .ThrowIfNull(Error.NoMatchingScopeWhenRegisteringInstance, instance, reuse)
- .SetOrAdd(reuse.GetScopedItemIdOrSelf(factory.FactoryID, request), instance);
+ // todo: v3: remove the hack
+ ((Container)container).UseInstanceInternal(serviceType, instance, serviceKey);
}
/// Registers initializing action that will be called after service is resolved just before returning it to caller.
/// Check example below for using initializer to automatically subscribe to singleton event aggregator.
- /// You can register multiple initializers for single service.
- /// Or you can register initializer for type to be applied for all services and use
+ /// You can register multiple initializers for single service.
+ /// Or you can register initializer for type to be applied for all services and use
/// to filter target services.
/// Any type implemented by requested service type including service type itself and object type.
/// Usually is object.
- /// Delegate with object and
+ /// Delegate with object and
/// to resolve additional services required by initializer.
/// (optional) Additional condition to select required target.
/// (Reuse.Singleton);
/// container.Register();
- ///
+ ///
/// // Registers initializer for all subscribers implementing ISubscriber.
/// container.RegisterInitiliazer((s, r) => r.Resolve().Subscribe(s));
/// ]]>
@@ -4411,14 +4917,13 @@ public TDecoratee Decorate(TDecoratee decoratee, IResolver resolver)
{
initialize.ThrowIfNull();
registrator.Register(
- made: Made.Of(r => _initializerMethod.MakeGenericMethod(typeof(TTarget), r.ServiceType),
+ made: Made.Of(r => _initializerMethod.MakeGenericMethod(typeof(TTarget), r.ServiceType),
parameters: Parameters.Of.Type(_ => initialize)),
- setup: Setup.DecoratorWith(useDecorateeReuse: true, condition:
- r => r.GetKnownImplementationOrServiceType().IsAssignableTo(typeof(TTarget))
- && (condition == null || condition(r))));
+ setup: Setup.DecoratorWith(useDecorateeReuse: true,
+ condition: r => r.ServiceType.IsAssignableTo(typeof(TTarget)) && (condition == null || condition(r))));
}
- private static readonly MethodInfo _initializerMethod =
+ private static readonly MethodInfo _initializerMethod =
typeof(Registrator).GetSingleMethodOrNull("Initializer", includeNonPublic: true).ThrowIfNull();
internal static TService Initializer(
@@ -4430,17 +4935,17 @@ public TDecoratee Decorate(TDecoratee decoratee, IResolver resolver)
/// Registers dispose action for reused target service.
/// Target service type.
- /// Registrator to use.
+ /// Registrator to use.
/// Actual dispose action to be invoke when scope is disposed.
/// (optional) Additional way to identify the service.
- public static void RegisterDisposer(this IRegistrator registrator,
+ public static void RegisterDisposer(this IRegistrator registrator,
Action dispose, Func condition = null)
{
dispose.ThrowIfNull();
var disposerKey = new object();
- registrator.RegisterDelegate(_ => new Disposer(dispose),
+ registrator.RegisterDelegate(_ => new Disposer(dispose),
serviceKey: disposerKey,
setup: Setup.With(useParentReuse: true));
@@ -4486,7 +4991,8 @@ public void Dispose()
/// Returns true if is registered in container or its open generic definition is registered in container.
/// Usually to explore or any other implementation.
/// The type of the registered service.
- /// (optional) Could be of any of type with overridden and .
+ /// (optional) Identifies registration via service key.
+ /// Not provided or null service key means to check the alone with any service key.
/// (optional) factory type to lookup, by default.
/// (optional) condition to specify what registered factory do you expect.
/// True if is registered, false - otherwise.
@@ -4499,7 +5005,8 @@ public void Dispose()
/// Returns true if type is registered in container or its open generic definition is registered in container.
/// The type of service.
/// Usually to explore or any other implementation.
- /// (optional) Could be of any of type with overridden and .
+ /// (optional) Identifies registration via service key.
+ /// Not provided or null service key means to check the alone with any service key.
/// (optional) factory type to lookup, by default.
/// (optional) condition to specify what registered factory do you expect.
/// True if name="serviceType"/> is registered, false - otherwise.
@@ -4669,20 +5176,20 @@ public static TService Resolve(this IResolver resolver, Type requiredS
return resolver.ResolveMany(serviceType, behavior, serviceKey);
}
- internal static Expression CreateResolutionExpression(Request request, bool openResolutionScope = false)
+ internal static Expression CreateResolutionExpression(Request request,
+ bool openResolutionScope = false, bool isRuntimeDependency = false)
{
- PopulateDependencyResolutionCallExpressions(request, openResolutionScope);
+ request.ContainsNestedResolutionCall = true;
var container = request.Container;
- var serviceType = request.ServiceType;
- var serviceKey = request.ServiceKey;
- var newPreResolveParent = request.ParentOrWrapper;
+ if (!isRuntimeDependency && container.Rules.DependencyResolutionCallExpressions != null)
+ PopulateDependencyResolutionCallExpressions(request, openResolutionScope);
- var serviceTypeExpr = Expression.Constant(serviceType, typeof(Type));
+ var serviceTypeExpr = Expression.Constant(request.ServiceType, typeof(Type));
var ifUnresolvedExpr = Expression.Constant(request.IfUnresolved == IfUnresolved.ReturnDefault, typeof(bool));
var requiredServiceTypeExpr = Expression.Constant(request.RequiredServiceType, typeof(Type));
- var serviceKeyExpr = container.GetOrAddStateItemExpression(serviceKey, typeof(object));
+ var serviceKeyExpr = container.GetOrAddStateItemExpression(request.ServiceKey, typeof(object));
// first ensure that we have parent scope if any to propagate it across resolution call boundaries
var scopeExpr = Container.GetResolutionScopeExpression(request);
@@ -4693,52 +5200,51 @@ internal static Expression CreateResolutionExpression(Request request, bool open
var actualServiceTypeExpr = Expression.Constant(request.GetActualServiceType(), typeof(Type));
var scopeCtor = typeof(Scope).GetSingleConstructorOrNull().ThrowIfNull();
scopeExpr = Expression.New(scopeCtor, scopeExpr,
- Expression.New(typeof(KV).GetSingleConstructorOrNull().ThrowIfNull(),
+ Expression.New(typeof(KV).GetSingleConstructorOrNull().ThrowIfNull(),
actualServiceTypeExpr, serviceKeyExpr));
}
+ var resolverExpr = Container.GetResolverExpr(request);
+
// Only parent is converted to be passed to Resolve (the current request is formed by rest of Resolve parameters)
- var preResolveParentExpr = container.RequestInfoToExpression(newPreResolveParent);
+ var parentRequestInfo = request.RawParent.IsEmpty ? request.PreResolveParent : request.RawParent.RequestInfo;
+ var preResolveParentExpr = container.RequestInfoToExpression(parentRequestInfo);
var resolveCallExpr = Expression.Call(
- Container.ResolverExpr, "Resolve", ArrayTools.Empty(),
- serviceTypeExpr, serviceKeyExpr, ifUnresolvedExpr, requiredServiceTypeExpr,
+ resolverExpr, "Resolve", ArrayTools.Empty(),
+ serviceTypeExpr, serviceKeyExpr, ifUnresolvedExpr, requiredServiceTypeExpr,
preResolveParentExpr, scopeExpr);
- var resolveExpr = Expression.Convert(resolveCallExpr, serviceType);
- return resolveExpr;
+ return Expression.Convert(resolveCallExpr, request.ServiceType);
}
private static void PopulateDependencyResolutionCallExpressions(Request request, bool openResolutionScope)
{
- var serviceType = request.ServiceType;
- var serviceKey = request.ServiceKey;
- var newPreResolveParent = request.ParentOrWrapper;
-
- var container = request.Container;
-
// Actually calls nested Resolution Call and stores produced expression in collection:
// - if the collection to accumulate call expressions is defined and:
// - Resolve call is the first nested in chain
// - Resolve call is not repeated for recursive dependency, e.g. new A(new Lazy r.Resolve()>) and new B(new A())
- if (container.Rules.DependencyResolutionCallExpressions != null &&
- (request.PreResolveParent.IsEmpty ||
- !request.PreResolveParent.EqualsWithoutParent(newPreResolveParent)))
+ var preResolveParent = request.PreResolveParent;
+ if (preResolveParent.IsEmpty ||
+ !request.RawParent.IsEmpty && !preResolveParent.EqualsWithoutParent(request.RawParent))
{
+ var serviceType = request.ServiceType;
+ var serviceKey = request.ServiceKey;
+
// Create scope for first nesting level or where corresponding setting is saying so
var scope = request.Scope;
if (scope == null || openResolutionScope)
scope = new Scope(scope, new KV(serviceType, serviceKey));
- var newRequest = request.Container.EmptyRequest.Push(serviceType, serviceKey,
+ var newRequest = Request.Create(request.Container, serviceType, serviceKey,
request.IfUnresolved, request.RequiredServiceType, scope,
- newPreResolveParent);
+ request.ParentOrWrapper);
var factory = request.Container.ResolveFactory(newRequest);
var factoryExpr = factory == null ? null : factory.GetExpressionOrDefault(newRequest);
if (factoryExpr != null)
- container.Rules.DependencyResolutionCallExpressions.Swap(
- expr => expr.AddOrUpdate(newRequest.RequestInfo, factoryExpr));
+ request.Rules.DependencyResolutionCallExpressions.Swap(it =>
+ it.AddOrUpdate(newRequest.RequestInfo, factoryExpr));
}
}
}
@@ -4752,7 +5258,7 @@ public enum ResolveManyBehavior
AsFixedArray
}
- /// Provides information required for service resolution: service type,
+ /// Provides information required for service resolution: service type,
/// and optional : key, what to do if service unresolved, and required service type.
public interface IServiceInfo
{
@@ -4790,7 +5296,7 @@ public class ServiceDetails
if (defaultValue != null && ifUnresolved == IfUnresolved.Throw)
ifUnresolved = IfUnresolved.ReturnDefault;
- return new ServiceDetails(requiredServiceType, ifUnresolved,
+ return new ServiceDetails(requiredServiceType, ifUnresolved,
serviceKey, metadataKey, metadata,
defaultValue, hasCustomValue: false);
}
@@ -4842,12 +5348,15 @@ public override string ToString()
s.Append("{RequiredServiceType=").Print(RequiredServiceType);
if (ServiceKey != null)
(s.Length == 0 ? s.Append('{') : s.Append(", ")).Append("ServiceKey=").Print(ServiceKey, "\"");
+ if (MetadataKey != null || Metadata != null)
+ (s.Length == 0 ? s.Append('{') : s.Append(", "))
+ .Append("Metadata=").Append(new KeyValuePair(MetadataKey, Metadata));
if (IfUnresolved != IfUnresolved.Throw)
(s.Length == 0 ? s.Append('{') : s.Append(", ")).Append(IfUnresolved);
return (s.Length == 0 ? s : s.Append('}')).ToString();
}
- private ServiceDetails(Type requiredServiceType, IfUnresolved ifUnresolved,
+ private ServiceDetails(Type requiredServiceType, IfUnresolved ifUnresolved,
object serviceKey, string metadataKey, object metadata,
object value, bool hasCustomValue)
{
@@ -4864,50 +5373,51 @@ public override string ToString()
/// Contains tools for combining or propagating of independent of its concrete implementations.
public static class ServiceInfoTools
{
+ /// Creates service info with new type but keeping the details.
+ /// Source info. New service type.
+ /// New info.
+ public static IServiceInfo With(this IServiceInfo source, Type serviceType)
+ {
+ return source.Create(serviceType, source.Details);
+ }
+
// todo: Should be renamed or better to be removed, the whole operation should be hidden behind abstraction
+ // todo: Remove request parameter as it is not used anymore
/// Combines service info with details: the main task is to combine service and required service type.
/// Type of .
- /// Source info. Details to combine with info.
+ /// Source info. Details to combine with info.
/// Owner request. Original source or new combined info.
public static T WithDetails(this T serviceInfo, ServiceDetails details, Request request)
where T : IServiceInfo
{
- return WithRequiredServiceType(serviceInfo, details, request);
+ details = details ?? ServiceDetails.Default;
+ var sourceDetails = serviceInfo.Details;
+ if (!details.HasCustomValue &&
+ sourceDetails != ServiceDetails.Default &&
+ sourceDetails != details)
+ {
+ var serviceKey = details.ServiceKey ?? sourceDetails.ServiceKey;
+ var metadataKey = details.MetadataKey ?? sourceDetails.MetadataKey;
+ var metadata = metadataKey == details.MetadataKey ? details.Metadata : sourceDetails.Metadata;
+ var defaultValue = details.DefaultValue ?? sourceDetails.DefaultValue;
+
+ details = ServiceDetails.Of(details.RequiredServiceType, serviceKey,
+ details.IfUnresolved, defaultValue, metadataKey, metadata);
+ }
+
+ return WithRequiredServiceType(serviceInfo, details, null);
}
internal static T WithRequiredServiceType(T serviceInfo, ServiceDetails details, Request request)
where T : IServiceInfo
{
var serviceType = serviceInfo.ServiceType;
- var requiredServiceType = details == null ? null : details.RequiredServiceType;
- if (requiredServiceType != null)
- {
- if (requiredServiceType == serviceType)
- {
- details = ServiceDetails.Of(null, details.ServiceKey, details.IfUnresolved);
- }
- else if (requiredServiceType.IsOpenGeneric())
- {
- // Checks that open-generic has corresponding closed service type to fill in its generic parameters.
- var openGenericServiceType = serviceType.GetGenericDefinitionOrNull();
- if (openGenericServiceType == null ||
- requiredServiceType != openGenericServiceType &&
- Array.IndexOf(requiredServiceType.GetImplementedTypes(), openGenericServiceType) == -1)
- Throw.It(Error.ServiceIsNotAssignableFromOpenGenericRequiredServiceType,
- openGenericServiceType, requiredServiceType, request);
- }
- else
- {
- var container = request.Container;
- var wrappedType = container.GetWrappedType(serviceType, null);
- if (wrappedType != null)
- {
- var wrappedRequiredType = container.GetWrappedType(requiredServiceType, null);
- wrappedType.ThrowIfNotImplementedBy(wrappedRequiredType, Error.WrappedNotAssignableFromRequiredType,
- request);
- }
- }
- }
+ var requiredServiceType = details.RequiredServiceType;
+
+ if (requiredServiceType != null && requiredServiceType == serviceType)
+ details = ServiceDetails.Of(null,
+ details.ServiceKey, details.IfUnresolved, details.DefaultValue,
+ details.MetadataKey, details.Metadata);
return serviceType == serviceInfo.ServiceType
&& (details == null || details == serviceInfo.Details)
@@ -4917,7 +5427,7 @@ internal static T WithRequiredServiceType(T serviceInfo, ServiceDetails detai
// todo: v3: remove unused @shouldInheritServiceKey parameter
// todo: v3: make @container parameter non optional
- /// Enables propagation/inheritance of info between dependency and its owner:
+ /// Enables propagation/inheritance of info between dependency and its owner:
/// for instance for wrappers.
/// Dependency info.
/// Dependency holder/owner info.
@@ -4926,11 +5436,9 @@ internal static T WithRequiredServiceType(T serviceInfo, ServiceDetails detai
/// required for
/// Either input dependency info, or new info with properties inherited from the owner.
public static IServiceInfo InheritInfoFromDependencyOwner(this IServiceInfo dependency, IServiceInfo owner,
- bool shouldInheritServiceKey = false, FactoryType ownerType = FactoryType.Service,
+ bool shouldInheritServiceKey = false, FactoryType ownerType = FactoryType.Service,
IContainer container = null)
{
- container = container.ThrowIfNull();
-
var ownerDetails = owner.Details;
if (ownerDetails == null || ownerDetails == ServiceDetails.Default)
return dependency;
@@ -4951,7 +5459,8 @@ internal static T WithRequiredServiceType(T serviceInfo, ServiceDetails detai
// propagate key and meta to the actual service
if (ownerType == FactoryType.Wrapper ||
- ownerType == FactoryType.Decorator && // propagate key only to decorated (and possibly wrapped) service
+ // for decorated dependency, but not for other decorator dependencies
+ ownerType == FactoryType.Decorator &&
container.GetWrappedType(serviceType, requiredServiceType).IsAssignableTo(owner.ServiceType))
{
if (serviceKey == null)
@@ -4966,7 +5475,7 @@ internal static T WithRequiredServiceType(T serviceInfo, ServiceDetails detai
}
}
- if (ownerType != FactoryType.Service && ownerRequiredServiceType != null &&
+ if (ownerType != FactoryType.Service && ownerRequiredServiceType != null &&
requiredServiceType == null) // if only dependency does not have its own
requiredServiceType = ownerRequiredServiceType;
@@ -4978,13 +5487,24 @@ internal static T WithRequiredServiceType(T serviceInfo, ServiceDetails detai
if (serviceType == requiredServiceType)
requiredServiceType = null;
- var serviceDetails = ServiceDetails.Of(requiredServiceType,
- serviceKey, ifUnresolved, dependencyDetails.DefaultValue,
+ var serviceDetails = ServiceDetails.Of(requiredServiceType,
+ serviceKey, ifUnresolved, dependencyDetails.DefaultValue,
metadataKey, metadata);
return dependency.Create(serviceType, serviceDetails);
}
+ /// Returns required service type if it is specified and assignable to service type,
+ /// otherwise returns service type.
+ /// The type to be used for lookup in registry.
+ public static Type GetActualServiceType(this IServiceInfo info)
+ {
+ var requiredServiceType = info.Details.RequiredServiceType;
+
+ return requiredServiceType != null && requiredServiceType.IsAssignableTo(info.ServiceType)
+ ? requiredServiceType : info.ServiceType;
+ }
+
/// Appends info string representation into provided builder.
/// String builder to print to. Info to print.
/// String builder with appended info.
@@ -4996,16 +5516,19 @@ public static StringBuilder Print(this StringBuilder s, IServiceInfo info)
}
}
- /// Represents custom or resolution root service info, there is separate representation for parameter,
+ /// Represents custom or resolution root service info, there is separate representation for parameter,
/// property and field dependencies.
public class ServiceInfo : IServiceInfo
{
+ /// Empty service info for convenience.
+ public static readonly IServiceInfo Empty = new ServiceInfo(null);
+
/// Creates info out of provided settings
/// Service type
/// (optional) If unresolved policy. Set to Throw if not specified.
/// (optional) Service key.
/// Created info.
- public static ServiceInfo Of(Type serviceType,
+ public static ServiceInfo Of(Type serviceType,
IfUnresolved ifUnresolved = IfUnresolved.Throw, object serviceKey = null)
{
return Of(serviceType, null, ifUnresolved, serviceKey);
@@ -5016,19 +5539,21 @@ public class ServiceInfo : IServiceInfo
/// Registered service type to search for.
/// (optional) If unresolved policy. Set to Throw if not specified.
/// (optional) Service key.
- /// (optional) Required metadata key
+ /// (optional) Required metadata key
/// Required metadata or the value if key passed.
/// Created info.
- public static ServiceInfo Of(Type serviceType, Type requiredServiceType,
+ public static ServiceInfo Of(Type serviceType, Type requiredServiceType,
IfUnresolved ifUnresolved = IfUnresolved.Throw, object serviceKey = null,
string metadataKey = null, object metadata = null)
{
serviceType.ThrowIfNull();
return serviceKey == null && requiredServiceType == null
&& metadataKey == null && metadata == null
- && ifUnresolved == IfUnresolved.Throw
+ ? (ifUnresolved == IfUnresolved.Throw
? new ServiceInfo(serviceType)
- : new WithDetails(serviceType,
+ : new WithDetails(serviceType,
+ ServiceDetails.IfUnresolvedReturnDefault))
+ : new WithDetails(serviceType,
ServiceDetails.Of(requiredServiceType, serviceKey, ifUnresolved, null, metadataKey, metadata));
}
@@ -5095,7 +5620,7 @@ private class TypedWithDetails : Typed
#endregion
}
- /// Provides for parameter,
+ /// Provides for parameter,
/// by default using parameter name as .
/// For parameter default setting is .
public class ParameterServiceInfo : IServiceInfo
@@ -5112,7 +5637,7 @@ public static ParameterServiceInfo Of(ParameterInfo parameter)
var defaultValue = isOptional ? parameter.DefaultValue : null;
var hasDefaultValue = defaultValue != null && parameter.ParameterType.IsTypeOf(defaultValue);
- return !isOptional
+ return !isOptional
? new ParameterServiceInfo(parameter)
: new WithDetails(parameter, !hasDefaultValue
? ServiceDetails.IfUnresolvedReturnDefault
@@ -5293,19 +5818,79 @@ public TypeWithDetails(FieldInfo field, Type serviceType, ServiceDetails details
#endregion
}
+ /// Memoized checks and conditions of two kinds: inherited down dependency chain and not.
+ [Flags]
+ public enum RequestFlags
+ {
+ /// Not inherited
+ TracksTransientDisposable = 1 << 1,
+ /// Not inherited
+ IsServiceCollection = 1 << 2,
+
+ /// Inherited
+ IsSingletonOrDependencyOfSingleton = 1 << 3,
+ /// Inherited
+ IsWrappedInFunc = 1 << 4,
+ /// Inherited
+ IsWrappedInFuncWithArgs = 1 << 5,
+ }
+
/// Contains resolution stack with information about resolved service and factory for it,
/// Additionally request contain weak reference to . That the all required information for resolving services.
/// Request implements interface on top of provided container, which could be use by delegate factories.
public sealed class Request
{
- /// Creates empty request associated with provided .
- /// Every resolution will start from this request by pushing service information into, and then resolving it.
- /// Reference to container issued the request. Could be changed later with method.
- /// New empty request.
- public static Request CreateEmpty(ContainerWeakRef container)
+ /// Not inherited down dependency chain.
+ public static readonly RequestFlags NotInheritedFlags
+ = RequestFlags.TracksTransientDisposable
+ | RequestFlags.IsServiceCollection;
+
+ // todo: v3: remove
+ /// Obsolete: replaced with /.
+ public static Request CreateEmpty(Container container)
{
- var resolverContext = new ResolverContext(container, container, null, RequestInfo.Empty);
- return new Request(resolverContext, parent: null, requestInfo: RequestInfo.Empty, made: null, funcArgs: null);
+ var resolverContext = new RequestContext(container, container, null, RequestInfo.Empty);
+ return new Request(resolverContext, null, ServiceInfo.Empty, null, null, null, default(RequestFlags));
+ }
+
+ private static readonly Request _empty = new Request(null, null, ServiceInfo.Empty, null, null, null, default(RequestFlags));
+
+ /// Creates empty request associated wit container.
+ /// The shared part of request is stored in request context. Pre-request info is also store once in shared context.
+ /// Associated container - part of request context.
+ /// Service type to resolve.
+ /// (optional) Service key to resolve.
+ /// (optional) How to handle unresolved service.
+ /// (optional) Actual registered or unwrapped service type to look for.
+ /// (optional) Pre-request info: resolution scope. // todo: v3: remove
+ /// (optional) Request info preceding Resolve call.
+ /// New request with provided info.
+ public static Request Create(IContainer container, Type serviceType,
+ object serviceKey = null, IfUnresolved ifUnresolved = IfUnresolved.Throw, Type requiredServiceType = null,
+ IScope scope = null, RequestInfo preResolveParent = null)
+ {
+ serviceType.ThrowIfNull()
+ .ThrowIf(serviceType.IsOpenGeneric(), Error.ResolvingOpenGenericServiceTypeIsNotPossible);
+
+ if (preResolveParent == null)
+ preResolveParent = RequestInfo.Empty;
+
+ var resolverContext = new RequestContext(container, (IScopeAccess)container, scope, preResolveParent);
+
+ IServiceInfo serviceInfo = ServiceInfo.Of(serviceType, requiredServiceType, ifUnresolved, serviceKey);
+
+ // inherit some flags and service details from parent (if any)
+ var flags = default(RequestFlags);
+ if (!preResolveParent.IsEmpty)
+ {
+ serviceInfo = serviceInfo.InheritInfoFromDependencyOwner(preResolveParent.ServiceInfo,
+ ownerType: preResolveParent.FactoryType, container: container);
+
+ // filter out not propagated flags
+ flags = preResolveParent.Flags & ~NotInheritedFlags;
+ }
+
+ return new Request(resolverContext, _empty, serviceInfo, null, null, null, flags);
}
/// Indicates that request is empty initial request: there is no in such a request.
@@ -5321,132 +5906,140 @@ public static Request CreateEmpty(ContainerWeakRef container)
public bool IsResolutionRoot { get { return IsResolutionCall && PreResolveParent.IsEmpty; } }
/// Request prior to Resolve call.
- public RequestInfo PreResolveParent { get { return _resolverContext.PreResolveParent; } }
-
- /// Returns true for the First Service in resolve call.
- public bool IsFirstNonWrapperInResolutionCall()
- {
- if (IsResolutionCall && RawParent.FactoryType != FactoryType.Wrapper)
- return true;
- var p = RawParent;
- while (!p.IsEmpty && p.FactoryType == FactoryType.Wrapper)
- p = p.RawParent;
- return p.IsEmpty;
- }
+ public RequestInfo PreResolveParent { get { return _requestContext.PreResolveParent; } }
/// Checks if request is wrapped in Func,
/// where Func is one of request immediate wrappers.
/// True if has Func ancestor.
public bool IsWrappedInFunc()
{
- return ParentOrWrapper.Enumerate()
- .Any(r => r.FactoryType == FactoryType.Wrapper && r.GetActualServiceType().IsFunc());
+ return (_flags & RequestFlags.IsWrappedInFunc) != 0;
}
- /// Checks if request has parent with service type of Func with arguments.
- /// If set indicate to check for immediate parent only,
+ /// Checks if request has parent with service type of Func with arguments.
+ /// If set indicate to check for immediate parent only,
/// otherwise will check whole parent chain.
/// True if has Func with arguments ancestor.
public bool IsWrappedInFuncWithArgs(bool immediateParent = false)
{
- var parent = ParentOrWrapper;
- return immediateParent
- ? parent.FactoryType == FactoryType.Wrapper && parent.GetActualServiceType().IsFuncWithArgs()
- : !parent.FirstOrEmpty(p =>
- p.FactoryType == FactoryType.Wrapper && p.GetActualServiceType().IsFuncWithArgs()).IsEmpty;
+ if ((_flags & RequestFlags.IsWrappedInFuncWithArgs) != 0)
+ {
+ if (!immediateParent)
+ return true; // skip other checks
+
+ // check if parent is not wrapped itself
+
+ // first run-time parent
+ if (!RawParent.IsEmpty)
+ return (RawParent._flags & RequestFlags.IsWrappedInFuncWithArgs) == 0;
+
+ // and if run-time parent does not exist then check the preresolve parent
+ if (!PreResolveParent.IsEmpty)
+ return (PreResolveParent.Flags & RequestFlags.IsWrappedInFuncWithArgs) != 0;
+ }
+
+ return false;
+ }
+
+ /// Indicates that requested service is transient disposable that should be tracked.
+ public bool TracksTransientDisposable
+ {
+ get { return (_flags & RequestFlags.TracksTransientDisposable) != 0; }
}
- /// Returns service parent of request, skipping intermediate wrappers if any.
- public RequestInfo Parent
+ /// Indicates the request is singleton or has singleton upper in dependency chain.
+ public bool IsSingletonOrDependencyOfSingleton
{
- get
- {
- return IsEmpty ? RequestInfo.Empty
- : RawParent.IsEmpty ? PreResolveParent.FirstOrEmpty(p => p.FactoryType != FactoryType.Wrapper)
- : RawParent.FactoryType != FactoryType.Wrapper ? RawParent.RequestInfo
- : RawParent.Parent;
- }
+ get { return (_flags & RequestFlags.IsSingletonOrDependencyOfSingleton) != 0; }
}
- /// Returns direct parent either it service or not (wrapper).
- /// In comparison with logical which returns first service parent skipping wrapper if any.
- public RequestInfo ParentOrWrapper
+ /// Gathers the info from resolved dependency graph.
+ /// If dependency injected asResolutionCall the whole graph is not cacheable (issue #416).
+ /// True if contains, false - otherwise or if not known.
+ public bool ContainsNestedResolutionCall
{
- get { return IsEmpty ? RequestInfo.Empty
- : RawParent.IsEmpty ? PreResolveParent
- : RawParent.RequestInfo; }
+ get { return _requestContext.ContainsNestedResolutionCall; }
+ set { if (value) _requestContext.ContainsNestedResolutionCall = true; }
}
- /// Gets first ancestor request which satisfies the condition,
- /// or empty if no ancestor is found.
- /// (optional) Condition to stop on.
- /// Request info of found parent.
- public RequestInfo Ancestor(Func condition = null)
+ /// Provides approximate nummber of dependencies in resolution graph (starting from Resolve method),
+ /// excluding registered delegates, instances, and wrappers.
+ public int DependencyCount { get { return _requestContext.DependencyCount; } }
+
+ /// Returns true if object grapth should be split due setting.
+ /// True if should be split, and false otherwise.
+ public bool ShouldSplitObjectGraph()
{
- var parent = ParentOrWrapper;
- return condition == null ? parent : parent.FirstOrEmpty(condition);
+ if (FactoryType != FactoryType.Service)
+ return false;
+ var maxObjectGraphSize = Rules.MaxObjectGraphSize;
+ return maxObjectGraphSize != -1 && DependencyCount > maxObjectGraphSize;
}
- /// Weak reference to container. May be replaced in request flowed from parent to child container.
- public ContainerWeakRef ContainerWeakRef { get { return _resolverContext.ContainerWeakRef; } }
+ /// Returns service parent of request, skipping intermediate wrappers if any.
+ public RequestInfo Parent { get { return RequestInfo.Parent; } }
+
+ /// Returns direct parent either it service or not (wrapper).
+ /// In comparison with logical which returns first service parent skipping wrapper if any.
+ public RequestInfo ParentOrWrapper { get { return RequestInfo.ParentOrWrapper; } }
- /// Provides access to container currently bound to request.
+ /// Provides access to container currently bound to request.
/// By default it is container initiated request by calling resolve method,
/// but could be changed along the way: for instance when resolving from parent container.
- public IContainer Container { get { return _resolverContext.ContainerWeakRef.Container; } }
+ public IContainer Container { get { return _requestContext.Container; } }
+
+ /// Shortcut to issued container rules.
+ public Rules Rules { get { return _requestContext.Container.Rules; } }
/// Separate from container because while container may be switched from parent to child, scopes should be from child/facade.
- public IScopeAccess Scopes { get { return _resolverContext.ScopesWeakRef.Scopes; } }
+ public IScopeAccess Scopes { get { return _requestContext.Scopes; } }
+
+ /// Singletons
+ public IScope SingletonScope { get { return ((Container)_requestContext.Container).SingletonScope; } }
+
+ /// Weak reference to container. May be replaced in request flowed from parent to child container.
+ public ContainerWeakRef ContainerWeakRef { get { return _requestContext.Container.ContainerWeakRef; } }
- /// Optionally associated resolution scope.
- public IScope Scope { get { return _resolverContext.Scope; } }
+ /// Resolution scope.
+ public IScope Scope { get { return _requestContext.Scope; } }
/// (optional) Made spec used for resolving request.
- public readonly Made Made;
+ public Made Made { get { return _factory == null ? null : _factory.Made; } }
/// User provided arguments: key tracks what args are still unused.
/// Mutable: tracks used arguments
public readonly KV FuncArgs;
- /// Counting nested levels. May be used to split object graph if level is too deep.
- public readonly int Level;
-
/// Requested service type.
- public Type ServiceType { get { return RequestInfo.ServiceType; } }
+ public Type ServiceType { get { return _serviceInfo.ServiceType; } }
/// Optional service key to identify service of the same type.
- public object ServiceKey { get { return RequestInfo.ServiceKey; } }
+ public object ServiceKey { get { return _serviceInfo.Details.ServiceKey; } }
/// Metadata key to find in metadata dictionary in resolved service.
- public string MetadataKey
- {
- get { return RequestInfo == null ? null : RequestInfo.MetadataKey; }
- }
+ public string MetadataKey { get { return _serviceInfo.Details.MetadataKey; } }
/// Metadata or the value (if key specified) to find in resolved service.
- public object Metadata
- {
- get { return RequestInfo == null ? null : RequestInfo.Metadata; }
- }
+ public object Metadata { get { return _serviceInfo.Details.Metadata; } }
/// Policy to deal with unresolved service.
- public IfUnresolved IfUnresolved { get { return RequestInfo.IfUnresolved; } }
+ public IfUnresolved IfUnresolved { get { return _serviceInfo.Details.IfUnresolved; } }
/// Required service type if specified.
- public Type RequiredServiceType { get { return RequestInfo.RequiredServiceType; } }
+ public Type RequiredServiceType { get { return _serviceInfo.Details.RequiredServiceType; } }
/// Implementation FactoryID.
/// The default unassigned value of ID is 0.
- public int FactoryID { get { return RequestInfo.FactoryID; } }
+ public int FactoryID { get { return _factory.ThrowIfNull().FactoryID; } }
/// Type of factory: Service, Wrapper, or Decorator.
- public FactoryType FactoryType { get { return RequestInfo.FactoryType; } }
+ public FactoryType FactoryType { get { return _factory.ThrowIfNull().FactoryType; } }
/// Service implementation type if known.
- public Type ImplementationType { get { return RequestInfo.ImplementationType; } }
+ public Type ImplementationType { get { return _factory.ThrowIfNull().ImplementationType; } }
/// Service reuse.
- public IReuse Reuse { get { return RequestInfo.Reuse; } }
+ public IReuse Reuse { get { return _reuse; } }
/// Relative number representing reuse lifespan.
public int ReuseLifespan { get { return Reuse == null ? 0 : Reuse.Lifespan; } }
@@ -5455,63 +6048,47 @@ public object Metadata
/// The type to be used for lookup in registry.
public Type GetActualServiceType()
{
- return RequestInfo.GetActualServiceType();
+ return _serviceInfo.GetActualServiceType();
}
/// Creates new request with provided info, and attaches current request as new request parent.
- /// Info about service to resolve. (optional) Resolution scope.
- /// (optional) Request info beyond/preceding Resolve call.
+ /// Info about service to resolve.
+ /// (optional) Pushed flags.
/// New request for provided info.
/// Existing/parent request should be resolved to factory ( ), before pushing info into it.
- public Request Push(IServiceInfo info, IScope scope = null, RequestInfo preResolveParent = null)
+ public Request Push(IServiceInfo info, RequestFlags flags = default(RequestFlags))
{
info.ThrowIfNull();
- if (IsEmpty)
- {
- preResolveParent = preResolveParent ?? RequestInfo.Empty;
- var resolverContext = _resolverContext.With(scope).With(preResolveParent);
+ if (_factory == null)
+ Throw.It(Error.PushingToRequestWithoutFactory, info, this);
- var requestInfo = Push(preResolveParent, info, Container);
+ var parentInfo = ChangeIfUnresolvedForCollectionServiceDependency();
- return new Request(resolverContext, this, requestInfo, made: null, funcArgs: null, level: 1);
- }
+ var inheritedInfo = info.InheritInfoFromDependencyOwner(parentInfo, ownerType: FactoryType, container: Container);
+ var inheritedFlags = _flags & ~NotInheritedFlags | flags;
- Throw.If(RequestInfo.FactoryID == 0, Error.PushingToRequestWithoutFactory, info.ThrowIfNull(), this);
-
- var inheritedRequestInfo = Push(RequestInfo, info, Container);
-
- return new Request(_resolverContext, this, inheritedRequestInfo, null, FuncArgs, Level + 1);
+ return new Request(_requestContext, this, inheritedInfo, null, null, FuncArgs, inheritedFlags);
}
- private static RequestInfo Push(RequestInfo parent, IServiceInfo serviceInfo, IContainer container)
+ // todo: v3: review and remove if possible
+ // if service info is dependency of service wrapped in collection,
+ // then change policy to collection policy
+ private IServiceInfo ChangeIfUnresolvedForCollectionServiceDependency()
{
- if (parent == null || parent.IsEmpty)
- return RequestInfo.Empty.Push(serviceInfo);
-
- // todo: v3: review and remove if possible
- // if service info is dependency of service wrapped in collection,
- // then change policy to collection policy
- var parentInfo = parent.ServiceInfo;
- if (parent.IfUnresolved == IfUnresolved.ReturnDefault)
+ if (IfUnresolved == IfUnresolved.ReturnDefault && FactoryType == FactoryType.Service)
{
- if (parent.FactoryType == FactoryType.Service)
- {
- var collectionParent = parent.Enumerate().FirstOrDefault(p =>
- p.FactoryType == FactoryType.Wrapper &&
- p.GetActualServiceType().IsSupportedCollectionType());
- if (collectionParent != null &&
- collectionParent.IfUnresolved == IfUnresolved.Throw)
- {
- parentInfo = ServiceInfo.Of(parentInfo.ServiceType,
- parentInfo.Details.RequiredServiceType, IfUnresolved.Throw, parentInfo.Details.ServiceKey);
- }
- }
+ var p = RequestInfo;
+ while (!p.IsEmpty &&
+ !(p.FactoryType == FactoryType.Wrapper && p.GetActualServiceType().IsSupportedCollectionType()))
+ p = p.ParentOrWrapper;
+
+ if (!p.IsEmpty && p.IfUnresolved == IfUnresolved.Throw)
+ return ServiceInfo.Of(ServiceType, RequiredServiceType, IfUnresolved.Throw, ServiceKey,
+ MetadataKey, Metadata);
}
- var newInfo = serviceInfo.InheritInfoFromDependencyOwner(parentInfo,
- ownerType: parent.FactoryType, container: container);
- return parent.Push(newInfo);
+ return _serviceInfo;
}
/// Composes service description into and calls Push.
@@ -5521,14 +6098,17 @@ private static RequestInfo Push(RequestInfo parent, IServiceInfo serviceInfo, IC
/// (optional) Registered/unwrapped service type to find.
/// (optional) Resolution scope.
/// (optional) Request info preceding Resolve call.
+ /// (optional) Sets some flags.
/// New request with provided info.
public Request Push(Type serviceType,
object serviceKey = null, IfUnresolved ifUnresolved = IfUnresolved.Throw, Type requiredServiceType = null,
- IScope scope = null, RequestInfo preResolveParent = null)
+ IScope scope = null, RequestInfo preResolveParent = null, RequestFlags flags = default(RequestFlags))
{
- serviceType.ThrowIfNull().ThrowIf(serviceType.IsOpenGeneric(), Error.ResolvingOpenGenericServiceTypeIsNotPossible);
- var details = ServiceDetails.Of(requiredServiceType, serviceKey, ifUnresolved);
- return Push(ServiceInfo.Of(serviceType).WithDetails(details, this), scope ?? Scope, preResolveParent);
+ serviceType.ThrowIfNull()
+ .ThrowIf(serviceType.IsOpenGeneric(), Error.ResolvingOpenGenericServiceTypeIsNotPossible);
+
+ var serviceInfo = ServiceInfo.Of(serviceType, requiredServiceType, ifUnresolved, serviceKey);
+ return Push(serviceInfo, flags);
}
/// Allow to switch current service info to new one: for instance it is used be decorators.
@@ -5536,119 +6116,232 @@ private static RequestInfo Push(RequestInfo parent, IServiceInfo serviceInfo, IC
/// New request with new service info but the same implementation and context.
public Request WithChangedServiceInfo(Func getInfo)
{
- var newRequestInfo = RequestInfo.With(getInfo);
- return new Request(_resolverContext, RawParent, newRequestInfo, Made, FuncArgs, Level);
+ return new Request(_requestContext, RawParent, getInfo(_serviceInfo), _factory, _reuse, FuncArgs, _flags);
}
/// Sets service key to passed value. Required for multiple default services to change null key to
/// actual
/// Key to set.
- public void ChangeServiceKey(object serviceKey) // NOTE: May be removed in future versions.
+ public void ChangeServiceKey(object serviceKey) // NOTE: May be removed in future versions.
{
- RequestInfo = RequestInfo.With(i =>
- {
- var details = i.Details;
- var newDetails = ServiceDetails.Of(details.RequiredServiceType, serviceKey, details.IfUnresolved, details.DefaultValue);
- return i.Create(i.ServiceType, newDetails);
- });
+ var i = _serviceInfo;
+ var d = i.Details;
+ var newDetails = ServiceDetails.Of(d.RequiredServiceType, serviceKey, d.IfUnresolved, d.DefaultValue);
+ _serviceInfo = i.Create(i.ServiceType, newDetails);
+ }
+
+ /// Adds input argument expression list to request.
+ /// The arguments are provided by Func and Action wrappers.
+ /// Argument parameter expressions. New request.
+ public Request WithArgs(ParameterExpression[] argExpressions)
+ {
+ var argsUsed = new bool[argExpressions.Length];
+ var argsInfo = new KV(argsUsed, argExpressions);
+ return new Request(_requestContext, RawParent, _serviceInfo, _factory, _reuse, argsInfo, _flags);
}
- /// Returns new request with parameter expressions created for input arguments.
- /// The expression is set to request field to use for
- /// resolution.
- /// Func type to get input arguments from.
- /// New request with field set.
+ // todo: v3: remove
+ /// Obsolete: replaced with .
public Request WithFuncArgs(Type funcType)
{
- var funcArgs = funcType.ThrowIf(!funcType.IsFuncWithArgs()).GetGenericParamsAndArgs();
- var funcArgExprs = new ParameterExpression[funcArgs.Length - 1];
+ var openGenType = funcType.GetGenericDefinitionOrNull().ThrowIfNull();
+
+ var funcIndex = WrappersSupport.FuncTypes.IndexOf(openGenType);
+ var actionIndex = funcIndex != -1 ? -1 : WrappersSupport.ActionTypes.IndexOf(openGenType);
+ Throw.If(funcIndex < 1 && actionIndex < 1);
- for (var i = 0; i < funcArgExprs.Length; ++i)
+ var argTypes = funcType.GetGenericParamsAndArgs();
+ var argCount = funcIndex > 0 ? argTypes.Length - 1 : argTypes.Length;
+
+ var argExprs = new ParameterExpression[argCount];
+ for (var i = 0; i < argCount; ++i)
{
- var funcArg = funcArgs[i];
- var funcArgName = "_" + funcArg.Name + i; // Valid non conflicting argument names for code generation
- funcArgExprs[i] = Expression.Parameter(funcArg, funcArgName);
+ var argType = argTypes[i];
+ var argName = "_" + argType.Name + i; // Valid unique argument names for code generation
+ argExprs[i] = Expression.Parameter(argType, argName);
}
- var funcArgsUsage = new bool[funcArgExprs.Length];
- var funcArgsUsageAndExpr = new KV(funcArgsUsage, funcArgExprs);
- return new Request(_resolverContext, RawParent, RequestInfo, Made, funcArgsUsageAndExpr, Level);
+ var argsUsed = new bool[argExprs.Length];
+ var argsInfo = new KV(argsUsed, argExprs);
+ return new Request(_requestContext, RawParent, _serviceInfo, _factory, _reuse, argsInfo, _flags);
}
- /// Changes container to passed one. Could be used by child container,
+ /// Changes container to passed one. Could be used by child container,
/// to switch child container to parent preserving the rest of request state.
/// Reference to container to switch to.
/// Request with replaced container.
public Request WithNewContainer(ContainerWeakRef newContainer)
{
- var newContext = _resolverContext.With(newContainer);
- return new Request(newContext, RawParent, RequestInfo, Made, FuncArgs, Level);
+ var newContext = _requestContext.With(newContainer.Container);
+ return new Request(newContext, RawParent, _serviceInfo, _factory, _reuse, FuncArgs, _flags);
}
/// Returns new request with set implementation details.
/// Factory to which request is resolved.
+ /// (optional) does not check for recursive dependency.
+ /// Use with caution. Make sense for Resolution expression.
+ /// (optional) allows to skip captive dependency check.
/// New request with set factory.
- public Request WithResolvedFactory(Factory factory)
+ public Request WithResolvedFactory(Factory factory,
+ bool skipRecursiveDependencyCheck = false,
+ bool skipCaptiveDependencyCheck = false)
{
- var newFactoryID = factory.FactoryID;
- if (IsEmpty || FactoryID == newFactoryID)
+ if (IsEmpty || _factory != null && _factory.FactoryID == factory.FactoryID)
return this; // resolving only once, no need to check recursion again.
- if (factory.FactoryType == FactoryType.Service)
+ if (factory.FactoryType == FactoryType.Service && !skipRecursiveDependencyCheck)
for (var p = RawParent; !p.IsEmpty; p = p.RawParent)
- Throw.If(p.FactoryID == newFactoryID, Error.RecursiveDependencyDetected, Print(newFactoryID));
+ if (p.FactoryID == factory.FactoryID)
+ Throw.It(Error.RecursiveDependencyDetected, Print(factory.FactoryID));
+
+ var reuse = factory.Reuse;
+ if (reuse == null)
+ reuse = GetDefaultReuse(factory);
+
+ var flags = _flags;
+
+ if (!skipCaptiveDependencyCheck && reuse.Lifespan != 0 &&
+ Rules.ThrowIfDependencyHasShorterReuseLifespan)
+ ThrowIfReuseHasShorterLifespanThanParent(reuse);
- var reuse = factory.Reuse ??
- (factory.Setup.UseParentReuse
- ? GetParentOrFuncOrEmpty().Reuse
- : FactoryID != 0 && factory.Setup is Setup.DecoratorSetup &&
- ((Setup.DecoratorSetup)factory.Setup).UseDecorateeReuse
- ? Reuse
- : null);
+ if (reuse == DryIoc.Reuse.Transient)
+ {
+ reuse = GetTransientDisposableTrackingReuse(factory);
+ if (reuse != DryIoc.Reuse.Transient)
+ flags |= RequestFlags.TracksTransientDisposable;
+ }
- var newInfo = RequestInfo.With(newFactoryID, factory.FactoryType, factory.ImplementationType, reuse);
- var made = factory is ReflectionFactory ? ((ReflectionFactory)factory).Made : null;
- return new Request(_resolverContext, RawParent, newInfo, made, FuncArgs, Level);
+ if (reuse == DryIoc.Reuse.Singleton)
+ flags |= RequestFlags.IsSingletonOrDependencyOfSingleton;
+
+ _requestContext.IncrementDependencyCount();
+ return new Request(_requestContext, RawParent, _serviceInfo, factory, reuse, FuncArgs, flags);
}
- /// Returns non-wrapper parent of Func wrapper if any.
- /// (optional) When set specifies to search for first not transient parent.
- /// Found parent or Func parent or empty.
- public RequestInfo GetParentOrFuncOrEmpty(bool firstNonTransientParent = false)
+ private IReuse GetDefaultReuse(Factory factory)
{
- var parent = ParentOrWrapper;
- if (!parent.IsEmpty)
+ IReuse reuse = null;
+
+ if (factory.Setup.UseParentReuse)
+ reuse = GetFirstParentNonTransientReuseUntilFunc();
+
+ else if (factory.Setup.FactoryType == FactoryType.Decorator
+ && ((Setup.DecoratorSetup)factory.Setup).UseDecorateeReuse)
+ reuse = Reuse; // use reuse of resolved service factory for decorator
+ else
+ // if no specified the wrapper reuse is always Transient,
+ // other container-wide default reuse is applied
+ reuse = factory.FactoryType != FactoryType.Wrapper
+ ? Container.Rules.DefaultReuseInsteadOfTransient
+ : DryIoc.Reuse.Transient;
+
+ return reuse;
+ }
+
+ private IReuse GetTransientDisposableTrackingReuse(Factory factory)
+ {
+ // Track transient disposable in parent scope (if any), or open scope (if any)
+ var setup = factory.Setup;
+ var tracksTransientDisposable =
+ !setup.PreventDisposal &&
+ (setup.TrackDisposableTransient || !setup.AllowDisposableTransient && Rules.TrackingDisposableTransients) &&
+ (factory.ImplementationType ?? GetActualServiceType()).IsAssignableTo(typeof(IDisposable));
+
+ if (!tracksTransientDisposable)
+ return DryIoc.Reuse.Transient;
+
+ var parentReuse = GetFirstParentNonTransientReuseUntilFunc();
+ if (parentReuse != DryIoc.Reuse.Transient)
+ return parentReuse;
+
+ if (IsWrappedInFunc())
+ return DryIoc.Reuse.Transient;
+
+ // If no reused parent, then track in current open scope, or if not opened in singleton
+ return Scopes.GetCurrentScope() != null ? DryIoc.Reuse.InCurrentScope : DryIoc.Reuse.Singleton;
+ }
+
+ private void ThrowIfReuseHasShorterLifespanThanParent(IReuse reuse)
+ {
+ if (!RawParent.IsEmpty)
+ for (var p = RawParent; !p.IsEmpty; p = p.RawParent)
+ {
+ if (p.FactoryType == FactoryType.Wrapper && p.GetActualServiceType().IsFunc())
+ break;
+
+ if (p.FactoryType == FactoryType.Service && p.ReuseLifespan > reuse.Lifespan)
+ Throw.It(Error.DependencyHasShorterReuseLifespan, PrintCurrent(), reuse, p);
+ }
+
+ if (!PreResolveParent.IsEmpty)
{
- foreach (var p in parent.Enumerate())
+ for (var p = PreResolveParent; !p.IsEmpty; p = p.ParentOrWrapper)
{
- if (p.FactoryType == FactoryType.Wrapper)
- {
- if (p.GetActualServiceType().IsFunc())
- return p;
- }
- else
- {
- if (!firstNonTransientParent || p.Reuse != null)
- return p;
- }
+ if (p.FactoryType == FactoryType.Wrapper && p.GetActualServiceType().IsFunc())
+ break;
+
+ if (p.FactoryType == FactoryType.Service && p.ReuseLifespan > reuse.Lifespan)
+ Throw.It(Error.DependencyHasShorterReuseLifespan, PrintCurrent(), reuse, p);
}
}
+ }
+
+ private IReuse GetFirstParentNonTransientReuseUntilFunc(bool firstNonTransientParent = false)
+ {
+ if (!RawParent.IsEmpty)
+ for (var p = RawParent; !p.IsEmpty; p = p.RawParent)
+ {
+ if (p.FactoryType == FactoryType.Wrapper && p.GetActualServiceType().IsFunc())
+ return DryIoc.Reuse.Transient;
+
+ if (p.FactoryType != FactoryType.Wrapper && p.Reuse != DryIoc.Reuse.Transient)
+ return p.Reuse;
+ }
- return RequestInfo.Empty;
+ if (!PreResolveParent.IsEmpty)
+ for (var p = PreResolveParent; !p.IsEmpty; p = p.ParentOrWrapper)
+ {
+ if (p.FactoryType == FactoryType.Wrapper && p.GetActualServiceType().IsFunc())
+ return DryIoc.Reuse.Transient;
+
+ if (p.FactoryType != FactoryType.Wrapper && p.Reuse != DryIoc.Reuse.Transient)
+ return p.Reuse;
+ }
+
+ return DryIoc.Reuse.Transient;
}
- /// Serializable request info stripped from run-time info.
- public RequestInfo RequestInfo { get; private set; } // note: mutable to change key in place
+ /// Serializable request info stripped off run-time info.
+ public RequestInfo RequestInfo
+ {
+ get
+ {
+ if (IsEmpty)
+ return PreResolveParent;
+
+ RequestInfo parentRequestInfo;
+ if (RawParent.IsEmpty)
+ parentRequestInfo = PreResolveParent;
+ else
+ parentRequestInfo = RawParent.RequestInfo;
+
+ if (_factory == null)
+ return parentRequestInfo.Push(_serviceInfo);
+
+ var f = _factory;
+ return parentRequestInfo.Push(_serviceInfo,
+ f.FactoryID, f.FactoryType, f.ImplementationType, _reuse);
+ }
+ }
// todo: v3: remove
/// Obsolete: use instead.
- /// Mirrored request info.
public RequestInfo ToRequestInfo()
{
return RequestInfo;
}
- /// If request corresponds to dependency injected into parameter,
+ /// If request corresponds to dependency injected into parameter,
/// then method calls handling and returns its result.
/// If request corresponds to property or field, then method calls respective handler.
/// If request does not correspond to dependency, then calls handler.
@@ -5664,7 +6357,7 @@ public RequestInfo ToRequestInfo()
Func property = null,
Func field = null)
{
- var serviceInfo = RequestInfo.ServiceInfo;
+ var serviceInfo = _serviceInfo;
if (serviceInfo is ParameterServiceInfo)
{
if (parameter != null)
@@ -5680,7 +6373,7 @@ public RequestInfo ToRequestInfo()
return property(propertyInfo);
}
else if (field != null)
- return field((FieldInfo)propertyOrFieldServiceInfo.Member);
+ return field((FieldInfo)propertyOrFieldServiceInfo.Member);
}
else if (root != null)
return root();
@@ -5688,7 +6381,7 @@ public RequestInfo ToRequestInfo()
return default(TResult);
}
- /// Enumerates all request stack parents.
+ /// Enumerates all request stack parents.
/// Last returned will empty parent.
/// Unfolding parents.
public IEnumerable Enumerate()
@@ -5703,21 +6396,42 @@ public IEnumerable Enumerate()
public StringBuilder PrintCurrent(StringBuilder s = null)
{
s = s ?? new StringBuilder();
- s = RequestInfo.PrintCurrent(s);
+
+ if (IsEmpty)
+ return s.Append("{empty}");
+
+ if (_factory != null)
+ {
+ if (_reuse != DryIoc.Reuse.Transient)
+ s.Append(Reuse is SingletonReuse ? "singleton" : "scoped").Append(' ');
+
+ var factoryType = _factory.FactoryType;
+ if (factoryType != FactoryType.Service)
+ s.Append(factoryType.ToString().ToLower()).Append(' ');
+
+ var implType = _factory.ImplementationType;
+ if (implType != null && implType != ServiceType)
+ s.Print(implType).Append(": ");
+ }
+
+ s.Append(_serviceInfo);
+
if (FuncArgs != null)
s.AppendFormat(" with {0} arg(s) ", FuncArgs.Key.Count(k => k == false));
+
return s;
}
/// Prints full stack of requests starting from current one using .
- /// Flag specifying that in case of found recursion/repetition of requests,
+ /// Flag specifying that in case of found recursion/repetition of requests,
/// mark repeated requests.
/// Builder with appended request stack info.
public StringBuilder Print(int recursiveFactoryID = -1)
{
- var s = PrintCurrent(new StringBuilder());
if (IsEmpty)
- return s;
+ return new StringBuilder("");
+
+ var s = PrintCurrent(new StringBuilder());
s = recursiveFactoryID == -1 ? s : s.Append(" <--recursive");
foreach (var r in RawParent.Enumerate())
@@ -5742,50 +6456,54 @@ public override string ToString()
#region Implementation
- internal Request(ResolverContext resolverContext,
- Request parent, RequestInfo requestInfo, Made made, KV funcArgs,
- int level = 0)
+ private Request(RequestContext requestContext, Request parent, IServiceInfo serviceInfo,
+ Factory factory, IReuse reuse,
+ KV funcArgs, RequestFlags flags)
{
- _resolverContext = resolverContext;
+ _requestContext = requestContext;
RawParent = parent;
- RequestInfo = requestInfo;
- Made = made;
+ _serviceInfo = serviceInfo;
+ _factory = factory;
+ _reuse = reuse;
FuncArgs = funcArgs;
- Level = level;
+ _flags = flags;
}
- private readonly ResolverContext _resolverContext;
+ private IServiceInfo _serviceInfo;
+ private readonly Factory _factory;
+ private readonly IReuse _reuse;
+
+ private readonly RequestFlags _flags;
- internal sealed class ResolverContext
+ private readonly RequestContext _requestContext;
+
+ internal sealed class RequestContext
{
- public readonly ContainerWeakRef ContainerWeakRef;
- public readonly ContainerWeakRef ScopesWeakRef;
+ public readonly IContainer Container;
+ public readonly IScopeAccess Scopes;
public readonly IScope Scope;
public readonly RequestInfo PreResolveParent;
- public ResolverContext(ContainerWeakRef container, ContainerWeakRef scopes, IScope scope, RequestInfo preResolveParent)
+ // Mutable updatable part
+ public bool ContainsNestedResolutionCall;
+ public int DependencyCount;
+
+ public RequestContext(IContainer container, IScopeAccess scopes, IScope scope, RequestInfo preResolveParent)
{
- ContainerWeakRef = container;
- ScopesWeakRef = scopes;
+ Container = container;
+ Scopes = scopes;
Scope = scope;
PreResolveParent = preResolveParent;
}
- public ResolverContext With(ContainerWeakRef newContainer)
+ public RequestContext With(IContainer newContainer)
{
- return new ResolverContext(newContainer, ScopesWeakRef, Scope, PreResolveParent);
+ return new RequestContext(newContainer, Scopes, Scope, PreResolveParent);
}
- public ResolverContext With(IScope scope)
+ public void IncrementDependencyCount()
{
- return scope == null ? this
- : new ResolverContext(ContainerWeakRef, ScopesWeakRef, scope, PreResolveParent);
- }
-
- public ResolverContext With(RequestInfo preResolveParent)
- {
- return preResolveParent == null || preResolveParent.IsEmpty ? this
- : new ResolverContext(ContainerWeakRef, ScopesWeakRef, Scope, preResolveParent);
+ Interlocked.Increment(ref DependencyCount);
}
}
@@ -5806,7 +6524,7 @@ public enum FactoryType
/// Base class to store optional settings.
public abstract class Setup
{
- /// Factory type is required to be specified by concrete setups as in
+ /// Factory type is required to be specified by concrete setups as in
/// , , .
public abstract FactoryType FactoryType { get; }
@@ -5816,14 +6534,14 @@ public abstract class Setup
/// Arbitrary metadata object associated with Factory/Implementation.
public virtual object Metadata { get { return null; } }
- /// Indicates that injected expression should be:
+ /// Indicates that injected expression should be:
/// (...)]]>
/// instead of:
public bool AsResolutionCall { get { return (_settings & Settings.AsResolutionCall) != 0; } }
/// Marks service (not a wrapper or decorator) registration that is expected to be resolved via Resolve call.
public bool AsResolutionRoot { get { return (_settings & Settings.AsResolutionRoot) != 0; } }
-
+
/// In addition to opens scope.
public bool OpenResolutionScope { get { return (_settings & Settings.OpenResolutionScope) != 0; } }
@@ -5860,16 +6578,20 @@ public bool MatchesMetadata(string metadataKey, object metadata)
/// Default setup for service factories.
public static readonly Setup Default = new ServiceSetup();
- /// Sets the basic settings.
+ /// Sets the base settings.
+ ///
///
///
///
///
- private Setup(bool openResolutionScope = false, bool asResolutionCall = false,
+ private Setup(Func condition = null,
+ bool openResolutionScope = false, bool asResolutionCall = false,
bool asResolutionRoot = false, bool preventDisposal = false, bool weaklyReferenced = false,
bool allowDisposableTransient = false, bool trackDisposableTransient = false,
bool useParentReuse = false)
{
+ Condition = condition;
+
if (asResolutionCall)
_settings |= Settings.AsResolutionCall;
if (openResolutionScope)
@@ -5910,7 +6632,8 @@ private enum Settings
private readonly Settings _settings;
/// Constructs setup object out of specified settings. If all settings are default then setup will be returned.
- /// (optional) Metadata object or Func returning metadata object. (optional)
+ /// (optional) Metadata object or Func returning metadata object.
+ /// (optional)
/// (optional) Same as but in addition opens new scope.
/// (optional) If true dependency expression will be "r.Resolve(...)" instead of inline expression.
/// (optional) Marks service (not a wrapper or decorator) registration that is expected to be resolved via Resolve call.
@@ -5944,17 +6667,24 @@ private enum Settings
public static readonly Setup Wrapper = new WrapperSetup();
/// Returns generic wrapper setup.
- /// Default is -1 for generic wrapper with single type argument. Need to be set for multiple type arguments.
+ /// Default is -1 for generic wrapper with single type argument. Need to be set for multiple type arguments.
/// Need to be set when generic wrapper type arguments should be ignored.
+ /// (optional) Delegate returning wrapped type from wrapper type. Overwrites other options.
/// (optional) Opens the new scope.
+ /// (optional) Injects decorator as resolution call.
/// (optional) Prevents disposal of reused instance if it is disposable.
+ /// (optional)
/// New setup or default .
- public static Setup WrapperWith(int wrappedServiceTypeArgIndex = -1, bool alwaysWrapsRequiredServiceType = false,
- bool openResolutionScope = false, bool preventDisposal = false)
+ public static Setup WrapperWith(int wrappedServiceTypeArgIndex = -1,
+ bool alwaysWrapsRequiredServiceType = false, Func unwrap = null,
+ bool openResolutionScope = false, bool asResolutionCall = false, bool preventDisposal = false,
+ Func condition = null)
{
- return wrappedServiceTypeArgIndex == -1 && !alwaysWrapsRequiredServiceType
- && !openResolutionScope && !preventDisposal ? Wrapper
- : new WrapperSetup(wrappedServiceTypeArgIndex, alwaysWrapsRequiredServiceType, openResolutionScope, preventDisposal);
+ return wrappedServiceTypeArgIndex == -1 && !alwaysWrapsRequiredServiceType && unwrap == null
+ && !openResolutionScope && !preventDisposal && condition == null
+ ? Wrapper
+ : new WrapperSetup(wrappedServiceTypeArgIndex, alwaysWrapsRequiredServiceType, unwrap,
+ condition, openResolutionScope, asResolutionCall, preventDisposal);
}
/// Default decorator setup: decorator is applied to service type it registered with.
@@ -5965,13 +6695,13 @@ private enum Settings
/// (optional) If provided specifies relative decorator position in decorators chain.
/// If provided specifies relative decorator position in decorators chain.
/// Greater number means further from decoratee - specify negative number to stay closer.
- /// Decorators without order (Order is 0) or with equal order are applied in registration order
+ /// Decorators without order (Order is 0) or with equal order are applied in registration order
/// - first registered are closer decoratee.
/// New setup with condition or .
- public static Setup DecoratorWith(Func condition = null, int order = 0,
+ public static Setup DecoratorWith(Func condition = null, int order = 0,
bool useDecorateeReuse = false)
{
- return condition == null && order == 0 && !useDecorateeReuse ? Decorator
+ return condition == null && order == 0 && !useDecorateeReuse ? Decorator
: new DecoratorSetup(condition, order, useDecorateeReuse);
}
@@ -5992,15 +6722,17 @@ public override object Metadata
}
}
- public ServiceSetup(Func condition = null, object metadataOrFuncOfMetadata = null,
- bool openResolutionScope = false, bool asResolutionCall = false, bool asResolutionRoot = false,
- bool preventDisposal = false, bool weaklyReferenced = false,
- bool allowDisposableTransient = false, bool trackDisposableTransient = false,
- bool useParentReuse = false) : base(openResolutionScope, asResolutionCall, asResolutionRoot,
+ public ServiceSetup() { }
+
+ public ServiceSetup(Func condition, object metadataOrFuncOfMetadata,
+ bool openResolutionScope, bool asResolutionCall, bool asResolutionRoot,
+ bool preventDisposal, bool weaklyReferenced,
+ bool allowDisposableTransient, bool trackDisposableTransient,
+ bool useParentReuse)
+ : base(condition, openResolutionScope, asResolutionCall, asResolutionRoot,
preventDisposal, weaklyReferenced, allowDisposableTransient, trackDisposableTransient,
useParentReuse)
{
- Condition = condition;
_metadataOrFuncOfMetadata = metadataOrFuncOfMetadata;
}
@@ -6008,36 +6740,52 @@ public override object Metadata
}
/// Setup for factory.
- public sealed class WrapperSetup : Setup
+ internal sealed class WrapperSetup : Setup
{
/// Returns type.
public override FactoryType FactoryType { get { return FactoryType.Wrapper; } }
- /// Delegate to get wrapped type from provided wrapper type.
+ /// Delegate to get wrapped type from provided wrapper type.
/// If wrapper is generic, then wrapped type is usually a generic parameter.
public readonly int WrappedServiceTypeArgIndex;
/// Per name.
public readonly bool AlwaysWrapsRequiredServiceType;
+ /// Delegate returning wrapped type from wrapper type. Overwrites other options.
+ public readonly Func Unwrap;
+
+ /// Default setup
+ /// Default is -1 for generic wrapper with single type argument.
+ /// Need to be set for multiple type arguments.
+ public WrapperSetup(int wrappedServiceTypeArgIndex = -1)
+ {
+ WrappedServiceTypeArgIndex = wrappedServiceTypeArgIndex;
+ }
+
/// Constructs wrapper setup from optional wrapped type selector and reuse wrapper factory.
- /// Default is -1 for generic wrapper with single type argument. Need to be set for multiple type arguments.
+ /// Default is -1 for generic wrapper with single type argument. Need to be set for multiple type arguments.
/// Need to be set when generic wrapper type arguments should be ignored.
- /// (optional) Opens the new scope.
- /// (optional) Prevents disposal of reused instance if it is disposable.
- public WrapperSetup(int wrappedServiceTypeArgIndex = -1, bool alwaysWrapsRequiredServiceType = false,
- bool openResolutionScope = false, bool asResolutionCall = false, bool preventDisposal = false)
- : base(openResolutionScope: openResolutionScope, asResolutionCall: asResolutionCall,
- preventDisposal: preventDisposal)
+ /// Delegate returning wrapped type from wrapper type. Overwrites other options.
+ /// Opens the new scope.
+ /// Prevents disposal of reused instance if it is disposable.
+ /// Predicate to check if factory could be used for resolved request.
+ public WrapperSetup(int wrappedServiceTypeArgIndex, bool alwaysWrapsRequiredServiceType, Func unwrap,
+ Func condition, bool openResolutionScope, bool asResolutionCall, bool preventDisposal)
+ : base(condition, openResolutionScope: openResolutionScope, asResolutionCall: asResolutionCall, preventDisposal: preventDisposal)
{
WrappedServiceTypeArgIndex = wrappedServiceTypeArgIndex;
AlwaysWrapsRequiredServiceType = alwaysWrapsRequiredServiceType;
+ Unwrap = unwrap;
}
/// Unwraps service type or returns its.
/// Wrapped type or self.
public Type GetWrappedTypeOrNullIfWrapsRequired(Type serviceType)
{
+ if (Unwrap != null)
+ return Unwrap(serviceType);
+
if (AlwaysWrapsRequiredServiceType || !serviceType.IsGeneric())
return null;
@@ -6055,38 +6803,41 @@ public Type GetWrappedTypeOrNullIfWrapsRequired(Type serviceType)
}
/// Setup applied to decorators.
- public sealed class DecoratorSetup : Setup
+ internal sealed class DecoratorSetup : Setup
{
/// Returns Decorator factory type.
public override FactoryType FactoryType { get { return FactoryType.Decorator; } }
/// If provided specifies relative decorator position in decorators chain.
/// Greater number means further from decoratee - specify negative number to stay closer.
- /// Decorators without order (Order is 0) or with equal order are applied in registration order
+ /// Decorators without order (Order is 0) or with equal order are applied in registration order
/// - first registered are closer decoratee.
public readonly int Order;
/// Instructs to use decorated service reuse. Decorated service may be decorator itself.
public readonly bool UseDecorateeReuse;
+ /// Default setup.
+ public DecoratorSetup() { }
+
/// Creates decorator setup with optional condition.
/// (optional) Applied to decorated service to find that service is the decorator target.
/// (optional) If provided specifies relative decorator position in decorators chain.
/// Greater number means further from decoratee - specify negative number to stay closer.
- /// Decorators without order (Order is 0) or with equal order are applied in registration order
+ /// Decorators without order (Order is 0) or with equal order are applied in registration order
/// - first registered are closer decoratee.
/// (optional) Instructs to use decorated service reuse.
/// Decorated service may be decorator itself.
- public DecoratorSetup(Func condition = null, int order = 0, bool useDecorateeReuse = false)
+ public DecoratorSetup(Func condition, int order, bool useDecorateeReuse)
+ : base(condition)
{
- Condition = condition;
Order = order;
UseDecorateeReuse = useDecorateeReuse;
}
}
}
- /// Facility for creating concrete factories from some template/prototype. Example:
+ /// Facility for creating concrete factories from some template/prototype. Example:
/// creating closed-generic type reflection factory from registered open-generic prototype factory.
public interface IConcreteFactoryGenerator
{
@@ -6094,11 +6845,14 @@ public interface IConcreteFactoryGenerator
ImTreeMap, ReflectionFactory> GeneratedFactories { get; }
/// Returns factory per request. May track already generated factories and return one without regenerating.
- /// Request to resolve. Returns new factory per request.
- Factory GetGeneratedFactoryOrDefault(Request request);
+ /// Request to resolve.
+ /// If set to true - returns null if unable to generate,
+ /// otherwise error result depends on .
+ /// Returns new factory per request.
+ Factory GetGeneratedFactory(Request request, bool ifErrorReturnDefault = false);
}
- /// Base class for different ways to instantiate service:
+ /// Base class for different ways to instantiate service:
///
/// - Through reflection -
/// - Using custom delegate -
@@ -6109,20 +6863,26 @@ public interface IConcreteFactoryGenerator
/// Each created factory has an unique ID set in .
public abstract class Factory
{
+ /// Get next factory ID in a atomic way. The ID.
+ public static int GetNextID()
+ {
+ return Interlocked.Increment(ref _lastFactoryID);
+ }
+
/// Unique factory id generated from static seed.
public int FactoryID { get; internal set; }
- /// Reuse policy for factory created services.
- public readonly IReuse Reuse;
+ /// Reuse policy for created services.
+ public virtual IReuse Reuse { get { return _reuse; } }
/// Setup may contain different/non-default factory settings.
- public Setup Setup
+ public virtual Setup Setup
{
get { return _setup; }
protected internal set { _setup = value ?? Setup.Default; }
}
- /// Checks that condition is met for request or there is no condition setup.
+ /// Checks that condition is met for request or there is no condition setup.
/// Additionally check for reuse scope availability.
/// Request to check against.
/// True if condition met or no condition setup.
@@ -6141,41 +6901,30 @@ public FactoryType FactoryType
/// Non-abstract closed implementation type. May be null if not known beforehand, e.g. in .
public virtual Type ImplementationType { get { return null; } }
- /// Indicates that Factory is factory provider and
- /// consumer should call to get concrete factory.
+ /// Indicates that Factory is factory provider and
+ /// consumer should call to get concrete factory.
public virtual IConcreteFactoryGenerator FactoryGenerator { get { return null; } }
- /// Get next factory ID in a atomic way. The ID.
- public static int GetNextID()
- {
- return Interlocked.Increment(ref _lastFactoryID);
- }
+ /// Settings (if any) to select Constructor/FactoryMethod, Parameters, Properties and Fields.
+ public virtual Made Made { get { return Made.Default; } }
/// Initializes reuse and setup. Sets the
- /// (optional)
- /// (optional)
+ /// (optional) (optional)
protected Factory(IReuse reuse = null, Setup setup = null)
{
FactoryID = GetNextID();
- Reuse = reuse;
- Setup = setup ?? Setup.Default;
+ _reuse = reuse;
+ _setup = setup ?? Setup.Default;
}
/// Returns true if for factory Reuse exists matching resolution or current Scope.
/// True if matching Scope exists.
public bool HasMatchingReuseScope(Request request)
{
- if (Reuse == null || !request.Container.Rules.ImplicitCheckForReuseMatchingScope)
+ if (!request.Rules.ImplicitCheckForReuseMatchingScope)
return true;
-
- if (Reuse is ResolutionScopeReuse)
- return Reuse.GetScopeOrDefault(request) != null;
-
- if (Reuse is CurrentScopeReuse)
- return request.IsWrappedInFunc()
- || Reuse.GetScopeOrDefault(request) != null;
-
- return true;
+ var reuse = Reuse as IReuseV3;
+ return reuse == null || reuse.CanApply(request);
}
/// The main factory method to create service expression, e.g. "new Client(new Service())".
@@ -6189,18 +6938,31 @@ public bool HasMatchingReuseScope(Request request)
/// Context. True if factory expression could be cached.
protected virtual bool IsFactoryExpressionCacheable(Request request)
{
- return request.FuncArgs == null
- && Setup.FactoryType == FactoryType.Service
- // the settings below specify context based resolution so that expression will be different on
- // different resolution paths, which prevents its caching and reuse.
- && Setup.Condition == null
- && !Setup.AsResolutionCall
- && !Setup.UseParentReuse
- && !(request.Reuse is ResolutionScopeReuse)
- // tracking disposable transient
- && !(request.Reuse == null
- && (Setup.TrackDisposableTransient || request.Container.Rules.TrackingDisposableTransients)
- && IsDisposableService(request));
+ return Setup.FactoryType == FactoryType.Service
+ && !request.TracksTransientDisposable
+ && request.FuncArgs == null
+ && !Setup.AsResolutionCall
+ && !request.IsResolutionRoot
+ && Setup.Condition == null &&
+ !IsScopeDependent(request);
+ }
+
+ private bool IsScopeDependent(Request request)
+ {
+ return Setup.UseParentReuse
+ || request.Reuse is ResolutionScopeReuse
+ || (request.Reuse is CurrentScopeReuse && ((CurrentScopeReuse)request.Reuse).Name != null);
+ }
+
+ private bool ShouldBeInjectedAsResolutionCall(Request request)
+ {
+ return !request.IsResolutionCall && // prevents recursion on already split graph
+ // explicit aka user requested split
+ (Setup.AsResolutionCall ||
+ // implicit split only when not inside func with args, cause until v3 args are not propagated through resolve call.
+ (request.ShouldSplitObjectGraph() || IsScopeDependent(request)) &&
+ !request.IsWrappedInFuncWithArgs()) &&
+ request.GetActualServiceType() != typeof(void);
}
/// Returns service expression: either by creating it with or taking expression from cache.
@@ -6210,93 +6972,82 @@ public virtual Expression GetExpressionOrDefault(Request request)
{
request = request.WithResolvedFactory(this);
- var container = request.Container;
-
- // Returns "r.Resolver.Resolve(...)" instead of "new Dependency()".
- if (Setup.AsResolutionCall && !request.IsFirstNonWrapperInResolutionCall()
- || request.Level >= container.Rules.LevelToSplitObjectGraphIntoResolveCalls
- // note: Split only if not wrapped in Func with args - Propagation of args across Resolve boundaries is not supported at the moment
- && !request.IsWrappedInFuncWithArgs())
+ if (ShouldBeInjectedAsResolutionCall(request))
return Resolver.CreateResolutionExpression(request, Setup.OpenResolutionScope);
- // Here's lookup for decorators
- var decoratorExpr = FactoryType != FactoryType.Decorator
- ? container.GetDecoratorExpressionOrDefault(request)
- : null;
+ var container = request.Container;
- var isDecorated = decoratorExpr != null;
- var cacheable = IsFactoryExpressionCacheable(request) && !isDecorated;
- if (cacheable)
+ // First look for decorators
+ if (FactoryType != FactoryType.Decorator)
{
- var cachedServiceExpr = container.GetCachedFactoryExpressionOrDefault(FactoryID);
- if (cachedServiceExpr != null)
- return cachedServiceExpr;
+ var decoratorExpr = container.GetDecoratorExpressionOrDefault(request);
+ if (decoratorExpr != null)
+ return decoratorExpr;
}
- var serviceExpr = decoratorExpr ?? CreateExpressionOrDefault(request);
- if (serviceExpr != null && !isDecorated)
+ // Then optimize for already resolved singleton object, otherwise goes normal ApplyReuse route
+ if (request.Rules.EagerCachingSingletonForFasterAccess &&
+ request.Reuse is SingletonReuse &&
+ !Setup.PreventDisposal && !Setup.WeaklyReferenced)
{
- // Getting reuse from Request to take useParentReuse or useDecorateeReuse into account
- var reuse = request.Reuse;
-
- // Track transient disposable in parent scope (if any), or open scope (if any)
- var tracksTransientDisposable = false;
- if (reuse == null && !Setup.PreventDisposal &&
- (Setup.TrackDisposableTransient ||
- !Setup.AllowDisposableTransient && container.Rules.TrackingDisposableTransients) &&
- IsDisposableService(request))
+ var singletons = (SingletonScope)request.SingletonScope;
+ var singletonID = singletons.IndexOf(FactoryID);
+ if (singletonID > 0)
{
- reuse = GetTransientDisposableTrackingReuse(request);
- tracksTransientDisposable = true;
+ var value = singletons.GetOrDefault(singletonID);
+ if (value != null)
+ return Expression.Constant(value, request.ServiceType);
}
+ }
- // As a last resort, apply per-container default reuse
- if (reuse == null)
- reuse = container.Rules.DefaultReuseInsteadOfTransient;
-
- ThrowIfReuseHasShorterLifespanThanParent(reuse, request);
-
- if (reuse != null)
- serviceExpr = ApplyReuse(serviceExpr, reuse, tracksTransientDisposable, request);
+ // Then check the expression cache
+ var isCacheable = IsFactoryExpressionCacheable(request);
+ if (isCacheable)
+ {
+ var cachedExpr = container.GetCachedFactoryExpressionOrDefault(FactoryID);
+ if (cachedExpr != null)
+ return cachedExpr;
}
- if (cacheable && serviceExpr != null)
- container.CacheFactoryExpression(FactoryID, serviceExpr);
+ // Then create new expression
+ var serviceExpr = CreateExpressionOrDefault(request);
+ if (serviceExpr != null)
+ {
+ // can be checked only after expression is created
+ if (request.ContainsNestedResolutionCall)
+ isCacheable = false;
- if (serviceExpr == null && request.IfUnresolved == IfUnresolved.Throw)
- Container.ThrowUnableToResolve(request);
+ if (request.Reuse != DryIoc.Reuse.Transient &&
+ request.GetActualServiceType() != typeof(void))
+ {
+ var originalServiceExprType = serviceExpr.Type;
- return serviceExpr;
- }
+ serviceExpr = ApplyReuse(serviceExpr, request.Reuse, request.TracksTransientDisposable, request);
- private static IReuse GetTransientDisposableTrackingReuse(Request request)
- {
- // Track in parent's scope
- var parent = request.GetParentOrFuncOrEmpty(firstNonTransientParent: true);
- if (parent.FactoryType == FactoryType.Wrapper)
- return null;
+ if (serviceExpr.NodeType == ExpressionType.Constant)
+ isCacheable = false;
- // If no reused parent, then track in current open scope if any
- // NOTE: No tracking in singleton scope cause it is most likely a memory leak.
- var reuse = parent.Reuse;
- if (reuse == null)
+ if (serviceExpr.Type != originalServiceExprType)
+ serviceExpr = Expression.Convert(serviceExpr, originalServiceExprType);
+ }
+
+ if (isCacheable)
+ {
+ container.CacheFactoryExpression(FactoryID, serviceExpr);
+ }
+ }
+ // Otherwise throw
+ else if (request.IfUnresolved == IfUnresolved.Throw)
{
- var currentScope = request.Scopes.GetCurrentScope();
- if (currentScope != null)
- reuse = DryIoc.Reuse.InCurrentScope;
+ Container.ThrowUnableToResolve(request);
}
- return reuse;
- }
-
- private bool IsDisposableService(Request request)
- {
- return request.GetActualServiceType().IsAssignableTo(typeof(IDisposable))
- || ImplementationType.IsAssignableTo(typeof(IDisposable));
+ return serviceExpr;
}
- /// Applies reuse to created expression.
- /// Actually wraps passed expression in scoped access and produces another expression.
+ // todo: remove trackTransientDisposable param as it is available from Request param.
+ /// Applies reuse to created expression. Actually wraps passed expression in scoped access
+ /// and produces another expression.
/// Raw service creation (or receiving) expression.
/// Reuse - may be different from if set .
/// Specifies that reuse is to track transient disposable.
@@ -6304,36 +7055,42 @@ private bool IsDisposableService(Request request)
/// Scoped expression or originally passed expression.
protected virtual Expression ApplyReuse(Expression serviceExpr, IReuse reuse, bool tracksTransientDisposable, Request request)
{
+ // optimization for already activated singleton
+ if (serviceExpr.NodeType == ExpressionType.Constant &&
+ reuse is SingletonReuse && request.Rules.EagerCachingSingletonForFasterAccess &&
+ !Setup.PreventDisposal && !Setup.WeaklyReferenced)
+ return serviceExpr;
+
var serviceType = serviceExpr.Type;
- // The singleton optimization: eagerly create singleton during the construction of object graph.
+ // Optimize: eagerly create singleton during the construction of object graph,
+ // but only for root singleton and not for singleton dependency inside singleton, because of double compilation work
if (reuse is SingletonReuse &&
- !tracksTransientDisposable &&
+ request.Rules.EagerCachingSingletonForFasterAccess &&
+ // except: For decorators and wrappers, when tracking tansient disposable and for lazy consumption in Func
FactoryType == FactoryType.Service &&
- !request.IsWrappedInFunc() &&
- request.Container.Rules.EagerCachingSingletonForFasterAccess)
+ !tracksTransientDisposable &&
+ !request.IsWrappedInFunc())
{
- var factoryDelegate = serviceExpr.CompileToDelegate(request.Container);
-
+ var factoryDelegate = serviceExpr.CompileToDelegate();
if (Setup.PreventDisposal)
{
var factory = factoryDelegate;
- factoryDelegate = (st, cs, rs) => new[] { factory(st, cs, rs) };
+ factoryDelegate = (_, cs, rs) => new[] {factory(null, cs, rs)};
}
if (Setup.WeaklyReferenced)
{
var factory = factoryDelegate;
- factoryDelegate = (st, cs, rs) => new WeakReference(factory(st, cs, rs));
+ factoryDelegate = (_, cs, rs) => new WeakReference(factory(null, cs, rs));
}
- var singletonScope = request.Scopes.SingletonScope;
-
+ var singletonScope = request.SingletonScope;
var singletonId = singletonScope.GetScopedItemIdOrSelf(FactoryID);
- singletonScope.GetOrAdd(singletonId, () =>
- factoryDelegate(request.Container.ResolutionStateCache, request.ContainerWeakRef, request.Scope));
+ var singleton = singletonScope.GetOrAdd(singletonId, () =>
+ factoryDelegate(null, request.ContainerWeakRef, request.Scope));
- serviceExpr = Container.GetStateItemExpression(singletonId);
+ serviceExpr = Expression.Constant(singleton);
}
else
{
@@ -6343,13 +7100,21 @@ protected virtual Expression ApplyReuse(Expression serviceExpr, IReuse reuse, bo
if (Setup.WeaklyReferenced)
serviceExpr = Expression.New(typeof(WeakReference).GetConstructorOrNull(args: typeof(object)), serviceExpr);
- var scopeExpr = reuse.GetScopeExpression(request);
+ var reuseV3 = reuse as IReuseV3;
+ if (reuseV3 != null)
+ {
+ serviceExpr = reuseV3.Apply(request, tracksTransientDisposable, serviceExpr);
+ }
+ else
+ {
+ var scopeExpr = reuse.GetScopeExpression(request);
- // For transient disposable we does not care to bind to specific ID, because it should be created each time.
- var scopedId = tracksTransientDisposable ? -1 : reuse.GetScopedItemIdOrSelf(FactoryID, request);
- serviceExpr = Expression.Call(scopeExpr, "GetOrAdd", ArrayTools.Empty(),
- Expression.Constant(scopedId),
- Expression.Lambda(serviceExpr, ArrayTools.Empty()));
+ // For transient disposable we don't care to bind to specific ID, because it should be created each time.
+ var scopedId = tracksTransientDisposable ? -1 : reuse.GetScopedItemIdOrSelf(FactoryID, request);
+ serviceExpr = Expression.Call(scopeExpr, "GetOrAdd", ArrayTools.Empty(),
+ Expression.Constant(scopedId),
+ Expression.Lambda(serviceExpr, ArrayTools.Empty()));
+ }
}
// Unwrap WeakReference and/or array preventing disposal
@@ -6364,31 +7129,7 @@ protected virtual Expression ApplyReuse(Expression serviceExpr, IReuse reuse, bo
Expression.Convert(serviceExpr, typeof(object[])),
Expression.Constant(0, typeof(int)));
- return Expression.Convert(serviceExpr, serviceType);
- }
-
- /// Throws if request direct or further ancestor has longer reuse lifespan,
- /// and throws if that is true. Until there is a Func wrapper in between.
- /// Reuse to check. Request to resolve.
- protected static void ThrowIfReuseHasShorterLifespanThanParent(IReuse reuse, Request request)
- {
- // Fast check: if reuse is not applied or the rule set then skip the check.
- if (reuse == null || reuse.Lifespan == 0 ||
- !request.Container.Rules.ThrowIfDependencyHasShorterReuseLifespan)
- return;
-
- var parent = request.ParentOrWrapper;
- if (parent.IsEmpty)
- return;
-
- var parentWithLongerLifespan = parent.Enumerate()
- .TakeWhile(r => r.FactoryType != FactoryType.Wrapper || !r.GetActualServiceType().IsFunc())
- .FirstOrDefault(r => r.FactoryType == FactoryType.Service
- && r.ReuseLifespan > reuse.Lifespan);
-
- if (parentWithLongerLifespan != null)
- Throw.It(Error.DependencyHasShorterReuseLifespan,
- request.PrintCurrent(), reuse, parentWithLongerLifespan);
+ return serviceExpr;
}
/// Creates factory delegate from service expression and returns it.
@@ -6398,7 +7139,7 @@ protected static void ThrowIfReuseHasShorterLifespanThanParent(IReuse reuse, Req
public virtual FactoryDelegate GetDelegateOrDefault(Request request)
{
var expression = GetExpressionOrDefault(request);
- return expression == null ? null : expression.CompileToDelegate(request.Container);
+ return expression == null ? null : expression.CompileToDelegate();
}
/// Returns nice string representation of factory.
@@ -6416,14 +7157,19 @@ public override string ToString()
s.Append(", Metadata=").Print(Setup.Metadata);
if (Setup.Condition != null)
s.Append(", HasCondition");
+
if (Setup.OpenResolutionScope)
s.Append(", OpensResolutionScope");
+ else if (Setup.AsResolutionCall)
+ s.Append(", AsResolutionScope");
+
return s.Append("}").ToString();
}
#region Implementation
private static int _lastFactoryID;
+ private IReuse _reuse;
private Setup _setup;
#endregion
@@ -6452,7 +7198,7 @@ public static class Parameters
public static ParameterSelector Of = request => ParameterServiceInfo.Of;
/// Returns service info which considers each parameter as optional.
- public static ParameterSelector IfUnresolvedReturnDefault =
+ public static ParameterSelector IfUnresolvedReturnDefault =
request => pi => ParameterServiceInfo.Of(pi).WithDetails(ServiceDetails.IfUnresolvedReturnDefault, request);
/// Combines source selector with other. Other is used as fallback when source returns null.
@@ -6475,7 +7221,7 @@ public static ParameterSelector And(this ParameterSelector source, ParameterSele
}
/// Overrides source parameter rules with specific parameter details. If it is not your parameter just return null.
- /// Original parameters rules
+ /// Original parameters rules
/// Should return specific details or null.
/// New parameters rules.
public static ParameterSelector Details(this ParameterSelector source, Func getDetailsOrNull)
@@ -6497,7 +7243,7 @@ public static ParameterSelector Details(this ParameterSelector source, Func(optional) Required metadata key Required metadata or value.
/// New parameters rules.
public static ParameterSelector Name(this ParameterSelector source, string name,
- Type requiredServiceType = null, object serviceKey = null,
+ Type requiredServiceType = null, object serviceKey = null,
IfUnresolved ifUnresolved = IfUnresolved.Throw, object defaultValue = null,
string metadataKey = null, object metadata = null)
{
@@ -6515,14 +7261,14 @@ public static ParameterSelector Name(this ParameterSelector source, string name,
}
/// Adds to selector service info for parameter identified by type .
- /// Type of parameter. Source selector.
+ /// Type of parameter. Source selector.
/// (optional) (optional)
/// (optional) By default throws exception if unresolved.
/// (optional) Specifies default value to use when unresolved.
/// (optional) Required metadata key Required metadata or value.
/// Combined selector.
public static ParameterSelector Type(this ParameterSelector source,
- Type requiredServiceType = null, object serviceKey = null,
+ Type requiredServiceType = null, object serviceKey = null,
IfUnresolved ifUnresolved = IfUnresolved.Throw, object defaultValue = null,
string metadataKey = null, object metadata = null)
{
@@ -6532,7 +7278,7 @@ public static ParameterSelector Name(this ParameterSelector source, string name,
/// Specify parameter by type and set custom value to it.
/// Parameter type.
- /// Original parameters rules.
+ /// Original parameters rules.
/// Custom value provider.
/// New parameters rules.
public static ParameterSelector Type(this ParameterSelector source, Func getCustomValue)
@@ -6573,11 +7319,11 @@ public static partial class PropertiesAndFields
PropertyOrFieldServiceInfo.Of(m).WithDetails(ServiceDetails.Of(ifUnresolved: ifUnresolved), r);
return r =>
{
- var properties = r.ImplementationType.GetDeclaredAndBase(_ => _.DeclaredProperties)
+ var properties = r.ImplementationType.GetMembers(_ => _.DeclaredProperties, includeBase: true)
.Where(p => p.IsInjectable(withNonPublic, withPrimitive))
.Select(m => getInfo(m, r));
return !withFields ? properties :
- properties.Concat(r.ImplementationType.GetDeclaredAndBase(_ => _.DeclaredFields)
+ properties.Concat(r.ImplementationType.GetMembers(_ => _.DeclaredFields, includeBase: true)
.Where(f => f.IsInjectable(withNonPublic, withPrimitive))
.Select(m => getInfo(m, r)));
};
@@ -6657,10 +7403,9 @@ public static PropertiesAndFieldsSelector Details(this PropertiesAndFieldsSelect
/// (optional) By default returns default value if unresolved.
/// (optional) Specifies default value to use when unresolved.
/// (optional) Required metadata key Required metadata or value.
- ///
/// Combined selector.
public static PropertiesAndFieldsSelector Name(this PropertiesAndFieldsSelector source, string name,
- Type requiredServiceType = null, object serviceKey = null,
+ Type requiredServiceType = null, object serviceKey = null,
IfUnresolved ifUnresolved = IfUnresolved.ReturnDefault, object defaultValue = null,
string metadataKey = null, object metadata = null)
{
@@ -6707,13 +7452,30 @@ public static bool IsInjectable(this FieldInfo field, bool withNonPublic = false
public sealed class ReflectionFactory : Factory
{
/// Non-abstract service implementation type. May be open generic.
- public override Type ImplementationType { get { return _implementationType; } }
+ public override Type ImplementationType
+ {
+ get
+ {
+ if (_implementationType == null && _implementationTypeProvider != null)
+ SetKnownImplementationType(_implementationTypeProvider(), Made);
+ return _implementationType;
+ }
+ }
/// Provides closed-generic factory for registered open-generic variant.
public override IConcreteFactoryGenerator FactoryGenerator { get { return _factoryGenerator; } }
/// Injection rules set for Constructor/FactoryMethod, Parameters, Properties and Fields.
- public readonly Made Made;
+ public override Made Made { get { return _made; } }
+
+ /// Sets single ctor in case there are no special rules or factory method. To don;t do this twice.
+ ///
+ public void SetKnownSingleCtor(ConstructorInfo ctor)
+ {
+ _knownSingleCtor = ctor;
+ }
+
+ private ConstructorInfo _knownSingleCtor;
/// Creates factory providing implementation type, optional reuse and setup.
/// (optional) Optional if Made.FactoryMethod is present Non-abstract close or open generic type.
@@ -6721,21 +7483,25 @@ public sealed class ReflectionFactory : Factory
public ReflectionFactory(Type implementationType = null, IReuse reuse = null, Made made = null, Setup setup = null)
: base(reuse, setup)
{
- Made = made ?? Made.Default;
- _implementationType = GetValidImplementationTypeOrDefault(implementationType);
+ _made = made ?? Made.Default;
+ SetKnownImplementationType(implementationType, _made);
+ }
- var originalImplementationType = _implementationType ?? implementationType;
- if (originalImplementationType == typeof(object) || // for open-generic T implementation
- originalImplementationType != null && ( // for open-generic X implementation
- originalImplementationType.IsGenericDefinition() ||
- originalImplementationType.IsGenericParameter))
- _factoryGenerator = new ClosedGenericFactoryGenerator(this);
+ /// Creates factory providing implementation type, optional reuse and setup.
+ /// Provider of non-abstract close or open generic type.
+ /// (optional) (optional) (optional)
+ public ReflectionFactory(Func implementationTypeProvider, IReuse reuse = null, Made made = null, Setup setup = null)
+ : base(reuse, setup)
+ {
+ _made = made ?? Made.Default;
+ _implementationTypeProvider = implementationTypeProvider.ThrowIfNull();
}
/// Add to base rules: do not cache if Made is context based.
/// Context. True if factory expression could be cached.
protected override bool IsFactoryExpressionCacheable(Request request)
{
+ // todo: review Made and may be move to IsContextDependent
return base.IsFactoryExpressionCacheable(request)
&& (Made == Made.Default
// Property injection.
@@ -6749,7 +7515,7 @@ protected override bool IsFactoryExpressionCacheable(Request request)
|| (Made.FactoryMethodKnownResultType != null && !Made.HasCustomDependencyValue));
}
- /// Creates service expression, so for registered implementation type "Service",
+ /// Creates service expression, so for registered implementation type "Service",
/// you will get "new Service()". If there is specified, then expression will
/// contain call to returned by reuse.
/// Request for service to resolve. Created expression.
@@ -6757,32 +7523,37 @@ public override Expression CreateExpressionOrDefault(Request request)
{
var factoryMethod = GetFactoryMethod(request);
+ var container = request.Container;
+
// If factory method is instance method, then resolve factory instance first.
Expression factoryExpr = null;
if (factoryMethod.FactoryServiceInfo != null)
{
var factoryRequest = request.Push(factoryMethod.FactoryServiceInfo);
- var factoryFactory = factoryRequest.Container.ResolveFactory(factoryRequest);
- factoryExpr = factoryFactory == null ? null : factoryFactory.GetExpressionOrDefault(factoryRequest);
+ var factoryFactory = container.ResolveFactory(factoryRequest);
+ if (factoryFactory == null)
+ return null;
+ factoryExpr = factoryFactory.GetExpressionOrDefault(factoryRequest);
if (factoryExpr == null)
return null;
}
- var containerRules = request.Container.Rules;
+ var containerRules = container.Rules;
+ var allParamsAreConstants = true;
Expression[] paramExprs = null;
- var constructorOrMethod = factoryMethod.ConstructorOrMethodOrMember as MethodBase;
- if (constructorOrMethod != null)
+ var ctorOrMethod = factoryMethod.ConstructorOrMethodOrMember as MethodBase;
+ if (ctorOrMethod != null)
{
- var parameters = constructorOrMethod.GetParameters();
+ var parameters = ctorOrMethod.GetParameters();
if (parameters.Length != 0)
{
paramExprs = new Expression[parameters.Length];
- var selector =
+ var selector =
containerRules.OverrideRegistrationMade
- ? containerRules.Parameters.OverrideWith(Made.Parameters)
- : Made.Parameters.OverrideWith(containerRules.Parameters);
+ ? Made.Parameters.OverrideWith(containerRules.Parameters)
+ : containerRules.Parameters.OverrideWith(Made.Parameters);
var parameterSelector = selector(request);
@@ -6820,42 +7591,49 @@ public override Expression CreateExpressionOrDefault(Request request)
var customValue = paramInfo.Details.CustomValue;
if (customValue != null)
customValue.ThrowIfNotOf(paramRequest.ServiceType, Error.InjectedCustomValueIsOfDifferentType, paramRequest);
- paramExpr = paramRequest.Container
- .GetOrAddStateItemExpression(customValue, paramRequest.ServiceType);
+ paramExpr = container.GetOrAddStateItemExpression(customValue, paramRequest.ServiceType);
}
else
{
- var paramFactory = paramRequest.Container.ResolveFactory(paramRequest);
+ var paramFactory = container.ResolveFactory(paramRequest);
paramExpr = paramFactory == null ? null : paramFactory.GetExpressionOrDefault(paramRequest);
- // Meant that parent Or parameter itself allows default value,
+ // Meant that parent Or parameter itself allows default value,
// otherwise we did not get null but exception
if (paramExpr == null)
{
- // Check if parameter itself (without propagated parent details)
+ // Check if parameter itself (without propagated parent details)
// does not allow default, then stop checking the rest of parameters.
if (paramInfo.Details.IfUnresolved == IfUnresolved.Throw)
return null;
var defaultValue = paramInfo.Details.DefaultValue;
paramExpr = defaultValue != null
- ? paramRequest.Container.GetOrAddStateItemExpression(defaultValue)
+ ? container.GetOrAddStateItemExpression(defaultValue)
: paramRequest.ServiceType.GetDefaultValueExpression();
}
}
}
+ if (paramExpr.NodeType != ExpressionType.Constant &&
+ !(paramExpr.NodeType == ExpressionType.Convert &&
+ ((UnaryExpression)paramExpr).Operand.NodeType == ExpressionType.Constant))
+ allParamsAreConstants = false;
+
paramExprs[i] = paramExpr;
}
}
}
- return CreateServiceExpression(factoryMethod.ConstructorOrMethodOrMember, factoryExpr, paramExprs, request);
+ return CreateServiceExpression(factoryMethod.ConstructorOrMethodOrMember, factoryExpr, paramExprs, request,
+ allParamsAreConstants);
}
#region Implementation
- private readonly Type _implementationType;
- private readonly ClosedGenericFactoryGenerator _factoryGenerator;
+ private Type _implementationType; // non-readonly to be set by provider
+ private readonly Func _implementationTypeProvider;
+ private readonly Made _made;
+ private ClosedGenericFactoryGenerator _factoryGenerator;
private sealed class ClosedGenericFactoryGenerator : IConcreteFactoryGenerator
{
@@ -6869,7 +7647,7 @@ public ClosedGenericFactoryGenerator(ReflectionFactory openGenericFactory)
_openGenericFactory = openGenericFactory;
}
- public Factory GetGeneratedFactoryOrDefault(Request request)
+ public Factory GetGeneratedFactory(Request request, bool ifErrorReturnDefault = false)
{
var serviceType = request.GetActualServiceType();
@@ -6883,103 +7661,153 @@ public Factory GetGeneratedFactoryOrDefault(Request request)
return generatedFactory;
}
- request = request.WithResolvedFactory(_openGenericFactory);
+ var openFactory = _openGenericFactory;
+ request = request.WithResolvedFactory(openFactory,
+ skipRecursiveDependencyCheck: ifErrorReturnDefault, skipCaptiveDependencyCheck: ifErrorReturnDefault);
- var implementationType = _openGenericFactory._implementationType;
+ var implType = openFactory._implementationType;
- var closedTypeArgs =
- implementationType == null ? serviceType.GetGenericParamsAndArgs()
- : implementationType == serviceType.GetGenericDefinitionOrNull() ? serviceType.GetGenericParamsAndArgs()
- : implementationType.IsGenericParameter ? new[] { serviceType }
- : GetClosedTypeArgsOrNullForOpenGenericType(implementationType, serviceType, request);
+ var closedTypeArgs = implType == null || implType == serviceType.GetGenericDefinitionOrNull()
+ ? serviceType.GetGenericParamsAndArgs()
+ : implType.IsGenericParameter ? new[] { serviceType }
+ : GetClosedTypeArgsOrNullForOpenGenericType(implType, serviceType, request, ifErrorReturnDefault);
if (closedTypeArgs == null)
return null;
- var closedMade = _openGenericFactory.Made;
- if (closedMade.FactoryMethod != null)
+ var made = openFactory.Made;
+ if (made.FactoryMethod != null)
{
- var factoryMethod = closedMade.FactoryMethod(request)
- .ThrowIfNull(Error.GotNullFactoryWhenResolvingService, request);
+ var factoryMethod = made.FactoryMethod(request);
+ if (factoryMethod == null)
+ return ifErrorReturnDefault ? null
+ : Throw.For(Error.GotNullFactoryWhenResolvingService, request);
- var checkMatchingType = implementationType != null && implementationType.IsGenericParameter;
+ var checkMatchingType = implType != null && implType.IsGenericParameter;
var closedFactoryMethod = GetClosedFactoryMethodOrDefault(
factoryMethod, closedTypeArgs, request, checkMatchingType);
// may be null only for IfUnresolved.ReturnDefault or check for matching type is failed
- if (closedFactoryMethod == null)
+ if (closedFactoryMethod == null)
return null;
- closedMade = Made.Of(closedFactoryMethod, closedMade.Parameters, closedMade.PropertiesAndFields);
+ made = Made.Of(closedFactoryMethod, made.Parameters, made.PropertiesAndFields);
}
- Type closedImplementationType = null;
- if (implementationType != null)
+ Type closedImplType = null;
+ if (implType != null)
{
- if (implementationType.IsGenericParameter)
- closedImplementationType = closedTypeArgs[0];
+ if (implType.IsGenericParameter)
+ closedImplType = closedTypeArgs[0];
else
- closedImplementationType = Throw.IfThrows(
- () => implementationType.MakeGenericType(closedTypeArgs),
- request.IfUnresolved == IfUnresolved.Throw,
- Error.NoMatchedGenericParamConstraints, implementationType, request);
+ closedImplType = Throw.IfThrows(
+ () => implType.MakeGenericType(closedTypeArgs),
+ !ifErrorReturnDefault && request.IfUnresolved == IfUnresolved.Throw,
+ Error.NoMatchedGenericParamConstraints, implType, request);
- if (closedImplementationType == null)
+ if (closedImplType == null)
return null;
}
- var closedGenericFactory = new ReflectionFactory(
- closedImplementationType, _openGenericFactory.Reuse, closedMade, _openGenericFactory.Setup);
+ var closedGenericFactory = new ReflectionFactory(closedImplType, openFactory.Reuse, made, openFactory.Setup);
- // Storing generated factory ID to service type/key mapping
+ // Storing generated factory ID to service type/key mapping
// to find/remove generated factories when needed
_generatedFactories.Swap(_ => _.AddOrUpdate(generatedFactoryKey, closedGenericFactory));
return closedGenericFactory;
}
private readonly ReflectionFactory _openGenericFactory;
- private readonly Ref, ReflectionFactory>>
+ private readonly Ref, ReflectionFactory>>
_generatedFactories = Ref.Of(ImTreeMap