Skip to content

Commit

Permalink
Merge branch 'feature-554' of https://github.com/simpleinjector/Simpl…
Browse files Browse the repository at this point in the history
…eInjector into v4.5.0-beta1

# Conflicts:
#	src/SimpleInjector/Container.Registration.CollectionRegistrator.cs
  • Loading branch information
dotnetjunkie committed Mar 14, 2019
2 parents 810eb60 + 164b5ae commit 64c78f4
Show file tree
Hide file tree
Showing 20 changed files with 905 additions and 202 deletions.
@@ -0,0 +1,19 @@
namespace SimpleInjector.Tests.Unit.Diagnostics
{
using SimpleInjector.Diagnostics;

public static class ContainerCollectionRegistratorExtensions
{
public static void AppendCollection<TService, TImplementation>(
this Container container,
Lifestyle lifestyle,
DiagnosticType suppression)
where TImplementation : class, TService
{
var reg = lifestyle.CreateRegistration<TImplementation>(container);
reg.SuppressDiagnosticWarning(suppression, "For testing");

container.Collection.Append(typeof(TService), reg);
}
}
}

Large diffs are not rendered by default.

30 changes: 1 addition & 29 deletions src/SimpleInjector.Tests.Unit/Diagnostics/TestHelpers.cs
Expand Up @@ -8,35 +8,7 @@
internal static class TestHelpers
{
internal static string ToFriendlyNamesText(this IEnumerable<Type> types) =>
string.Join(", ", types.Select(ToFriendlyName));

internal static string ToFriendlyName(this Type type)
{
if (type == null)
{
return "null";
}

string name = type.Name;

if (type.IsNested && !type.IsGenericParameter)
{
name = type.DeclaringType.ToFriendlyName() + "+" + type.Name;
}

var genericArguments = GetGenericArguments(type);

if (genericArguments.Length == 0)
{
return name;
}

name = name.Substring(0, name.IndexOf('`'));

var argumentNames = genericArguments.Select(argument => argument.ToFriendlyName()).ToArray();

return name + "<" + string.Join(", ", argumentNames) + ">";
}
string.Join(", ", types.Select(TypesExtensions.ToFriendlyName));

private static Type[] GetGenericArguments(Type type)
{
Expand Down
8 changes: 6 additions & 2 deletions src/SimpleInjector.Tests.Unit/MemoryTests.cs
Expand Up @@ -24,7 +24,9 @@ public void CreatingManyContainers_WithNoRegistrations_DoesNotIncreaseMemoryFoot
};

// Warmup
BuildContainers(BuildEmptyContainer, count: 10);
BuildContainers(BuildEmptyContainer, count: values.NumberOfIterations);
GetTotalMemory();
BuildContainers(BuildEmptyContainer, count: values.NumberOfIterations);

values.InitialMemoryFootprint = GetTotalMemory();

Expand All @@ -46,7 +48,9 @@ public void CreatingManyContainers_WithDecoratorRegistration_DoesNotIncreaseMemo
};

// Warmup
BuildContainers(BuildSimpleVerifiedContainerWithDecorator, count: 10);
BuildContainers(BuildSimpleVerifiedContainerWithDecorator, count: values.NumberOfIterations);
GetTotalMemory();
BuildContainers(BuildSimpleVerifiedContainerWithDecorator, count: values.NumberOfIterations);

values.InitialMemoryFootprint = GetTotalMemory();

