This commit is contained in:
Eugene Sadovoi 2019-06-12 20:25:19 -04:00
Родитель 063765d99f
Коммит 61ffdbdb1a
21 изменённых файлов: 659 добавлений и 548 удалений

Просмотреть файл

@ -4,10 +4,23 @@ namespace Unity.Exceptions
{
public class InvalidRegistrationException : Exception
{
internal InvalidRegistrationException(Exception ex)
: base(ex.Message, ex)
{
}
internal InvalidRegistrationException(string message)
: base(message)
{
}
internal InvalidRegistrationException(string message, object info)
: base(message)
{
Data.Add(Guid.NewGuid(), info);
}
}
}

Просмотреть файл

@ -0,0 +1,117 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Unity.Exceptions;
using Unity.Resolution;
namespace Unity
{
public abstract class MethodBasePipeline<TMemberInfo> : MemberPipeline<TMemberInfo, object[]>
where TMemberInfo : MethodBase
{
#region Fields
protected readonly UnityContainer Container;
protected readonly ParametersProcessor Processor;
#endregion
#region Constructors
protected MethodBasePipeline(Type attribute, UnityContainer container, ParametersProcessor? processor)
: base(container)
{
Container = container;
Markers = new[] { attribute };
Processor = processor ?? new ParametersProcessor();
}
#endregion
#region Markers
public void AddMarkers(IEnumerable<Type> attributes)
{
Markers = Markers.Concat(attributes).ToArray();
}
public Type[] Markers { get; private set; }
#endregion
#region Overrides
protected override Type MemberType(TMemberInfo info) => info.DeclaringType;
#endregion
#region Implementation
protected virtual IEnumerable<ResolveDelegate<PipelineContext>> ParameterResolvers(ParameterInfo[] parameters, object? injectors)
{
if (null != injectors && injectors is object[] resolvers && 0 != resolvers.Length)
{
for (var i = 0; i < parameters.Length; i++)
{
yield return Processor.ParameterResolver(parameters[i], resolvers[i]);
}
}
else
{
for (var i = 0; i < parameters.Length; i++)
{
yield return Processor.ParameterResolver(parameters[i]);
}
}
}
protected virtual IEnumerable<Expression> ParameterExpressions(ParameterExpression[] expressions, ParameterInfo[] parameters, object? injectors)
{
if (null != injectors && injectors is object[] resolvers && 0 != resolvers.Length)
{
for (var i = 0; i < parameters.Length; i++)
{
yield return Processor.ParameterExpression(expressions[i], parameters[i], resolvers[i]);
}
}
else
{
for (var i = 0; i < parameters.Length; i++)
{
yield return Processor.ParameterExpression(expressions[i], parameters[i]);
}
}
}
protected ParameterExpression ToVariable(ParameterInfo info)
{
if (info.ParameterType.IsByRef)
throw new InvalidRegistrationException($"Invalid By Ref parameter '{info.Name}' ({info.ParameterType})", info);
return Expression.Variable(info.ParameterType, info.Name);
}
protected bool CanResolve(ParameterInfo info)
{
foreach (var node in AttributeFactories)
{
if (null == node.Factory) continue;
var attribute = info.GetCustomAttribute(node.Type);
if (null == attribute) continue;
// If found match, use provided factory to create expression
return Container.CanResolve(info.ParameterType, node.Name(attribute));
}
return Container.CanResolve(info.ParameterType, null);
}
#endregion
}
}

Просмотреть файл

@ -52,15 +52,15 @@ namespace Unity
typeof(string) == parameters[0].ParameterType;
});
protected static readonly Expression NewGuid = Expression.Call(typeof(Guid).GetTypeInfo().GetDeclaredMethod(nameof(Guid.NewGuid)));
protected static readonly Expression CallNewGuidExpr = Expression.Call(typeof(Guid).GetTypeInfo().GetDeclaredMethod(nameof(Guid.NewGuid)));
protected static readonly PropertyInfo DataProperty = typeof(Exception).GetTypeInfo().GetDeclaredProperty(nameof(Exception.Data));
protected static readonly PropertyInfo DataPropertyInfo = typeof(Exception).GetTypeInfo().GetDeclaredProperty(nameof(Exception.Data));
protected static readonly MethodInfo AddMethod = typeof(IDictionary).GetTypeInfo().GetDeclaredMethod(nameof(IDictionary.Add));
protected static readonly MethodInfo AddMethodInfo = typeof(IDictionary).GetTypeInfo().GetDeclaredMethod(nameof(IDictionary.Add));
protected static readonly ParameterExpression ExceptionExpr = Expression.Variable(typeof(Exception), "exception");
protected static readonly MemberExpression ExceptionDataExpr = Expression.MakeMemberAccess(ExceptionExpr, DataProperty);
protected static readonly MemberExpression ExceptionDataExpr = Expression.MakeMemberAccess(ExceptionExpr, DataPropertyInfo);
#endregion

Просмотреть файл

