Skip to content

Commit

Permalink
Enabled property injection for conditionals
Browse files Browse the repository at this point in the history
Fixes #383.
  • Loading branch information
dotnetjunkie committed Feb 18, 2017
1 parent fc694a2 commit 1a5a8d4
Show file tree
Hide file tree
Showing 28 changed files with 126 additions and 162 deletions.
Expand Up @@ -145,7 +145,7 @@ public class NamedDependencyInjectionBehavior : IDependencyInjectionBehavior
this.keyedProducerRetriever = keyedProducerRetriever;
}

public InstanceProducer GetInstanceProducerFor(InjectionConsumerInfo consumer)
public InstanceProducer GetInstanceProducer(InjectionConsumerInfo consumer, bool throwOnFailure)
{
var attribute = consumer.Target.GetCustomAttribute<NamedAttribute>();

Expand All @@ -154,7 +154,7 @@ public InstanceProducer GetInstanceProducerFor(InjectionConsumerInfo consumer)
return this.keyedProducerRetriever(consumer.Target.TargetType, attribute.Name);
}

return this.defaultBehavior.GetInstanceProducerFor(consumer);
return this.defaultBehavior.GetInstanceProducer(consumer, throwOnFailure);
}

public void Verify(InjectionConsumerInfo consumer)
Expand Down
@@ -1,7 +1,6 @@
namespace SimpleInjector.CodeSamples
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
Expand All @@ -14,30 +13,25 @@ public static class AttributedPropertyInjectionExtensions
public static void AutoWirePropertiesWithAttribute<TAttribute>(this ContainerOptions options)
where TAttribute : Attribute
{
options.PropertySelectionBehavior =
new AttributePropertyInjectionBehavior(options.PropertySelectionBehavior, typeof(TAttribute));
options.PropertySelectionBehavior = new AttributePropertyInjectionBehavior<TAttribute>(
options.PropertySelectionBehavior);
}

internal sealed class AttributePropertyInjectionBehavior : IPropertySelectionBehavior
internal sealed class AttributePropertyInjectionBehavior<TAttribute> : IPropertySelectionBehavior
where TAttribute : Attribute
{
private readonly IPropertySelectionBehavior baseBehavior;
private readonly Type attributeType;

public AttributePropertyInjectionBehavior(IPropertySelectionBehavior baseBehavior, Type attributeType)
public AttributePropertyInjectionBehavior(IPropertySelectionBehavior baseBehavior)
{
this.baseBehavior = baseBehavior;
this.attributeType = attributeType;
}

[DebuggerStepThrough]
public bool SelectProperty(PropertyInfo p) =>
this.IsPropertyDecoratedWithAttribute(p) || this.baseBehavior.SelectProperty(p);
public bool SelectProperty(Type t, PropertyInfo p) =>
this.IsPropertyDecoratedWithAttribute(p) || this.baseBehavior.SelectProperty(t, p);

[DebuggerStepThrough]
private bool IsPropertyDecoratedWithAttribute(PropertyInfo property)
{
return property.GetCustomAttributes(this.attributeType, true).Any();
}
private bool IsPropertyDecoratedWithAttribute(PropertyInfo property) =>
property.GetCustomAttributes(typeof(TAttribute), true).Any();
}
}
}
Expand Up @@ -233,7 +233,8 @@ void IDependencyInjectionBehavior.Verify(InjectionConsumerInfo consumer)
}
}

InstanceProducer IDependencyInjectionBehavior.GetInstanceProducerFor(InjectionConsumerInfo consumer)
InstanceProducer IDependencyInjectionBehavior.GetInstanceProducer(
InjectionConsumerInfo consumer, bool throwOnFailure)
{
ThreadLocal<object> local = this.FindThreadLocal(consumer.Target);

Expand All @@ -253,7 +254,7 @@ InstanceProducer IDependencyInjectionBehavior.GetInstanceProducerFor(InjectionCo
this.container);
}

return this.originalBehavior.GetInstanceProducerFor(consumer);
return this.originalBehavior.GetInstanceProducer(consumer, throwOnFailure);
}

