/// <summary> /// Constructs a <see cref="DelegateTarget"/> from the passed factory delegate (optionally with the given <paramref name="declaredType"/>, /// <paramref name="scopeBehaviour"/> and <paramref name="scopePreference"/>) and registers it in the target container. /// </summary> /// <param name="targetContainer">The target container in which the new target is to registered</param> /// <param name="factory">The factory delegate that is to be executed by the <see cref="DelegateTarget"/> that is created.</param> /// <param name="declaredType">Optional - if provided, then it overrides the <see cref="ITarget.DeclaredType"/> of the <see cref="DelegateTarget"/> /// that is created which, in turn, will change the type against which the target will be registered in the target container. If null, then /// the return type of the factory will be used.</param> /// <param name="scopeBehaviour">Optional. Controls how the object generated from the factory delegate will be /// tracked if the target is executed within an <see cref="ContainerScope" />. The default is <see cref="ScopeBehaviour.None" /> - which means /// no disposal will take place. Be careful with changing this - if the delegate produces new instances each time it's used, then /// <see cref="ScopeBehaviour.Implicit"/> is suitable; if not, then only <see cref="ScopeBehaviour.None"/> or <see cref="ScopeBehaviour.Explicit"/> /// are suitable.</param> /// <param name="scopePreference">Optional. If <paramref name="scopeBehaviour"/> is not <see cref="ScopeBehaviour.None"/>, then this controls the /// type of scope in which the instance should be tracked. Defaults to <see cref="ScopePreference.Current"/>. <see cref="ScopePreference.Root"/> /// should be used if the result of the delegate is effectively a singleton.</param> public static void RegisterDelegate( this ITargetContainer targetContainer, Delegate factory, Type declaredType = null, ScopeBehaviour scopeBehaviour = ScopeBehaviour.Implicit, ScopePreference scopePreference = ScopePreference.Current) { if (targetContainer == null) { throw new ArgumentNullException(nameof(targetContainer)); } if (factory == null) { throw new ArgumentNullException(nameof(factory)); } ITarget toRegister = null; if (factory.GetMethodInfo().GetParameters()?.Length > 0) { toRegister = new DelegateTarget(factory, declaredType, scopeBehaviour: scopeBehaviour, scopePreference: scopePreference); } else { toRegister = new NullaryDelegateTarget(factory, declaredType, scopeBehaviour: scopeBehaviour, scopePreference: scopePreference); } targetContainer.Register(toRegister); }
/// <summary> /// Registers an alias for one type to another type. /// /// The created entry will effectively represent a second Resolve call into the container for the aliased type. /// </summary> /// <param name="targetContainer">The builder in which the alias is to be registered</param> /// <param name="aliasType">The type to be registered as an alias</param> /// <param name="originalType">The type being aliased.</param> /// <remarks>Use this when it's important that a given target type is always served through the same compiled target, even when the consumer /// expects it to be of a different type. A very common scenario is when you have a singleton instance of the <paramref name="originalType" />, /// and need to serve that same instance for <paramref name="aliasType"/>. If you register the same singleton for both types, you get two /// separate singletons for each type, whereas if you create an alias, both will be served by the same alias. /// </remarks> public static void RegisterAlias(this ITargetContainer targetContainer, Type aliasType, Type originalType) { if (targetContainer == null) { throw new ArgumentNullException(nameof(targetContainer)); } if (aliasType == originalType) { throw new ArgumentException("The aliased type and its alias must be different", nameof(aliasType)); } ITarget target = new ResolvedTarget(originalType); // if there's no implicit conversion to our alias type from the aliased type, but there is // the other way around, then we need to stick in an explicit change of type, otherwise the registration will // fail. This does, unfortunately, give rise to the situation where we could be performing an invalid cast - but that // will come out in the wash at runtime. if (!aliasType.IsAssignableFrom(originalType) && originalType.IsAssignableFrom(aliasType)) { target = new ChangeTypeTarget(target, aliasType); } targetContainer.Register(target, aliasType); }
/// <summary> /// Sets the passed <paramref name="option"/> into the <paramref name="targets"/> target container, associating /// it with the given <paramref name="serviceType"/>. /// /// The value can later be retrieved through a call to <see cref="GetOption{TOption, TService}(ITargetContainer, TOption)"/> /// or <see cref="GetOption{TOption}(ITargetContainer, Type, TOption)"/>, passing the same type, or a derived type. /// </summary> /// <typeparam name="TOption">The type of option to be set.</typeparam> /// <param name="targets">The target container into which the option will be set.</param> /// <param name="option">The option value to be set</param> /// <param name="serviceType">The type against which the option is to be set. It's called 'serviceType' because the majority /// of the time, you will used this method and its generic overload to customise behaviour for specific types. If <c>null</c>, /// then it's equivalent to calling <see cref="SetOption{TOption}(ITargetContainer, TOption)"/>.</param> /// <returns>The target container on which the method is called, to enable method chaining.</returns> public static ITargetContainer SetOption <TOption>(this ITargetContainer targets, TOption option, Type serviceType) where TOption : class { if (targets == null) { throw new ArgumentNullException(nameof(targets)); } if (option == null) { throw new ArgumentNullException(nameof(option)); } if (serviceType == null) { return(SetOption <TOption>(targets, option)); } // this is cheeky - we create an instance of OptionContainer<TOption> to service // the type OptionContainer<TService, TOption>. When we retrieve it in the various GetOption // methods, we *expect* only an OptionContainer<TOption> despite the fact that we fetch // a target for the <TService, TOption> pair. That's part of the reason why all this stuff is // internal. targets.Register(new OptionContainer <TOption>(option), typeof(IOptionContainer <,>).MakeGenericType(serviceType, typeof(TOption))); return(targets); }
/// <summary> /// Registers the expression in the target container /// </summary> /// <param name="targetContainer">The target container in which the registration will be made.</param> /// <param name="expression">The expression to be registered.</param> /// <param name="declaredType">Optional. The <see cref="ITarget.DeclaredType"/> of the target to be created, /// if different from the <see cref="Expression.Type"/> of the <paramref name="expression"/> (or its /// <see cref="LambdaExpression.Body"/> if the expression is a <see cref="LambdaExpression"/>). /// /// Will also override the type against which the expression will be registered if provided.</param> /// <param name="scopeBehaviour">Optional. Controls how the object generated from the compiled expression will be /// tracked if the target is executed within an <see cref="ContainerScope" />. The default is <see cref="ScopeBehaviour.Implicit" />.</param> /// <param name="scopePreference">Optional. If <paramref name="scopeBehaviour"/> is not <see cref="ScopeBehaviour.None"/>, then this controls the /// type of scope in which the instance should be tracked. Defaults to <see cref="ScopePreference.Current"/>. <see cref="ScopePreference.Root"/> /// should be used if the result of the delegate is effectively a singleton.</param> public static void RegisterExpression( this ITargetContainer targetContainer, Expression expression, Type declaredType = null, ScopeBehaviour scopeBehaviour = ScopeBehaviour.Implicit, ScopePreference scopePreference = ScopePreference.Current) { if (targetContainer == null) { throw new ArgumentNullException(nameof(targetContainer)); } if (expression == null) { throw new ArgumentNullException(nameof(expression)); } ITarget toRegister = new ExpressionTarget(expression, declaredType); if (scopeBehaviour == ScopeBehaviour.Explicit) { toRegister = toRegister.Scoped(); } else if (scopeBehaviour == ScopeBehaviour.None) { toRegister = toRegister.Unscoped(); } targetContainer.Register(toRegister); }
/// <summary> /// Register a type by constructor (represented by the expression <paramref name="newExpr"/>). /// </summary> /// <typeparam name="TObject"></typeparam> /// <param name="targets"></param> /// <param name="newExpr">A lambda expression whose <see cref="LambdaExpression.Body"/> is a <see cref="NewExpression"/> /// which identifies the constructor that is to be used to create the instance of <typeparamref name="TObject"/>. An exception /// will be thrown if the lambda does not follow this pattern.</param> /// <param name="configureMemberBinding">A callback which configures a custom member binding via the /// <see cref="IMemberBindingBehaviourBuilder{TInstance}"/> interface. A new builder will be created and passed to this /// callback.</param> /// <remarks>Note that you can achieve a similar result by simply registering an expression which /// represents a call to a type's constructor. /// /// **Generic constructors** /// If you wish to register an open generic type by constructor through the use of an expression, then you /// need to use the <see cref="RegisterGenericConstructor{TObject}(ITargetContainer, Expression{Func{TObject}}, IMemberBindingBehaviour)"/> /// overload.</remarks> public static void RegisterConstructor <TObject>( this ITargetContainer targets, Expression <Func <TObject> > newExpr, Action <IMemberBindingBehaviourBuilder <TObject> > configureMemberBinding) { if (targets == null) { throw new ArgumentNullException(nameof(targets)); } if (configureMemberBinding == null) { throw new ArgumentNullException(nameof(configureMemberBinding)); } var ctor = Extract.Constructor(newExpr ?? throw new ArgumentNullException(nameof(newExpr))); if (ctor == null) { throw new ArgumentException($"The expression ${newExpr} does not represent a NewExpression", nameof(newExpr)); } var behaviour = MemberBindingBehaviour.For <TObject>(); configureMemberBinding(behaviour); targets.Register(Target.ForConstructor(ctor, behaviour.BuildBehaviour())); }
/// <summary> /// Called to register multiple targets against the same type. /// /// It is the same as calling <see cref="ITargetContainer.Register(ITarget, Type)"/> multiple times /// with the different targets. /// </summary> /// <param name="targetContainer">The container on which the registration is to be performed.</param> /// <param name="targets">The targets to be registered - all must support a common service type (potentially /// passed in the <paramref name="commonServiceType"/> argument.</param> /// <param name="commonServiceType">Optional - if provided, then this will be used as the common service type /// for registration. If not provided, then the <see cref="ITarget.DeclaredType"/> of the first target /// will be used.</param> /// <remarks>If the container has the capability to create enumerables automatically (enabled by the /// <see cref="Configuration.InjectEnumerables"/> target container behaviour - which is switched on by default) /// then each target will be returned when an IEnumerable of the common service type is requested.</remarks> public static void RegisterMultiple(this ITargetContainer targetContainer, IEnumerable <ITarget> targets, Type commonServiceType = null) { if (targets == null) { throw new ArgumentNullException(nameof(targets)); } var targetArray = targets.ToArray(); if (targets.Any(t => t == null)) { throw new ArgumentException("All targets must be non-null", "targets"); } // for now I'm going to take the common type from the first target. if (commonServiceType == null) { commonServiceType = targetArray[0].DeclaredType; } if (targetArray.All(t => t.SupportsType(commonServiceType))) { foreach (var target in targets) { targetContainer.Register(target, commonServiceType); } } else { throw new ArgumentException(string.Format(ExceptionResources.TargetDoesntSupportType_Format, commonServiceType), "target"); } }
/// <summary> /// Registers an instance to be used when resolve a particular service type via the <see cref="ObjectTarget"/> /// </summary> /// <param name="targetContainer">The target container which will receive the registration.</param> /// <param name="obj">Required, but can be <c>null</c>. The instance that will be resolved when the service type is requested.</param> /// <param name="declaredType">Type to be set as the <see cref="ITarget.DeclaredType"/> of the <see cref="ObjectTarget"/> /// that is created for <paramref name="obj"/>, if different from the object's type.</param> /// <param name="serviceType">The service type against which this object is to be registered, if different /// from <paramref name="declaredType"/> (or the object's type).</param> /// <param name="scopeBehaviour">Sets the <see cref="ITarget.ScopeBehaviour"/> for the <see cref="ObjectTarget"/> that's created</param> /// <remarks><c>null</c> objects are implicitly treated as <see cref="System.Object"/> if <paramref name="declaredType"/> is not passed.</remarks> public static void RegisterObject(this ITargetContainer targetContainer, object obj, Type declaredType = null, Type serviceType = null, ScopeBehaviour scopeBehaviour = ScopeBehaviour.None) { if (targetContainer == null) { throw new ArgumentNullException(nameof(targetContainer)); } targetContainer.Register(Target.ForObject(obj, declaredType ?? obj?.GetType(), scopeBehaviour: scopeBehaviour), serviceType ?? declaredType ?? obj?.GetType()); }
/// <summary> /// Creates and registers a target bound to the constructor of a generic type definition using the /// <see cref="Target.ForGenericConstructor{TExample}(Expression{Func{TExample}}, IMemberBindingBehaviour)"/> factory method. /// /// See the documentation on that method for more. /// /// The registration will be made against the open generic type. /// </summary> /// <typeparam name="TExample">Must be a generic type which represents a concrete generic whose generic type definition will be /// bound by the created target. This type is also used as the service type for the registration.</typeparam> /// <param name="targets">The container into which the registration will be made.</param> /// <param name="newExpr">Exemplar expression which is used to identify the constructor to be bound.</param> /// <param name="memberBindingBehaviour">A member binding behaviour to be passed to the created target</param> /// <seealso cref="Target.ForGenericConstructor{TExample}(Expression{Func{TExample}}, IMemberBindingBehaviour)"/> public static void RegisterGenericConstructor <TExample>( this ITargetContainer targets, Expression <Func <TExample> > newExpr, IMemberBindingBehaviour memberBindingBehaviour = null) { if (targets == null) { throw new ArgumentNullException(nameof(targets)); } targets.Register(Target.ForGenericConstructor(newExpr, memberBindingBehaviour)); }
/// <summary> /// Implementation of <see cref="ITargetContainer.Register(ITarget, Type)"/>. /// </summary> /// <param name="target">The target to be registered</param> /// <param name="serviceType">The service type against which the <paramref name="target"/> is to be registered, if /// different from the target's <see cref="ITarget.DeclaredType"/>.</param> /// <remarks>This implementation creates a child <see cref="ITargetContainer"/> for the <paramref name="serviceType"/> /// with a call to the protected method <see cref="EnsureContainer(Type)"/> if one doesn't already exist. /// /// The registration is then delegated to that child container's own implementation of /// <see cref="ITargetContainer.Register(ITarget, Type)"/>.</remarks> public virtual void Register(ITarget target, Type serviceType = null) { if (target == null) { throw new ArgumentNullException(nameof(target)); } serviceType = serviceType ?? target.DeclaredType; ITargetContainer container = EnsureContainer(serviceType); container.Register(target, serviceType); }
/// <summary> /// Batch-registers multiple targets against their <see cref="ITarget.DeclaredType"/>. /// /// This is the same as calling <see cref="ITargetContainer.Register(ITarget, Type)"/> for each of the /// <paramref name="targets"/>, except the type cannot be overriden from the target's DeclaredType. /// </summary> /// <param name="targetContainer">The target container on which the registrations are to be performed.</param> /// <param name="targets">The targets to be registered</param> public static void RegisterAll(this ITargetContainer targetContainer, IEnumerable <ITarget> targets) { if (targetContainer == null) { throw new ArgumentNullException(nameof(targetContainer)); } foreach (var target in targets) { targetContainer.Register(target); } }
/// <summary> /// Sets the passed <paramref name="option"/> into the <paramref name="targets"/> target container. /// /// The value can later be retrieved through a call to <see cref="GetOption{TOption}(ITargetContainer, TOption)"/> /// or one of its overloads. /// </summary> /// <typeparam name="TOption">The type of option to be set.</typeparam> /// <param name="targets">The target container into which the option will be set.</param> /// <param name="option">The option value to be set</param> /// <returns>The target container on which the method is called, to enable method chaining.</returns> public static ITargetContainer SetOption <TOption>(this ITargetContainer targets, TOption option) where TOption : class { if (targets == null) { throw new ArgumentNullException(nameof(targets)); } if (option == null) { throw new ArgumentNullException(nameof(option)); } targets.Register(new OptionContainer <TOption>(option), typeof(IOptionContainer <TOption>)); return(targets); }
/// <summary> /// Same as <see cref="RegisterGenericConstructor{TObject}(ITargetContainer, Expression{Func{TObject}}, IMemberBindingBehaviour)"/> /// except this creates the target and then registers it against a generic base or interface of the generic type definition identified /// from <typeparamref name="TExampleService"/>. /// </summary> /// <typeparam name="TExample">Must be a generic type which represents a concrete generic whose generic type definition will be /// bound by the target that is created and registered. The type must inherit or implement the type <typeparamref name="TExampleService"/>.</typeparam> /// <typeparam name="TExampleService">Must be a generic type that is a base or interface of <typeparamref name="TExample"/>. The registration will /// be made against this type's generic type definition.</typeparam> /// <param name="targets">The container into which the registration will be made.</param> /// <param name="newExpr">Exemplar expression which is used to identify the constructor to be bound.</param> /// <param name="memberBindingBehaviour">A member binding behaviour to be passed to the created target</param> /// <seealso cref="Target.ForGenericConstructor{TExample}(Expression{Func{TExample}}, IMemberBindingBehaviour)"/> public static void RegisterGenericConstructor <TExample, TExampleService>( this ITargetContainer targets, Expression <Func <TExample> > newExpr, IMemberBindingBehaviour memberBindingBehaviour = null) where TExample : TExampleService { if (targets == null) { throw new ArgumentNullException(nameof(targets)); } if (!typeof(TExampleService).IsGenericType) { throw new ArgumentException($"Service type {typeof(TExampleService)} is not a generic type", nameof(TExampleService)); } targets.Register(Target.ForGenericConstructor(newExpr, memberBindingBehaviour), typeof(TExampleService).GetGenericTypeDefinition()); }
/// <summary> /// Sets the passed <paramref name="option"/> into the <paramref name="targets"/> target container, associating /// it with the given <typeparamref name="TService"/>. /// /// The value can later be retrieved through a call to <see cref="GetOption{TOption, TService}(ITargetContainer, TOption)"/> /// or <see cref="GetOption{TOption}(ITargetContainer, Type, TOption)"/>, passing the same type, or a derived type. /// </summary> /// <typeparam name="TOption">The type of option to be set.</typeparam> /// <typeparam name="TService">The type against which the option is to be set.</typeparam> /// <param name="targets">The target container into which the option will be set.</param> /// <param name="option">The option value to be set</param> /// <returns>The target container on which the method is called, to enable method chaining.</returns> public static ITargetContainer SetOption <TOption, TService>(this ITargetContainer targets, TOption option) where TOption : class { if (targets == null) { throw new ArgumentNullException(nameof(targets)); } if (option == null) { throw new ArgumentNullException(nameof(option)); } // see long comment in method above targets.Register(new OptionContainer <TOption>(option), typeof(IOptionContainer <TService, TOption>)); return(targets); }
/// <summary> /// Register a type by constructor (represented by the expression <paramref name="newExpr"/>). /// </summary> /// <typeparam name="TObject"></typeparam> /// <param name="targets"></param> /// <param name="newExpr">A lambda expression whose <see cref="LambdaExpression.Body"/> is a <see cref="NewExpression"/> /// which identifies the constructor that is to be used to create the instance of <typeparamref name="TObject"/>.</param> /// <param name="memberBindingBehaviour">Optional. If you wish to bind members on the new instance, passing a member binding /// behaviour here.</param> /// <remarks>Note that you can achieve a similar result by simply registering an expression which /// represents a call to a type's constructor.</remarks> public static void RegisterConstructor <TObject>( this ITargetContainer targets, Expression <Func <TObject> > newExpr, IMemberBindingBehaviour memberBindingBehaviour = null) { if (targets == null) { throw new ArgumentNullException(nameof(targets)); } var ctor = Extract.Constructor(newExpr ?? throw new ArgumentNullException(nameof(newExpr))); if (ctor == null) { throw new ArgumentException($"The expression ${newExpr} does not represent a NewExpression", nameof(newExpr)); } targets.Register(Target.ForConstructor(ctor, memberBindingBehaviour)); }
/// <summary> /// Translates all registrations in <paramref name="services"/> into registered targets in <paramref name="targets"/>. /// </summary> /// <param name="targets">The target container into which the registrations will be added</param> /// <param name="services">The service collection to be registered</param> public static void Populate(this ITargetContainer targets, IServiceCollection services) { if (targets == null) { throw new ArgumentNullException(nameof(targets)); } //register service provider - ensuring that it's marked as unscoped because the lifetimes of //containers which are also scopes are managed by the code that creates them, not by the containers themselves targets.RegisterExpression(context => (IServiceProvider)context, scopeBehaviour: ScopeBehaviour.None); //register scope factory - uses the context as a scope factory (it will choose between the container or //the scope as the actual scope factory that will be used. targets.RegisterExpression(context => new RezolverContainerScopeFactory(context), typeof(IServiceScopeFactory), ScopeBehaviour.None); foreach (var serviceAndTarget in services.Select(s => new { serviceType = s.ServiceType, target = CreateTargetFromService(s) }).Where(st => st.target != null)) { targets.Register(serviceAndTarget.target, serviceAndTarget.serviceType); } }
internal static void RegisterScopedInternal(ITargetContainer targetContainer, Type objectType, Type serviceType, IMemberBindingBehaviour memberBinding) { targetContainer.Register(Target.ForType(objectType, memberBinding).Scoped(), serviceType: serviceType); }
internal static void RegisterSingletonInternal(ITargetContainer builder, Type objectType, Type serviceType, IMemberBindingBehaviour memberBinding) { builder.Register(Target.ForType(objectType, memberBinding).Singleton(), serviceType: serviceType); }