@ -63,7 +63,7 @@ namespace Unity
#region Constructors
public ConstructorDiagnostic(UnityContainer container)
: base(container)
: base(container, new ParametersDiagnosticProcessor())
{
container.Defaults.Set(typeof(Func<Type, InjectionMember, ConstructorInfo>), InjectionValidatingSelector);
}
@ -71,7 +71,7 @@ namespace Unity
#endregion
#region Selection
#region Selection Overrides
public override IEnumerable<object> Select(Type type, InjectionMember[]? injectionMembers)
{
@ -137,12 +137,6 @@ namespace Unity
return new[] { SelectMethod(type, constructors) };
}
/// <summary>
/// Selects default constructor
/// </summary>
/// <param name="type"><see cref="Type"/> to be built</param>
/// <param name="members">All public constructors this type implements</param>
/// <returns></returns>
public override object? LegacySelector(Type type, ConstructorInfo[] members)
{
// TODO: Add validation to legacy selector
@ -230,82 +224,6 @@ namespace Unity
#endregion
#region Expression Overrides
public override IEnumerable<Expression> Express(ref PipelineBuilder builder)
{
#if NETSTANDARD1_0 || NETCOREAPP1_0
var typeInfo = builder.Type.GetTypeInfo();
#else
var typeInfo = builder.Type;
#endif
// Validate if Type could be created
if (typeInfo.IsInterface) return CannotConstructInterfaceExpr.Concat(builder.Express());
if (typeInfo.IsAbstract) return CannotConstructAbstractClassExpr.Concat(builder.Express());
if (typeInfo.IsSubclassOf(typeof(Delegate)))
return CannotConstructDelegateExpr.Concat(builder.Express());
if (typeof(string) == builder.Type)
return TypeIsNotConstructableExpr.Concat(builder.Express());
// Build expression as usual
return base.Express(ref builder);
}
protected override IEnumerable<Expression> GetConstructorExpression(ConstructorInfo info, object? resolvers)
{
var parameters = info.GetParameters();
var variables = parameters.Select(p => Expression.Variable(p.ParameterType, p.Name))
.ToArray();
// Check if has Out or ByRef parameters
var tryBlock = parameters.Any(pi => pi.ParameterType.IsByRef)
// Report error
? (Expression)Expression.Throw(Expression.New(InvalidRegistrationExpressionCtor,
Expression.Constant(CreateErrorMessage("The constructor {1} selected for type {0} has ref or out parameters. Such parameters are not supported for constructor injection.",
info.DeclaringType, info))))
// Create new instance
: Expression.Assign(
PipelineContextExpression.Existing,
Expression.Convert(Expression.New(info, CreateDiagnosticParameterExpressions(info.GetParameters(), resolvers)), typeof(object)));
//
var thenBlock = Expression.Block(variables, CreateParameterExpressions(variables, parameters, resolvers)
.Concat(new[] { Expression.Assign(
PipelineContextExpression.Existing,
Expression.Convert(
Expression.New(info, variables), typeof(object)))}));
yield return Expression.IfThen(NullEqualExisting, thenBlock);
// Add location to dictionary and re-throw
var catchBlock = Expression.Block(tryBlock.Type,
Expression.Call(ExceptionDataExpr, AddMethod,
Expression.Convert(NewGuid, typeof(object)),
Expression.Constant(info, typeof(object))),
Expression.Rethrow(tryBlock.Type));
// Create
yield return Expression.IfThen(NullEqualExisting,
Expression.TryCatch(tryBlock, Expression.Catch(ExceptionExpr, catchBlock)));
// Report error
string CreateErrorMessage(string format, Type type, MethodBase constructor)
{
var parameterDescriptions =
constructor.GetParameters()
.Select(parameter => $"{parameter.ParameterType.FullName} {parameter.Name}");
return string.Format(format, type.FullName, string.Join(", ", parameterDescriptions));
}
}
#endregion
#region Resolver Overrides
public override ResolveDelegate<PipelineContext>? Build(ref PipelineBuilder builder)
@ -373,7 +291,34 @@ namespace Unity
protected override ResolveDelegate<PipelineContext> GetResolverDelegate(ConstructorInfo info, object? resolvers, ResolveDelegate<PipelineContext>? pipeline)
{
var parameterResolvers = CreateDiagnosticParameterResolvers(info.GetParameters(), resolvers).ToArray();
var parameters = info.GetParameters();
// Check for 'out' parameters
if (parameters.Any(param => param.IsOut))
{
return (ref PipelineContext context) =>
{
if (null == context.Existing)
new InvalidRegistrationException($"Constructor {info} with 'out' parameters cannot be injected.", info);
return null == pipeline ? context.Existing : pipeline.Invoke(ref context);
};
}
// Check for 'ref' parameters
if (parameters.Any(param => param.ParameterType.IsByRef))
{
return (ref PipelineContext context) =>
{
if (null == context.Existing)
new InvalidRegistrationException($"Constructor {info} with 'ref' parameters cannot be injected.", info);
return null == pipeline ? context.Existing : pipeline.Invoke(ref context);
};
}
// Create resolver
var parameterResolvers = ParameterResolvers(parameters, resolvers).ToArray();
return (ref PipelineContext context) =>
{
@ -400,7 +345,35 @@ namespace Unity
protected override ResolveDelegate<PipelineContext> GetPerResolveDelegate(ConstructorInfo info, object? resolvers, ResolveDelegate<PipelineContext>? pipeline)
{
var parameterResolvers = CreateDiagnosticParameterResolvers(info.GetParameters(), resolvers).ToArray();
var parameters = info.GetParameters();
// Check for 'out' parameters
if (parameters.Any(param => param.IsOut))
{
return (ref PipelineContext context) =>
{
if (null == context.Existing)
new InvalidRegistrationException($"Constructor {info} with 'out' parameters cannot be injected.", info);
return null == pipeline ? context.Existing : pipeline.Invoke(ref context);
};
}
// Check for 'ref' parameters
if (parameters.Any(param => param.ParameterType.IsByRef))
{
return (ref PipelineContext context) =>
{
if (null == context.Existing)
new InvalidRegistrationException($"Constructor {info} with 'ref' parameters cannot be injected.", info);
return null == pipeline ? context.Existing : pipeline.Invoke(ref context);
};
}
// Create resolver
var parameterResolvers = ParameterResolvers(parameters, resolvers).ToArray();
// PerResolve lifetime
return (ref PipelineContext context) =>
{
@ -413,21 +386,65 @@ namespace Unity
dependencies[i] = parameterResolvers[i](ref context);
context.Existing = info.Invoke(dependencies);
context.Set(typeof(LifetimeManager),
new RuntimePerResolveLifetimeManager(context.Existing));
}
catch (Exception ex)
{
ex.Data.Add(Guid.NewGuid(), info);
throw;
}
context.Set(typeof(LifetimeManager),
new RuntimePerResolveLifetimeManager(context.Existing));
}
return null == pipeline ? context.Existing : pipeline.Invoke(ref context);
};
}
#endregion
#region Expression Overrides
public override IEnumerable<Expression> Express(ref PipelineBuilder builder)
{
#if NETSTANDARD1_0 || NETCOREAPP1_0
var typeInfo = builder.Type.GetTypeInfo();
#else
var typeInfo = builder.Type;
#endif
// Validate if Type could be created
if (typeInfo.IsInterface)
return CannotConstructInterfaceExpr.Concat(builder.Express());
if (typeInfo.IsAbstract)
return CannotConstructAbstractClassExpr.Concat(builder.Express());
if (typeInfo.IsSubclassOf(typeof(Delegate)))
return CannotConstructDelegateExpr.Concat(builder.Express());
if (typeof(string) == builder.Type)
return TypeIsNotConstructableExpr.Concat(builder.Express());
// Build expression as usual
return base.Express(ref builder);
}
protected override Expression GetResolverExpression(ConstructorInfo info, object? resolvers)
{
var tryBlock = base.GetResolverExpression(info, resolvers);
var catchBlock = Expression.Block(tryBlock.Type,
Expression.Call(ExceptionDataExpr, AddMethodInfo,
Expression.Convert(CallNewGuidExpr, typeof(object)),
Expression.Constant(info, typeof(object))),
Expression.Rethrow(tryBlock.Type));
return Expression.TryCatch(tryBlock,
Expression.Catch(ExceptionExpr, catchBlock));
}
#endregion
}
}