// Called by RegisterFactory<TFactory>
Expand Down
Expand Up @@ -73,9 +73,9 @@ public void Verify(InjectionConsumerInfo consumer)
this.defaultBehavior.Verify(consumer);
}

public InstanceProducer GetInstanceProducerFor(InjectionConsumerInfo consumer)
public InstanceProducer GetInstanceProducer(InjectionConsumerInfo consumer, bool throwOnFailure)
{
InstanceProducer producer = this.defaultBehavior.GetInstanceProducerFor(consumer);
InstanceProducer producer = this.defaultBehavior.GetInstanceProducer(consumer, throwOnFailure);

List<PredicatePair> pairs;

Expand Down
Expand Up @@ -19,34 +19,29 @@ internal sealed class ImplicitPropertyInjectionBehavior
: IPropertySelectionBehavior
{
private readonly IPropertySelectionBehavior core;
private readonly ContainerOptions options;
private readonly IDependencyInjectionBehavior injectionBehavior;

internal ImplicitPropertyInjectionBehavior(IPropertySelectionBehavior core,
ContainerOptions options)
{
this.core = core;
this.options = options;
this.injectionBehavior = options.DependencyInjectionBehavior;
}

[DebuggerStepThrough]
public bool SelectProperty(PropertyInfo p) =>
this.IsImplicitInjectable(p) || this.core.SelectProperty(p);
public bool SelectProperty(Type t, PropertyInfo p) =>
this.IsImplicitInjectable(t, p) || this.core.SelectProperty(t, p);

[DebuggerStepThrough]
private bool IsImplicitInjectable(PropertyInfo p) =>
IsInjectableProperty(p) && this.IsAvailableService(p.PropertyType);
private bool IsImplicitInjectable(Type t, PropertyInfo p) =>
IsInjectableProperty(p) && this.CanBeResolved(t, p);

[DebuggerStepThrough]
private static bool IsInjectableProperty(PropertyInfo property)
{
MethodInfo setMethod = property.GetSetMethod(nonPublic: false);
private static bool IsInjectableProperty(PropertyInfo property) =>
property.CanWrite && property.GetSetMethod(nonPublic: false)?.IsStatic == false;

return setMethod != null && !setMethod.IsStatic && property.CanWrite;
}
private bool CanBeResolved(Type t, PropertyInfo property) =>
this.GetProducer(new InjectionConsumerInfo(t, property)) != null;

[DebuggerStepThrough]
private bool IsAvailableService(Type type) =>
this.options.Container.GetRegistration(type) != null;
private InstanceProducer GetProducer(InjectionConsumerInfo info) =>
this.injectionBehavior.GetInstanceProducer(info, false);
}
}
}
Expand Up @@ -41,60 +41,37 @@ public ConstructorInfo GetConstructor(Type implementationType)
throw new ActivationException(BuildExceptionMessage(implementationType));
}

[DebuggerStepThrough]
private IEnumerable<ConstructorInfo> GetConstructors(Type implementation)
{
var constructors = implementation.GetConstructors();
// We prevent calling GetRegistration during the registration phase, because at this point not
// all dependencies might be registered, and calling GetRegistration would lock the container,
// making it impossible to do other registrations.
private IEnumerable<ConstructorInfo> GetConstructors(Type implementation) =>
from ctor in implementation.GetConstructors()
let parameters = ctor.GetParameters()
where this.IsCalledDuringRegistrationPhase
|| implementation.GetConstructors().Length == 1
|| ctor.GetParameters().All(this.CanBeResolved)
orderby parameters.Length descending
select ctor;

// We prevent calling GetRegistration during the registration phase, because at this point not
// all dependencies might be registered, and calling GetRegistration would lock the container,
// making it impossible to do other registrations.
return
from ctor in constructors
let parameters = ctor.GetParameters()
where this.IsCalledDuringRegistrationPhase
|| constructors.Length == 1
|| ctor.GetParameters().All(p => this.CanBeResolved(p, implementation))
orderby parameters.Length descending
select ctor;
}
private bool CanBeResolved(ParameterInfo parameter) =>
this.GetInstanceProducerFor(new InjectionConsumerInfo(parameter)) != null;

