/// <summary> /// Builds an expression which represents an instance of <see cref="IEnumerable{T}"/> whose elements are created by the /// <see cref="EnumerableTarget.Targets"/> of the passed <paramref name="target"/>. /// </summary> /// <param name="target">The target for which an expression is to be built.</param> /// <param name="context">The current compilation context.</param> /// <param name="compiler">The compiler to use when building expressions for child targets.</param> /// <returns>An expression which can be compiled into a delegate that, when executed, will create an instance of the enumerable /// represented by <paramref name="target"/> /// </returns> /// <remarks> /// The compiler is capable of producing both lazy-loaded and eager-loaded enumerables, which can be controlled via /// target container options. /// /// ## Lazy vs Eager loading /// /// The option <see cref="Options.LazyEnumerables"/> is read from the <paramref name="context"/> for the /// <see cref="EnumerableTarget.ElementType"/> of the <paramref name="target"/>. If it is equivalent to <c>true</c> /// (the <see cref="Options.LazyEnumerables.Default"/>), then a lazily-loaded enumerable is constructed which will /// create new instances of each object in the enumerable each time it is enumerated. /// /// If the option is instead equivalent to <c>false</c>, then all instances will be created in advance, and an already-materialised /// enumerable is constructed.</remarks> protected override Expression Build(EnumerableTarget target, IExpressionCompileContext context, IExpressionCompiler compiler) { if (context.GetOption(target.ElementType, Options.LazyEnumerables.Default)) { var funcs = target.Targets.Select(t => compiler.BuildResolveLambdaStrong(t, context.NewContext(target.ElementType)).Compile()) .ToArray(); var lazyType = typeof(LazyEnumerable <>).MakeGenericType(target.ElementType); var ctor = lazyType.GetConstructor(new[] { typeof(Delegate[]) }); var lazy = ctor.Invoke(new object[] { funcs }); return(Expression.Call( Expression.Constant(lazy), "GetInstances", null, context.ResolveContextParameterExpression)); } else { List <Expression> all = new List <Expression>(); for (var f = 0; f < target.Targets.Length; f++) { all.Add(compiler.Build(target.Targets[f], context.NewContext(target.ElementType))); } return(Expression.New( typeof(EagerEnumerable <>).MakeGenericType(target.ElementType).GetConstructors()[0], Expression.NewArrayInit(target.ElementType, all))); } }
/// <summary> /// Builds an expression for the given <paramref name="target"/>. /// </summary> /// <param name="target">The target whose expression is to be built.</param> /// <param name="context">The compilation context.</param> /// <param name="compiler">The expression compiler to be used to build any other expressions for targets /// which might be required by the <paramref name="target" />. Note that unlike on the interface, where this /// parameter is optional, this will always be provided</param> protected override Expression Build(SingletonTarget target, IExpressionCompileContext context, IExpressionCompiler compiler) { var holder = context.ResolveContext.Resolve <SingletonTarget.SingletonContainer>(); int? targetIdOverride = context.GetOption <TargetIdentityOverride>(context.TargetType ?? target.DeclaredType); TypeAndTargetId id = new TypeAndTargetId(context.TargetType ?? target.DeclaredType, targetIdOverride ?? target.Id); var lazy = holder.GetLazy(id); if (lazy == null) { lazy = holder.GetLazy( target, id, compiler.CompileTargetStrong( target.InnerTarget, context.NewContext( context.TargetType ?? target.DeclaredType, // this override is important - when forcing into the root-scope, as we do // for singletons, 'explicit' means absolutely nothing. So, instead of allowing // our child target to choose, we explicitly ensure that all instances are implicitly // tracked within the root scope, if it is one which can track instances. scopeBehaviourOverride: ScopeBehaviour.Implicit, scopePreferenceOverride: ScopePreference.Root)), context); } return(Expression.Call( Expression.Constant(lazy), lazy.GetType().GetMethod("Resolve"), context.ResolveContextParameterExpression)); }
private static void TrackCompilation(ITarget target, IExpressionCompileContext context) { Type theType = context.TargetType ?? target.DeclaredType; int? targetIdOverride = context.GetOption <Runtime.TargetIdentityOverride>(theType); _compiledTargets.GetOrAdd(targetIdOverride ?? target.Id, target); _compileCounts.AddOrUpdate(new TypeAndTargetId(theType, targetIdOverride ?? target.Id), 1, (k, i) => i + 1); }
/// <summary> /// Resolves an expression builder that can build the given target for the given compile context. /// /// Or /// /// Returns null if no builder can be found. /// </summary> /// <param name="target">The target.</param> /// <param name="context">The context.</param> /// <remarks>The function builds a list of all the types in the hierarchy represented /// by the type of the <paramref name="target"/> and, for each of those types which are /// compatible with <see cref="ITarget"/>, it looks for an <see cref="IExpressionBuilder{TTarget}"/> /// which is specialised for that type. If no compatible builder is found, then it attempts /// to find a general purpose <see cref="IExpressionBuilder"/> which can build the type.</remarks> public virtual IExpressionBuilder ResolveBuilder(ITarget target, IExpressionCompileContext context) { if (target == null) { throw new ArgumentNullException(nameof(target)); } if (context == null) { throw new ArgumentNullException(nameof(context)); } return(context.GetOption <IExpressionBuilder>(target.GetType())); }