Просмотреть файл

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Unity.Exceptions;
using Unity.Injection;
using Unity.Lifetime;
@ -64,9 +65,8 @@ namespace Unity
var lifetimeManager = (LifetimeManager?)builder.Policies?.Get(typeof(LifetimeManager));
return lifetimeManager is PerResolveLifetimeManager
? GetConstructorExpression(info, resolvers).Concat(new[] { SetPerBuildSingletonExpr })
.Concat(expressions)
: GetConstructorExpression(info, resolvers).Concat(expressions);
? new[] { GetResolverExpression(info, resolvers), SetPerBuildSingletonExpr }.Concat(expressions)
: new[] { GetResolverExpression(info, resolvers) }.Concat(expressions);
}
#endregion
@ -74,19 +74,26 @@ namespace Unity
#region Overrides
protected virtual IEnumerable<Expression> GetConstructorExpression(ConstructorInfo info, object? resolvers)
protected override Expression GetResolverExpression(ConstructorInfo info, object? resolvers)
{
var parameters = info.GetParameters();
var variables = parameters.Select(p => Expression.Variable(p.ParameterType, p.Name))
.ToArray();
try
{
var parameters = info.GetParameters();
var variables = parameters.Select(ToVariable)
.ToArray();
var thenBlock = Expression.Block(variables, CreateParameterExpressions(variables, parameters, resolvers)
.Concat(new[] { Expression.Assign(
return Expression.IfThen(NullEqualExisting,
Expression.Block(variables, ParameterExpressions(variables, parameters, resolvers)
.Concat(new[] {
Expression.Assign(
PipelineContextExpression.Existing,
Expression.Convert(
Expression.New(info, variables), typeof(object)))}));
yield return Expression.IfThen(NullEqualExisting, thenBlock);
Expression.New(info, variables), typeof(object)))})));
}
catch (Exception ex)
{
return Expression.IfThen(NullEqualExisting, Expression.Throw(Expression.Constant(new InvalidRegistrationException(ex))));
}
}
#endregion

Просмотреть файл

