/// <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()));
        }
Пример #6
0
        /// <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");
            }
        }
Пример #7
0
 /// <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));
        }
Пример #9
0
        /// <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);
        }
Пример #10
0
        /// <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));
        }
Пример #15
0
        /// <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);
            }
        }
Пример #16
0
 internal static void RegisterScopedInternal(ITargetContainer targetContainer, Type objectType, Type serviceType, IMemberBindingBehaviour memberBinding)
 {
     targetContainer.Register(Target.ForType(objectType, memberBinding).Scoped(), serviceType: serviceType);
 }
Пример #17
0
 internal static void RegisterSingletonInternal(ITargetContainer builder, Type objectType, Type serviceType, IMemberBindingBehaviour memberBinding)
 {
     builder.Register(Target.ForType(objectType, memberBinding).Singleton(), serviceType: serviceType);
 }