Expand Down
53 changes: 45 additions & 8 deletions src/SimpleInjector/Advanced/KnownRelationship.cs
@@ -1,7 +1,7 @@
#region Copyright Simple Injector Contributors
/* The Simple Injector is an easy-to-use Inversion of Control library for .NET
*
* Copyright (c) 2013 Simple Injector Contributors
* Copyright (c) 2013-2019 Simple Injector Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction, including
Expand Down Expand Up @@ -34,12 +34,13 @@ namespace SimpleInjector.Advanced
[DebuggerDisplay(nameof(KnownRelationship))]
public sealed class KnownRelationship : IEquatable<KnownRelationship>
{
// This constructor is here for backwards compatibility: the library itself uses the internal ctor.
/// <summary>Initializes a new instance of the <see cref="KnownRelationship"/> class.</summary>
/// <param name="implementationType">The implementation type of the parent type.</param>
/// <param name="lifestyle">The lifestyle of the parent type.</param>
/// <param name="dependency">The type that the parent depends on (it is injected into the parent).</param>
public KnownRelationship(Type implementationType, Lifestyle lifestyle,
InstanceProducer dependency)
public KnownRelationship(
Type implementationType, Lifestyle lifestyle, InstanceProducer dependency)
{
Requires.IsNotNull(implementationType, nameof(implementationType));
Requires.IsNotNull(lifestyle, nameof(lifestyle));
Expand All @@ -50,6 +51,24 @@ public sealed class KnownRelationship : IEquatable<KnownRelationship>
this.Dependency = dependency;
}

internal KnownRelationship(
Type implementationType,
Lifestyle lifestyle,
InjectionConsumerInfo consumer,
InstanceProducer dependency,
string additionalInformation = null)
{
Requires.IsNotNull(implementationType, nameof(implementationType));
Requires.IsNotNull(lifestyle, nameof(lifestyle));
Requires.IsNotNull(dependency, nameof(dependency));

this.ImplementationType = implementationType;
this.Lifestyle = lifestyle;
this.Consumer = consumer;
this.Dependency = dependency;
this.AdditionalInformation = additionalInformation ?? string.Empty;
}

/// <summary>Gets the implementation type of the parent type of the relationship.</summary>
/// <value>The implementation type of the parent type of the relationship.</value>
[DebuggerDisplay("{" + nameof(ImplementationTypeDebuggerDisplay) + ", nq}")]
Expand All @@ -63,6 +82,11 @@ public sealed class KnownRelationship : IEquatable<KnownRelationship>
/// <value>The type that the parent depends on.</value>
public InstanceProducer Dependency { get; }

// WARNING: Can be null.
internal InjectionConsumerInfo Consumer { get; }

internal string AdditionalInformation { get; } = string.Empty;

[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode",
Justification = "This method is called by the debugger.")]
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
Expand All @@ -79,8 +103,18 @@ public sealed class KnownRelationship : IEquatable<KnownRelationship>

/// <summary>Serves as a hash function for a particular type.</summary>
/// <returns>A hash code for the current <see cref="KnownRelationship"/>.</returns>
public override int GetHashCode() =>
this.ImplementationType.GetHashCode() ^ this.Lifestyle.GetHashCode() ^ this.Dependency.GetHashCode();
public override int GetHashCode() =>
this.ImplementationType.GetHashCode()
^ this.Lifestyle.GetHashCode()
^ this.Consumer?.GetHashCode() ?? 0
^ this.Dependency.GetHashCode();

/// <summary>
/// Determines whether the specified object is equal to the current object.
/// </summary>
/// <param name="obj">The object to compare with the current object.</param>
/// <returns>true if the specified object is equal to the current object; otherwise, false.</returns>
public override bool Equals(object obj) => this.Equals(obj as KnownRelationship);

/// <summary>
/// Determines whether the specified <see cref="KnownRelationship"/> is equal to the current
Expand All @@ -102,9 +136,12 @@ public bool Equals(KnownRelationship other)
}

return
this.ImplementationType == other.ImplementationType &&
this.Lifestyle == other.Lifestyle &&
this.Dependency == other.Dependency;
this.ImplementationType.Equals(other.ImplementationType)
&& this.Lifestyle.Equals(other.Lifestyle)
&& this.Dependency.Equals(other.Dependency)
&& (this.Consumer?.Equals(other.Consumer)
?? other.Consumer?.Equals(this.Consumer)
?? true);
}
}
}
10 changes: 8 additions & 2 deletions src/SimpleInjector/Advanced/PropertyInjectionHelper.cs
Expand Up @@ -192,7 +192,8 @@ private static void VerifyProperty(PropertyInfo property)

return new PropertyInjectionData(
expression: Expression.Invoke(Expression.Constant(propertyInjectionDelegate), arguments),
producers: producers.Concat(data.Producers));
producers: producers.Concat(data.Producers),
properties: properties.Concat(data.Properties));
}

private InstanceProducer[] GetPropertyInstanceProducers(PropertyInfo[] properties)
Expand Down Expand Up @@ -240,11 +241,16 @@ internal struct PropertyInjectionData
{
public readonly Expression Expression;
public readonly IEnumerable<InstanceProducer> Producers;
public readonly IEnumerable<PropertyInfo> Properties;

public PropertyInjectionData(Expression expression, IEnumerable<InstanceProducer> producers = null)
public PropertyInjectionData(
Expression expression,
IEnumerable<InstanceProducer> producers = null,
IEnumerable<PropertyInfo> properties = null)
{
this.Expression = expression;
this.Producers = producers ?? Enumerable.Empty<InstanceProducer>();
this.Properties = properties ?? Enumerable.Empty<PropertyInfo>();
}
}
}
Expand Down
Expand Up @@ -979,8 +979,7 @@ private void RegisterForVerification<TService>(ContainerControlledCollection<TSe
// which allows it to be verified. To prevent memory leaks however, this external producer is
// linked using a WeakReference to allow it to be GCed. To prevent this from happening, while
// the application keeps referencing the collection, we let the collection reference the producer.
collection.ParentProducer =
SingletonLifestyle.CreateControlledCollectionProducer(collection, this.Container);
collection.ParentProducer = collection.CreateInstanceProducer(this.Container);
}

// This method is internal to prevent the main API of the framework from being 'polluted'. The
Expand Down
19 changes: 5 additions & 14 deletions src/SimpleInjector/Container.Resolving.cs
Expand Up @@ -572,24 +572,15 @@ private InstanceProducer TryBuildStreamInstanceProducer(Type collectionType)

Type elementType = collectionType.GetGenericArguments()[0];

object stream = this.GetAllInstances(elementType);
var stream = this.GetAllInstances(elementType) as IContainerControlledCollection;

if (!(stream is IContainerControlledCollection))
if (stream == null)
{
return null;
}

// We need special handling for Collection<T>, because the ContainerControlledCollection does not
// (and can't) inherit from Collection<T>. So we have to wrap that stream into a Collection<T>.
if (serviceTypeDefinition == typeof(Collection<>))
{
Type listType = typeof(IList<>).MakeGenericType(elementType);
stream = collectionType.GetConstructor(new[] { listType }).Invoke(new[] { stream });
}

var registration =
SingletonLifestyle.CreateSingleInstanceRegistration(collectionType, stream, this);

Registration registration = stream.CreateRegistration(collectionType, this);

return new InstanceProducer(collectionType, registration)
{
IsContainerAutoRegistered = !((IEnumerable<object>)stream).Any()
Expand All @@ -600,7 +591,7 @@ private InstanceProducer BuildEmptyCollectionInstanceProducerForEnumerable(Type
{
Type elementType = enumerableType.GetGenericArguments()[0];

var collection = DecoratorHelpers.CreateContainerControlledCollection(elementType, this);
var collection = ControlledCollectionHelper.CreateContainerControlledCollection(elementType, this);

var registration = new ExpressionRegistration(Expression.Constant(collection, enumerableType), this);

Expand Down
71 changes: 0 additions & 71 deletions src/SimpleInjector/Decorators/DecoratorHelpers.cs
Expand Up @@ -30,8 +30,6 @@ namespace SimpleInjector.Decorators
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using SimpleInjector.Advanced;
using SimpleInjector.Internals;

internal static partial class DecoratorHelpers
{
Expand Down Expand Up @@ -86,52 +84,6 @@ internal static void AddRange<T>(this Collection<T> collection, IEnumerable<T> r
}
}

internal static Registration CreateRegistrationForContainerControlledCollection(Type serviceType,
IContainerControlledCollection instance, Container container)
{
Type enumerableServiceType = typeof(IEnumerable<>).MakeGenericType(serviceType);

return new ContainerControlledCollectionRegistration(enumerableServiceType, instance, container)
{
IsCollection = true
};
}

internal static IContainerControlledCollection ExtractContainerControlledCollectionFromRegistration(
Registration registration)
{
var controlledRegistration = registration as ContainerControlledCollectionRegistration;

// We can only determine the value when registration is created using the
// CreateRegistrationForContainerControlledCollection method. When the registration is null the
// collection might be registered as container-uncontrolled collection.
if (controlledRegistration == null)
{
return null;
}

return controlledRegistration.Collection;
}

internal static IContainerControlledCollection CreateContainerControlledCollection(
Type serviceType, Container container)
{
var collection = Activator.CreateInstance(
typeof(ContainerControlledCollection<>).MakeGenericType(serviceType),
new object[] { container });

return (IContainerControlledCollection)collection;
}

internal static bool IsContainerControlledCollectionExpression(Expression enumerableExpression)
{
var constantExpression = enumerableExpression as ConstantExpression;

object enumerable = constantExpression != null ? constantExpression.Value : null;

return enumerable is IContainerControlledCollection;
}

internal static IEnumerable Select(this IEnumerable source, Type type, Delegate selector)
{
var selectMethod = EnumerableSelectMethod.MakeGenericMethod(type, type);
Expand Down Expand Up @@ -244,28 +196,5 @@ internal static bool IsScopeDecorateeFactoryDependencyParameter(Type parameterTy
&& parameterType.GetGenericTypeDefinition() == typeof(Func<,>)
&& parameterType == typeof(Func<,>).MakeGenericType(typeof(Scope), decoratingType);
}

private sealed class ContainerControlledCollectionRegistration : Registration
{
internal ContainerControlledCollectionRegistration(Type serviceType,
IContainerControlledCollection collection, Container container)
: base(Lifestyle.Singleton, container)
{
this.Collection = collection;
this.ImplementationType = serviceType;
}

public override Type ImplementationType { get; }

internal override bool MustBeVerified => !this.Collection.AllProducersVerified;

internal IContainerControlledCollection Collection { get; }

public override Expression BuildExpression() =>
Expression.Constant(this.Collection, this.ImplementationType);

internal override KnownRelationship[] GetRelationshipsCore() =>
base.GetRelationshipsCore().Concat(this.Collection.GetRelationships()).ToArray();
}
}
}
2 changes: 1 addition & 1 deletion src/SimpleInjector/Decorators/DecoratorInterceptor.cs
Expand Up @@ -90,7 +90,7 @@ private void TryToApplyDecorator(ExpressionBuiltEventArgs e)
private void TryToApplyDecoratorOnContainerUncontrolledCollections(ExpressionBuiltEventArgs e)
{
if (!IsCollectionType(e.RegisteredServiceType) ||
DecoratorHelpers.IsContainerControlledCollectionExpression(e.Expression))
ControlledCollectionHelper.IsContainerControlledCollectionExpression(e.Expression))
{
// NOTE: Decorators on controlled collections will be applied by the normal mechanism.
return;
Expand Down
Expand Up @@ -68,16 +68,18 @@ where LifestyleMismatchChecker.HasLifestyleMismatch(container, relationship)
relationship: relationship))
.ToArray();

private static string BuildRelationshipDescription(KnownRelationship relationship) =>
private static string BuildRelationshipDescription(KnownRelationship relationship) =>
string.Format(CultureInfo.InvariantCulture,
"{0} ({1}) depends on {2}{3} ({4}).",
"{0} ({1}) depends on {2}{3} ({4}).{5}{6}",
relationship.ImplementationType.ToFriendlyName(),
relationship.Lifestyle.Name,
relationship.Dependency.ServiceType.ToFriendlyName(),
relationship.Dependency.ServiceType != relationship.Dependency.ImplementationType
? " implemented by " + relationship.Dependency.ImplementationType.ToFriendlyName()
: string.Empty,
relationship.Dependency.Lifestyle.Name);
relationship.Dependency.Lifestyle.Name,
relationship.AdditionalInformation == string.Empty ? string.Empty : " ",
relationship.AdditionalInformation);

private static string ServicePlural(int number) => number == 1 ? "service" : "services";

Expand Down

0 comments on commit 64c78f4

Please sign in to comment.