@ -7,12 +7,12 @@ using Unity.Utility;
namespace Unity
{
public partial class ConstructorPipeline : ParametersPipeline<ConstructorInfo>
public partial class ConstructorPipeline : MethodBasePipeline<ConstructorInfo>
{
#region Constructors
public ConstructorPipeline(UnityContainer container)
: base(typeof(InjectionConstructorAttribute), container)
public ConstructorPipeline(UnityContainer container, ParametersProcessor? processor = null)
: base(typeof(InjectionConstructorAttribute), container, processor)
{
SelectMethod = container.ExecutionMode.IsLegacy()
? (Func<Type, ConstructorInfo[], object?>)LegacySelector

Просмотреть файл

@ -88,7 +88,7 @@ namespace Unity
protected virtual ResolveDelegate<PipelineContext> GetResolverDelegate(ConstructorInfo info, object? resolvers, ResolveDelegate<PipelineContext>? pipeline)
{
var parameterResolvers = CreateParameterResolvers(info.GetParameters(), resolvers).ToArray();
var parameterResolvers = ParameterResolvers(info.GetParameters(), resolvers).ToArray();
return (ref PipelineContext context) =>
{
@ -107,7 +107,7 @@ namespace Unity
protected virtual ResolveDelegate<PipelineContext> GetPerResolveDelegate(ConstructorInfo info, object? resolvers, ResolveDelegate<PipelineContext>? pipeline)
{
var parameterResolvers = CreateParameterResolvers(info.GetParameters(), resolvers).ToArray();
var parameterResolvers = ParameterResolvers(info.GetParameters(), resolvers).ToArray();
// PerResolve lifetime
return (ref PipelineContext context) =>
{

Просмотреть файл

@ -66,7 +66,7 @@ namespace Unity
var tryBody = Expression.Block(expressions);
var catchBody = Expression.Block(tryBody.Type,
Expression.IfThen(filter, Expression.Call(ExceptionDataExpr, AddMethod, Expression.Convert(NewGuid, typeof(object)), infoExpr)),
Expression.IfThen(filter, Expression.Call(ExceptionDataExpr, AddMethodInfo, Expression.Convert(CallNewGuidExpr, typeof(object)), infoExpr)),
Expression.Rethrow(tryBody.Type));
return new Expression[]

Просмотреть файл

@ -36,7 +36,7 @@ namespace Unity
// Select Attributed members
foreach (var member in type.GetDeclaredFields())
{
foreach(var node in AttributeFactories)
foreach (var node in AttributeFactories)
{
#if NET40
if (!member.IsDefined(node.Type, true) ||
@ -75,20 +75,10 @@ namespace Unity
}
}
protected override Expression GetResolverExpression(FieldInfo field, object? resolver)
{
var ex = Expression.Variable(typeof(Exception));
var exData = Expression.MakeMemberAccess(ex, DataProperty);
var block =
Expression.Block(field.FieldType,
Expression.Call(exData, AddMethod,
Expression.Convert(NewGuid, typeof(object)),
Expression.Constant(field, typeof(object))),
Expression.Rethrow(field.FieldType));
#endregion
return Expression.TryCatch(base.GetResolverExpression(field, resolver),
Expression.Catch(ex, block));
}
#region Resolution
protected override ResolveDelegate<PipelineContext> GetResolverDelegate(FieldInfo info, object? resolver)
{
@ -109,5 +99,22 @@ namespace Unity
}
#endregion
#region Expression
protected override Expression GetResolverExpression(FieldInfo field, object? resolver)
{
var block = Expression.Block(field.FieldType,
Expression.Call(ExceptionDataExpr, AddMethodInfo,
Expression.Convert(CallNewGuidExpr, typeof(object)),
Expression.Constant(field, typeof(object))),
Expression.Rethrow(field.FieldType));
return Expression.TryCatch(base.GetResolverExpression(field, resolver),
Expression.Catch(ExceptionExpr, block));
}
#endregion
}
}

Просмотреть файл

@ -37,6 +37,21 @@ namespace Unity
#endregion
#region Resolution
protected override ResolveDelegate<PipelineContext> GetResolverDelegate(FieldInfo info, object? resolver)
{
var value = PreProcessResolver(info, resolver);
return (ref PipelineContext context) =>
{
info.SetValue(context.Existing, context.Resolve(info, value));
return context.Existing;
};
}
#endregion
#region Expression
protected override Expression GetResolverExpression(FieldInfo info, object? resolver)
@ -52,20 +67,5 @@ namespace Unity
}
#endregion
#region Resolution
protected override ResolveDelegate<PipelineContext> GetResolverDelegate(FieldInfo info, object? resolver)
{
var value = PreProcessResolver(info, resolver);
return (ref PipelineContext context) =>
{
info.SetValue(context.Existing, context.Resolve(info, value));
return context.Existing;
};
}
#endregion
}
}

Просмотреть файл

@ -58,6 +58,10 @@ namespace Unity
break;
case Exception exception:
yield return Expression.Throw(Expression.Constant(exception));
yield break;
// Unknown
default:
throw new InvalidOperationException($"Unknown MemberInfo<{typeof(TMemberInfo)}> type");

Просмотреть файл

@ -14,7 +14,7 @@ namespace Unity
#region Constructors
public MethodDiagnostic(UnityContainer container)
: base(container)
: base(container, new ParametersDiagnosticProcessor())
{
container.Defaults.Set(typeof(Func<Type, InjectionMember, MethodInfo>), InjectionValidatingSelector);
}
@ -51,34 +51,34 @@ namespace Unity
if (member.IsStatic)
{
yield return new InvalidRegistrationException(
$"Static method {member.Name} on type '{member.DeclaringType.Name}' is marked for injection. Static methods cannot be injected");
$"Static method {member.Name} on type '{member.DeclaringType.Name}' is marked for injection. Static methods cannot be injected", member);
}
if (member.IsPrivate)
yield return new InvalidRegistrationException(
$"Private method '{member.Name}' on type '{member.DeclaringType.Name}' is marked for injection. Private methods cannot be injected");
$"Private method '{member.Name}' on type '{member.DeclaringType.Name}' is marked for injection. Private methods cannot be injected", member);
if (member.IsFamily)
yield return new InvalidRegistrationException(
$"Protected method '{member.Name}' on type '{member.DeclaringType.Name}' is marked for injection. Protected methods cannot be injected");
$"Protected method '{member.Name}' on type '{member.DeclaringType.Name}' is marked for injection. Protected methods cannot be injected", member);
if (member.IsGenericMethodDefinition)
{
yield return new InvalidRegistrationException(
$"Open generic method {member.Name} on type '{member.DeclaringType.Name}' is marked for injection. Open generic methods cannot be injected.");
$"Open generic method {member.Name} on type '{member.DeclaringType.Name}' is marked for injection. Open generic methods cannot be injected.", member);
}
var parameters = member.GetParameters();
if (parameters.Any(param => param.IsOut))
{
yield return new InvalidRegistrationException(
$"Method {member.Name} on type '{member.DeclaringType.Name}' is marked for injection. Methods with 'out' parameters cannot be injected.");
$"Method {member.Name} on type '{member.DeclaringType.Name}' is marked for injection. Methods with 'out' parameters cannot be injected.", member);
}
if (parameters.Any(param => param.ParameterType.IsByRef))
{
yield return new InvalidRegistrationException(
$"Method {member.Name} on type '{member.DeclaringType.Name}' is marked for injection. Methods with 'ref' parameters cannot be injected.");
$"Method {member.Name} on type '{member.DeclaringType.Name}' has 'ref' parameter and cannot be injected.", member);
}
yield return member;
@ -87,27 +87,14 @@ namespace Unity
}
}
protected override Expression GetResolverExpression(MethodInfo info, object? resolvers)
{
var ex = Expression.Variable(typeof(Exception));
var exData = Expression.MakeMemberAccess(ex, DataProperty);
var block = Expression.Block(typeof(void),
Expression.Call(exData, AddMethod,
Expression.Convert(NewGuid, typeof(object)),
Expression.Constant(info, typeof(object))),
Expression.Rethrow(typeof(void)));
#endregion
return
Expression.TryCatch(
Expression.Call(
Expression.Convert(PipelineContextExpression.Existing, info.DeclaringType),
info, CreateDiagnosticParameterExpressions(info.GetParameters(), resolvers)),
Expression.Catch(ex, block));
}
#region Resolution
protected override ResolveDelegate<PipelineContext> GetResolverDelegate(MethodInfo info, object? resolvers)
{
var parameterResolvers = CreateDiagnosticParameterResolvers(info.GetParameters(), resolvers).ToArray();
var parameterResolvers = ParameterResolvers(info.GetParameters(), resolvers).ToArray();
return (ref PipelineContext c) =>
{
try
@ -129,6 +116,23 @@ namespace Unity
return c.Existing;
};
}
#endregion
#region Expression
protected override Expression GetResolverExpression(MethodInfo info, object? resolvers)
{
var block = Expression.Block(typeof(void),
Expression.Call(ExceptionDataExpr, AddMethodInfo,
Expression.Convert(CallNewGuidExpr, typeof(object)),
Expression.Constant(info, typeof(object))),
Expression.Rethrow(typeof(void)));
return Expression.TryCatch(base.GetResolverExpression(info, resolvers),
Expression.Catch(ExceptionExpr, block));
}
#endregion
}