[DebuggerStepThrough]
private bool CanBeResolved(ParameterInfo parameter, Type implementationType)
{
return this.container.GetRegistration(parameter.ParameterType) != null ||
this.CanBuildExpression(implementationType, parameter);
}
private InstanceProducer GetInstanceProducerFor(InjectionConsumerInfo info) =>
this.container.Options.DependencyInjectionBehavior.GetInstanceProducer(info, false);

[DebuggerStepThrough]
private bool CanBuildExpression(Type implementationType, ParameterInfo parameter)
{
try
{
var info = new InjectionConsumerInfo(implementationType, parameter);
this.container.Options.DependencyInjectionBehavior.GetInstanceProducerFor(info);
private static string BuildExceptionMessage(Type type) =>
!type.GetConstructors().Any()
? TypeShouldHaveAtLeastOnePublicConstructor(type)
: TypeShouldHaveConstructorWithResolvableTypes(type);

return true;
}
catch (ActivationException)
{
return false;
}
}
private static string TypeShouldHaveAtLeastOnePublicConstructor(Type type) =>
string.Format(CultureInfo.InvariantCulture,
"For the container to be able to create {0}, it should contain at least one public " +
"constructor.", type.ToFriendlyName());

[DebuggerStepThrough]
private static string BuildExceptionMessage(Type type)
{
if (!type.GetConstructors().Any())
{
return string.Format(CultureInfo.InvariantCulture,
"For the container to be able to create {0}, it should contain at least one public " +
"constructor.", type);
}

return string.Format(CultureInfo.InvariantCulture,
"For the container to be able to create {0}, it should contain a public constructor that " +
"only contains parameters that can be resolved.", type);
}
private static string TypeShouldHaveConstructorWithResolvableTypes(Type type) =>
string.Format(CultureInfo.InvariantCulture,
"For the container to be able to create {0}, it should contain a public constructor " +
"that only contains parameters that can be resolved.", type.ToFriendlyName());
}
}
Expand Up @@ -32,8 +32,8 @@ public void Verify(InjectionConsumerInfo consumer)
}
}

public InstanceProducer GetInstanceProducerFor(InjectionConsumerInfo c) =>
this.TryCreateInstanceProducer(c.Target.Parameter) ?? this.original.GetInstanceProducerFor(c);
public InstanceProducer GetInstanceProducer(InjectionConsumerInfo c, bool t) =>
this.TryCreateInstanceProducer(c.Target.Parameter) ?? this.original.GetInstanceProducer(c, t);

private InstanceProducer TryCreateInstanceProducer(ParameterInfo parameter) =>
parameter != null && IsOptional(parameter) && !this.CanBeResolved(parameter)
Expand Down
33 changes: 14 additions & 19 deletions src/SimpleInjector.CodeSamples/ParameterConventionExtensions.cs
Expand Up @@ -3,7 +3,9 @@
using System;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.InteropServices;
using SimpleInjector.Advanced;

Expand Down Expand Up @@ -48,11 +50,11 @@ public void Verify(InjectionConsumerInfo consumer)
}