Просмотреть файл

@ -8,12 +8,12 @@ using Unity.Resolution;
namespace Unity
{
public partial class MethodPipeline : ParametersPipeline<MethodInfo>
public partial class MethodPipeline : MethodBasePipeline<MethodInfo>
{
#region Constructors
public MethodPipeline(UnityContainer container)
: base(typeof(InjectionMethodAttribute), container)
public MethodPipeline(UnityContainer container, ParametersProcessor? processor = null)
: base(typeof(InjectionMethodAttribute), container, processor)
{
}
@ -36,29 +36,11 @@ namespace Unity
#endregion
#region Expression
protected override Expression GetResolverExpression(MethodInfo info, object? resolvers)
{
var parameters = info.GetParameters();
var variables = parameters.Select(p => Expression.Variable(p.ParameterType, p.Name))
.ToArray();
return Expression.Block(variables, CreateParameterExpressions(variables, parameters, resolvers)
.Concat(new[] {
Expression.Call(
Expression.Convert(PipelineContextExpression.Existing, info.DeclaringType),
info, variables) }));
}
#endregion
#region Resolution
protected override ResolveDelegate<PipelineContext> GetResolverDelegate(MethodInfo info, object? resolvers)
{
var parameterResolvers = CreateParameterResolvers(info.GetParameters(), resolvers).ToArray();
var parameterResolvers = ParameterResolvers(info.GetParameters(), resolvers).ToArray();
return (ref PipelineContext c) =>
{
if (null == c.Existing) return c.Existing;
@ -74,5 +56,23 @@ namespace Unity
}
#endregion
#region Expression
protected override Expression GetResolverExpression(MethodInfo info, object? resolvers)
{
var parameters = info.GetParameters();
var variables = parameters.Select(ToVariable)
.ToArray();
return Expression.Block(variables, ParameterExpressions(variables, parameters, resolvers)
.Concat(new[] {
Expression.Call(
Expression.Convert(PipelineContextExpression.Existing, info.DeclaringType),
info, variables) }));
}
#endregion
}
}

Просмотреть файл

@ -1,50 +1,75 @@
using System.Collections.Generic;
using System;
using System.Reflection;
using Unity.Exceptions;
using Unity.Resolution;
namespace Unity
{
public abstract partial class ParametersPipeline<TMemberInfo>
public partial class ParametersProcessor
{
protected virtual IEnumerable<ResolveDelegate<PipelineContext>> CreateParameterResolvers(ParameterInfo[] parameters, object? injectors = null)
public ResolveDelegate<PipelineContext> ParameterResolver(ParameterInfo parameter)
=> ParameterResolverFactory(parameter, FromAttribute(parameter));
public virtual ResolveDelegate<PipelineContext> ParameterResolver(ParameterInfo parameter, object injector)
=> ParameterResolverFactory(parameter, PreProcessResolver(parameter, injector));
protected virtual ResolveDelegate<PipelineContext> ParameterResolverFactory(ParameterInfo parameter, object resolver)
{
object[]? resolvers = null != injectors && injectors is object[] array && 0 != array.Length ? array : null;
for (var i = 0; i < parameters.Length; i++)
#if NET40
if (parameter.DefaultValue is DBNull)
#else
if (!parameter.HasDefaultValue)
#endif
{
var parameter = parameters[i];
var resolver = null == resolvers
? FromAttribute(parameter)
: PreProcessResolver(parameter, resolvers[i]);
return (ref PipelineContext context) => context.Resolve(parameter, resolver);
}
else
{
// Check if has default value
#if NET40
if (parameter.DefaultValue is DBNull)
var defaultValue = !(parameter.DefaultValue is DBNull) ? parameter.DefaultValue : null;
#else
if (!parameter.HasDefaultValue)
var defaultValue = parameter.HasDefaultValue ? parameter.DefaultValue : null;
#endif
return (ref PipelineContext context) =>
{
// Plain vanilla case
yield return (ref PipelineContext context) => context.Resolve(parameter, resolver);
}
else
{
// Check if has default value
#if NET40
var defaultValue = !(parameter.DefaultValue is DBNull) ? parameter.DefaultValue : null;
#else
var defaultValue = parameter.HasDefaultValue ? parameter.DefaultValue : null;
#endif
yield return (ref PipelineContext context) =>
try
{
try
{
return context.Resolve(parameter, resolver);
}
catch
{
return defaultValue;
}
};
}
return context.Resolve(parameter, resolver);
}
catch
{
return defaultValue;
}
};
}
}
#region Attribute Resolver Factories
protected virtual ResolveDelegate<PipelineContext> DependencyResolverFactory(Attribute attribute, ParameterInfo info, object? value = null)
{
return (ref PipelineContext context) => context.Resolve(info.ParameterType, ((DependencyResolutionAttribute)attribute).Name);
}
protected virtual ResolveDelegate<PipelineContext> OptionalDependencyResolverFactory(Attribute attribute, ParameterInfo info, object? value = null)
{
return (ref PipelineContext context) =>
{
try
{
return context.Resolve(info.ParameterType, ((DependencyResolutionAttribute)attribute).Name);
}
catch (Exception ex) when (!(ex is CircularDependencyException))
{
return value;
}
};
}
#endregion
}
}

Просмотреть файл

@ -1,133 +1,67 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
using Unity.Resolution;
namespace Unity
{
public abstract partial class ParametersPipeline<TMemberInfo>
public partial class ParametersDiagnosticProcessor : ParametersProcessor
{
#region Diagnostic Parameter Factories
protected virtual IEnumerable<Expression> CreateDiagnosticParameterExpressions(ParameterInfo[] parameters, object? injectors = null)
protected override ResolveDelegate<PipelineContext> ParameterResolverFactory(ParameterInfo parameter, object resolver)
{
object[]? resolvers = null != injectors && injectors is object[] array && 0 != array.Length ? array : null;
for (var i = 0; i < parameters.Length; i++)
{
var parameter = parameters[i];
var resolver = null == resolvers
? FromAttribute(parameter)
: PreProcessResolver(parameter, resolvers[i]);
// Check if has default value
#if NET40
Expression defaultValueExpr = null;
if (parameter.DefaultValue is DBNull)
if (parameter.DefaultValue is DBNull)
#else
var defaultValueExpr = parameter.HasDefaultValue
? Expression.Constant(parameter.DefaultValue, parameter.ParameterType)
: null;
if (!parameter.HasDefaultValue)
if (!parameter.HasDefaultValue)
#endif
{
return (ref PipelineContext context) =>
{
var ex = Expression.Variable(typeof(Exception));
var exData = Expression.MakeMemberAccess(ex, DataProperty);
var block = Expression.Block(parameter.ParameterType,
Expression.Call(exData, AddMethod,
Expression.Convert(NewGuid, typeof(object)),
Expression.Constant(parameter, typeof(object))),
Expression.Rethrow(parameter.ParameterType));
var tryBlock = Expression.Convert(
Expression.Call(PipelineContextExpression.Context,
PipelineContextExpression.ResolveParameterMethod,
Expression.Constant(parameter, typeof(ParameterInfo)),
Expression.Constant(resolver, typeof(object))),
parameter.ParameterType);
yield return Expression.TryCatch(tryBlock, Expression.Catch(ex, block));
}
else
{
var variable = Expression.Variable(parameter.ParameterType);
var resolve = Expression.Convert(
Expression.Call(PipelineContextExpression.Context,
PipelineContextExpression.ResolveParameterMethod,
Expression.Constant(parameter, typeof(ParameterInfo)),
Expression.Constant(resolver, typeof(object))),
parameter.ParameterType);
yield return Expression.Block(new[] { variable }, new Expression[]
try
{
Expression.TryCatch(
Expression.Assign(variable, resolve),
Expression.Catch(typeof(Exception),
Expression.Assign(variable, defaultValueExpr))),
variable
});
}
return context.Resolve(parameter, resolver);
}
catch (Exception ex)
{
ex.Data.Add(Guid.NewGuid(), parameter);
throw;
}
};
}
else
{
// Check if has default value
#if NET40
var defaultValue = !(parameter.DefaultValue is DBNull) ? parameter.DefaultValue : null;
#else
var defaultValue = parameter.HasDefaultValue ? parameter.DefaultValue : null;
#endif
return (ref PipelineContext context) =>
{
try
{
return context.Resolve(parameter, resolver);
}
catch
{
return defaultValue;
}
};
}
}
protected virtual IEnumerable<ResolveDelegate<PipelineContext>> CreateDiagnosticParameterResolvers(ParameterInfo[] parameters, object? injectors = null)
protected override Expression ParameterExpressionFactory(ParameterExpression expression, ParameterInfo parameter, object resolver)
{
object[]? resolvers = null != injectors && injectors is object[] array && 0 != array.Length ? array : null;
for (var i = 0; i < parameters.Length; i++)
{
var parameter = parameters[i];
var resolver = null == resolvers
? FromAttribute(parameter)
: PreProcessResolver(parameter, resolvers[i]);
var tryBlock = base.ParameterExpressionFactory(expression, parameter, resolver);
var catchBlock = Expression.Block(tryBlock.Type,
Expression.Call(ExceptionDataExpr, AddMethodInfo,
Expression.Convert(CallNewGuidExpr, typeof(object)),
Expression.Constant(parameter, typeof(object))),
Expression.Rethrow(tryBlock.Type));
// TODO: Add diagnostic for parameters
// Check if has default value
#if NET40
if (parameter.DefaultValue is DBNull)
#else
if (!parameter.HasDefaultValue)
#endif
{
// Plain vanilla case
yield return (ref PipelineContext context) =>
{
try
{
return context.Resolve(parameter, resolver);
}
catch (Exception ex)
{
ex.Data.Add(Guid.NewGuid(), parameter);
throw;
}
};
}
else
{
// Check if has default value
#if NET40
var defaultValue = !(parameter.DefaultValue is DBNull) ? parameter.DefaultValue : null;
#else
var defaultValue = parameter.HasDefaultValue ? parameter.DefaultValue : null;
#endif
yield return (ref PipelineContext context) =>
{
try
{
return context.Resolve(parameter, resolver);
}
catch
{
return defaultValue;
}
};
}
}
return Expression.TryCatch(tryBlock,
Expression.Catch(ExceptionExpr, catchBlock));
}
#endregion
}
}

Просмотреть файл