[DebuggerStepThrough]
public InstanceProducer GetInstanceProducerFor(InjectionConsumerInfo consumer)
public InstanceProducer GetInstanceProducer(InjectionConsumerInfo consumer, bool throwOnFailure)
{
if (!this.convention.CanResolve(consumer.Target))
{
return this.decorated.GetInstanceProducerFor(consumer);
return this.decorated.GetInstanceProducer(consumer, throwOnFailure);
}

return InstanceProducer.FromExpression(
Expand Down Expand Up @@ -205,24 +207,17 @@ public OptionalParameterConvention(IDependencyInjectionBehavior injectionBehavio
}

[DebuggerStepThrough]
public bool CanResolve(InjectionTargetInfo target)
{
return target.Parameter != null &&
target.GetCustomAttributes(typeof(OptionalAttribute), true).Length > 0;
}
public bool CanResolve(InjectionTargetInfo target) =>
target.Parameter != null && target.GetCustomAttributes(typeof(OptionalAttribute), true).Any();

[DebuggerStepThrough]
public Expression BuildExpression(InjectionConsumerInfo consumer)
{
try
{
return this.injectionBehavior.GetInstanceProducerFor(consumer).BuildExpression();
}
catch (ActivationException)
{
var parameter = consumer.Target.Parameter;
return Expression.Constant(parameter.RawDefaultValue, parameter.ParameterType);
}
}
public Expression BuildExpression(InjectionConsumerInfo consumer) =>
this.GetProducer(consumer)?.BuildExpression() ?? GetDefault(consumer.Target.Parameter);

private InstanceProducer GetProducer(InjectionConsumerInfo consumer) =>
this.injectionBehavior.GetInstanceProducer(consumer, throwOnFailure: false);

private static ConstantExpression GetDefault(ParameterInfo parameter) =>
Expression.Constant(parameter.RawDefaultValue, parameter.ParameterType);
}
}
4 changes: 2 additions & 2 deletions src/SimpleInjector.CodeSamples/PropertyInjectionExtensions.cs
Expand Up @@ -73,8 +73,8 @@ internal PropertyRegistrations(IPropertySelectionBehavior baseBehavior)
this.baseBehavior = baseBehavior;
}

bool IPropertySelectionBehavior.SelectProperty(PropertyInfo p) =>
this.IsPropertyRegisteredForAutowiring(p) || this.baseBehavior.SelectProperty(p);
bool IPropertySelectionBehavior.SelectProperty(Type t, PropertyInfo p) =>
this.IsPropertyRegisteredForAutowiring(p) || this.baseBehavior.SelectProperty(t, p);

public void AddPropertySelector(Predicate<PropertyInfo> selector) =>
this.propertySelectors.Add(selector);
Expand Down
Expand Up @@ -2,7 +2,6 @@
{
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SimpleInjector.Advanced;
Expand All @@ -17,7 +16,7 @@ public void BuildExpression_WithNullParameterArgument_ThrowsExpectedException()
var behavior = GetContainerOptions().DependencyInjectionBehavior;

// Act
Action action = () => behavior.GetInstanceProducerFor(null);
Action action = () => behavior.GetInstanceProducer(null, false);

// Assert
AssertThat.ThrowsWithParamName<ArgumentNullException>("consumer", action);
Expand Down Expand Up @@ -62,9 +61,7 @@ public void Verify_TValueTypeParameter_ThrowsExpectedException()
var constructor =
typeof(TypeWithSinglePublicConstructorWithValueTypeParameter).GetConstructors().Single();

var consumer = new InjectionConsumerInfo(
constructor.DeclaringType,
constructor.GetParameters().Single());
var consumer = new InjectionConsumerInfo(constructor.GetParameters().Single());

try
{
Expand Down Expand Up @@ -96,9 +93,7 @@ public void Verify_StringTypeParameter_ThrowsExpectedException()
var constructor =
typeof(TypeWithSinglePublicConstructorWithStringTypeParameter).GetConstructors().Single();

var consumer = new InjectionConsumerInfo(
constructor.DeclaringType,
constructor.GetParameters().Single());
var consumer = new InjectionConsumerInfo(constructor.GetParameters().Single());

try
{
Expand Down Expand Up @@ -134,7 +129,7 @@ private sealed class FakeDependencyInjectionBehavior : IDependencyInjectionBehav
{
public InstanceProducer ProducerToReturn { get; set; }

public InstanceProducer GetInstanceProducerFor(InjectionConsumerInfo c) => this.ProducerToReturn;
public InstanceProducer GetInstanceProducer(InjectionConsumerInfo c, bool f) => this.ProducerToReturn;

public void Verify(InjectionConsumerInfo consumer)
{
Expand Down
Expand Up @@ -483,7 +483,7 @@ public PredicatePropertySelectionBehavior(Predicate<PropertyInfo> selector)
this.selector = selector;
}

public bool SelectProperty(PropertyInfo property) => this.selector(property);
public bool SelectProperty(Type type, PropertyInfo property) => this.selector(property);
}
}
}

0 comments on commit 1a5a8d4

Please sign in to comment.