@ -1,60 +1,55 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
namespace Unity
{
public abstract partial class ParametersPipeline<TMemberInfo>
public partial class ParametersProcessor
{
protected virtual IEnumerable<Expression> CreateParameterExpressions(ParameterExpression[] expressions, ParameterInfo[] parameters, object? injectors)
public Expression ParameterExpression(ParameterExpression expression, ParameterInfo parameter)
=> ParameterExpressionFactory(expression, parameter, FromAttribute(parameter));
public virtual Expression ParameterExpression(ParameterExpression expression, ParameterInfo parameter, object injector)
=> ParameterExpressionFactory(expression, parameter, PreProcessResolver(parameter, injector));
protected virtual Expression ParameterExpressionFactory(ParameterExpression expression, ParameterInfo parameter, object resolver)
{
object[]? resolvers = null != injectors && injectors is object[] array && 0 != array.Length ? array : null;
for (var i = 0; i < parameters.Length; i++)
{
var parameterExpr = expressions[i];
var parameterInfo = parameters[i];
var resolver = null == resolvers
? FromAttribute(parameterInfo)
: PreProcessResolver(parameterInfo, resolvers[i]);
// Check if has default value
// Check if has default value
#if NET40
var defaultValueExpr = parameter.DefaultValue is DBNull
? Expression.Constant(parameter.DefaultValue, parameter.ParameterType)
: null;
var defaultValueExpr = parameter.DefaultValue is DBNull
? Expression.Constant(parameter.DefaultValue, parameter.ParameterType)
: null;
if (parameter.DefaultValue is DBNull)
if (parameter.DefaultValue is DBNull)
#else
var defaultValueExpr = parameterInfo.HasDefaultValue
? Expression.Constant(parameterInfo.DefaultValue, parameterInfo.ParameterType)
: null;
var defaultValueExpr = parameter.HasDefaultValue
? Expression.Constant(parameter.DefaultValue, parameter.ParameterType)
: null;
if (!parameterInfo.HasDefaultValue)
if (!parameter.HasDefaultValue)
#endif
{
// Plain vanilla case
yield return Expression.Assign(parameterExpr, Expression.Convert(
Expression.Call(PipelineContextExpression.Context,
PipelineContextExpression.ResolveParameterMethod,
Expression.Constant(parameterInfo, typeof(ParameterInfo)),
Expression.Constant(resolver, typeof(object))),
parameterInfo.ParameterType));
}
else
{
var resolve = Expression.Convert(
Expression.Call(PipelineContextExpression.Context,
PipelineContextExpression.ResolveParameterMethod,
Expression.Constant(parameterInfo, typeof(ParameterInfo)),
Expression.Constant(resolver, typeof(object))),
parameterInfo.ParameterType);
{
// Plain vanilla case
return Expression.Assign(expression, Expression.Convert(
Expression.Call(PipelineContextExpression.Context,
PipelineContextExpression.ResolveParameterMethod,
Expression.Constant(parameter, typeof(ParameterInfo)),
Expression.Constant(resolver, typeof(object))),
parameter.ParameterType));
}
else
{
var resolve = Expression.Convert(
Expression.Call(PipelineContextExpression.Context,
PipelineContextExpression.ResolveParameterMethod,
Expression.Constant(parameter, typeof(ParameterInfo)),
Expression.Constant(resolver, typeof(object))),
parameter.ParameterType);
yield return Expression.TryCatch(
Expression.Assign(parameterExpr, resolve),
Expression.Catch(typeof(Exception),
Expression.Assign(parameterExpr, defaultValueExpr)));
}
return Expression.TryCatch(
Expression.Assign(expression, resolve),
Expression.Catch(typeof(Exception),
Expression.Assign(expression, defaultValueExpr)));
}
}
}

Просмотреть файл

@ -1,144 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Unity.Exceptions;
using Unity.Resolution;
namespace Unity
{
public abstract partial class ParametersPipeline<TMemberInfo> : MemberPipeline<TMemberInfo, object[]>
where TMemberInfo : MethodBase
{
#region Fields
protected readonly UnityContainer Container;
#endregion
#region Constructors
protected ParametersPipeline(Type attribute, UnityContainer container)
: base(container)
{
Container = container;
Markers = new[] { attribute };
}
#endregion
#region Public Members
public void AddMarkers(IEnumerable<Type> attributes)
{
Markers = Markers.Concat(attributes).ToArray();
}
public Type[] Markers { get; private set; }
#endregion
#region Overrides
protected override Type MemberType(TMemberInfo info) => info.DeclaringType;
#endregion
#region Implementation
private object PreProcessResolver(ParameterInfo parameter, object resolver)
{
switch (resolver)
{
case IResolve policy:
return (ResolveDelegate<PipelineContext>)policy.Resolve;
case IResolverFactory<ParameterInfo> factory:
return factory.GetResolver<PipelineContext>(parameter);
case Type type:
return
typeof(Type) == parameter.ParameterType
? type
: type == parameter.ParameterType
? FromAttribute(parameter)
: FromType(type);
}
return resolver;
}
private object FromType(Type type)
{
return (ResolveDelegate<PipelineContext>)((ref PipelineContext context) => context.Resolve(type, (string?)null));
}
private object FromAttribute(ParameterInfo info)
{
#if NET40
var defaultValue = !(info.DefaultValue is DBNull) ? info.DefaultValue : null;
#else
var defaultValue = info.HasDefaultValue ? info.DefaultValue : null;
#endif
foreach (var node in AttributeFactories)
{
if (null == node.Factory) continue;
var attribute = info.GetCustomAttribute(node.Type);
if (null == attribute) continue;
// If found match, use provided factory to create expression
return node.Factory(attribute, info, defaultValue);
}
return info;
}
protected bool CanResolve(ParameterInfo info)
{
foreach (var node in AttributeFactories)
{
if (null == node.Factory) continue;
var attribute = info.GetCustomAttribute(node.Type);
if (null == attribute) continue;
// If found match, use provided factory to create expression
return Container.CanResolve(info.ParameterType, node.Name(attribute));
}
return Container.CanResolve(info.ParameterType, null);
}
#endregion
#region Attribute Factories
protected override ResolveDelegate<PipelineContext> DependencyResolverFactory(Attribute attribute, object info, object? value = null)
{
return (ref PipelineContext context) => context.Resolve(((ParameterInfo)info).ParameterType, ((DependencyResolutionAttribute)attribute).Name);
}
protected override ResolveDelegate<PipelineContext> OptionalDependencyResolverFactory(Attribute attribute, object info, object? value = null)
{
return (ref PipelineContext context) =>
{
try
{
return context.Resolve(((ParameterInfo)info).ParameterType,
((DependencyResolutionAttribute)attribute).Name);
}
catch (Exception ex) when (!(ex is CircularDependencyException))
{
return value;
}
};
}
#endregion
}
}

Просмотреть файл

@ -0,0 +1,126 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Unity.Resolution;
namespace Unity
{
public partial class ParametersProcessor
{
#region Fields
protected static readonly Expression CallNewGuidExpr = Expression.Call(typeof(Guid).GetTypeInfo().GetDeclaredMethod(nameof(Guid.NewGuid)));
protected static readonly PropertyInfo DataPropertyInfo = typeof(Exception).GetTypeInfo().GetDeclaredProperty(nameof(Exception.Data));
protected static readonly MethodInfo AddMethodInfo = typeof(IDictionary).GetTypeInfo().GetDeclaredMethod(nameof(IDictionary.Add));
protected static readonly ParameterExpression ExceptionExpr = Expression.Variable(typeof(Exception), "exception");
protected static readonly MemberExpression ExceptionDataExpr = Expression.MakeMemberAccess(ExceptionExpr, DataPropertyInfo);
#endregion
#region Constructors
public ParametersProcessor()
{
AttributeFactories = new[]
{
new AttributeFactory(typeof(DependencyAttribute), (a)=>((DependencyResolutionAttribute)a).Name, DependencyResolverFactory),
new AttributeFactory(typeof(OptionalDependencyAttribute), (a)=>((DependencyResolutionAttribute)a).Name, OptionalDependencyResolverFactory),
};
}
#endregion
#region Public Members
// TODO: Requires optimization
public void AddFactories(IEnumerable<AttributeFactory> factories)
{
AttributeFactories = AttributeFactories.Concat(factories).ToArray();
}
public AttributeFactory[] AttributeFactories { get; private set; }
#endregion
#region Implementation
private object PreProcessResolver(ParameterInfo parameter, object resolver)
{
switch (resolver)
{
case IResolve policy:
return (ResolveDelegate<PipelineContext>)policy.Resolve;
case IResolverFactory<ParameterInfo> factory:
return factory.GetResolver<PipelineContext>(parameter);
case Type type:
return
typeof(Type) == parameter.ParameterType
? type
: type == parameter.ParameterType
? FromAttribute(parameter)
: FromType(type);
}
return resolver;
}
private object FromType(Type type)
{
return (ResolveDelegate<PipelineContext>)((ref PipelineContext context) => context.Resolve(type, (string?)null));
}
private object FromAttribute(ParameterInfo info)
{
#if NET40
var defaultValue = !(info.DefaultValue is DBNull) ? info.DefaultValue : null;
#else
var defaultValue = info.HasDefaultValue ? info.DefaultValue : null;
#endif
foreach (var node in AttributeFactories)
{
if (null == node.Factory) continue;
var attribute = info.GetCustomAttribute(node.Type);
if (null == attribute) continue;
// If found match, use provided factory to create expression
return node.Factory(attribute, info, defaultValue);
}
return info;
}
#endregion
#region Nested Types
public struct AttributeFactory
{
public readonly Type Type;
public Func<Attribute, ParameterInfo, object?, ResolveDelegate<PipelineContext>> Factory;
public Func<Attribute, string> Name;
public AttributeFactory(Type type, Func<Attribute, string> getName, Func<Attribute, ParameterInfo, object?, ResolveDelegate<PipelineContext>> factory)
{
Type = type;
Name = getName;
Factory = factory;
}
}
#endregion
}
}

Просмотреть файл

@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
using Unity;
using Unity.Exceptions;
using Unity.Injection;
using Unity.Resolution;
@ -74,20 +73,10 @@ namespace Unity
}
}
protected override Expression GetResolverExpression(PropertyInfo property, object? resolver)
{
var ex = Expression.Variable(typeof(Exception));
var exData = Expression.MakeMemberAccess(ex, DataProperty);
var block =
Expression.Block(property.PropertyType,
Expression.Call(exData, AddMethod,
Expression.Convert(NewGuid, typeof(object)),
Expression.Constant(property, typeof(object))),
Expression.Rethrow(property.PropertyType));
#endregion
return Expression.TryCatch(base.GetResolverExpression(property, resolver),
Expression.Catch(ex, block));
}
#region Resolution
protected override ResolveDelegate<PipelineContext> GetResolverDelegate(PropertyInfo info, object? resolver)
{
@ -112,5 +101,22 @@ namespace Unity
}
#endregion
#region Expression
protected override Expression GetResolverExpression(PropertyInfo property, object? resolver)
{
var block = Expression.Block(property.PropertyType,
Expression.Call(ExceptionDataExpr, AddMethodInfo,
Expression.Convert(CallNewGuidExpr, typeof(object)),
Expression.Constant(property, typeof(object))),
Expression.Rethrow(property.PropertyType));
return Expression.TryCatch(base.GetResolverExpression(property, resolver),
Expression.Catch(ExceptionExpr, block));
}
#endregion
}
}

Просмотреть файл

@ -44,23 +44,6 @@ namespace Unity
#endregion
#region Expression
protected override Expression GetResolverExpression(PropertyInfo info, object? resolver)
{
return Expression.Assign(
Expression.Property(Expression.Convert(PipelineContextExpression.Existing, info.DeclaringType), info),
Expression.Convert(
Expression.Call(PipelineContextExpression.Context,
PipelineContextExpression.ResolvePropertyMethod,
Expression.Constant(info, typeof(PropertyInfo)),
Expression.Constant(PreProcessResolver(info, resolver), typeof(object))),
info.PropertyType));
}
#endregion
#region Resolution
protected override ResolveDelegate<PipelineContext> GetResolverDelegate(PropertyInfo info, object? resolver)
@ -78,5 +61,22 @@ namespace Unity
}
#endregion
#region Expression
protected override Expression GetResolverExpression(PropertyInfo info, object? resolver)
{
return Expression.Assign(
Expression.Property(Expression.Convert(PipelineContextExpression.Existing, info.DeclaringType), info),
Expression.Convert(
Expression.Call(PipelineContextExpression.Context,
PipelineContextExpression.ResolvePropertyMethod,
Expression.Constant(info, typeof(PropertyInfo)),
Expression.Constant(PreProcessResolver(info, resolver), typeof(object))),
info.PropertyType));
}
#endregion
}
}

Просмотреть файл

@ -226,7 +226,7 @@ namespace Unity
var builder = new StringBuilder();
builder.AppendLine(ex.Message);
builder.AppendLine(line);
builder.AppendLine("Exception occurred while:");
builder.AppendLine("Exception occurred:");
foreach (DictionaryEntry item in ex.Data)
builder.AppendLine(DataToString(item.Value));