This commit is contained in:
Eugene Sadovoi 2019-05-20 15:31:00 -04:00
Родитель b0c69184d2
Коммит 1ce7a54b62
18 изменённых файлов: 358 добавлений и 158 удалений

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

@ -31,7 +31,6 @@ namespace Unity.Processors
yield return GetResolverExpression(info, value);
break;
// Injection Member
case InjectionMember<TMemberInfo, TData> injectionMember:
@ -39,7 +38,6 @@ namespace Unity.Processors
injectionMember.Data);
break;
// Unknown
default:
throw new InvalidOperationException($"Unknown MemberInfo<{typeof(TMemberInfo)}> type");

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

@ -4,6 +4,7 @@ using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Unity.Builder;
using Unity.Exceptions;
using Unity.Injection;
using Unity.Lifetime;
using Unity.Policy;
@ -96,13 +97,21 @@ namespace Unity.Processors
var variable = Expression.Variable(info.DeclaringType);
var parametersExpr = CreateParameterExpressions(info.GetParameters(), resolvers);
return Expression.IfThen(
Expression.Equal(Expression.Constant(null), BuilderContextExpression.Existing),
Expression.Block(new[] { variable }, new Expression[]
{
Expression.Assign(variable, Expression.New(info, parametersExpr)),
Expression.Assign(BuilderContextExpression.Existing, Expression.Convert(variable, typeof(object)))
}));
try
{
return Expression.IfThen(
Expression.Equal(Expression.Constant(null), BuilderContextExpression.Existing),
Expression.Block(new[] { variable }, new Expression[]
{
Expression.Assign(variable, Expression.New(info, parametersExpr)),
Expression.Assign(BuilderContextExpression.Existing, Expression.Convert(variable, typeof(object)))
}));
}
catch (ArgumentException ex)
{
throw new InvalidRegistrationException("Invalid Argument", ex);
}
}
#endregion

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

@ -4,6 +4,7 @@ using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Unity.Builder;
using Unity.Exceptions;
using Unity.Policy;
using Unity.Resolution;
@ -37,9 +38,16 @@ namespace Unity.Processors
protected override Expression GetResolverExpression(MethodInfo info, object resolvers)
{
return Expression.Call(
Expression.Convert(BuilderContextExpression.Existing, info.DeclaringType),
info, CreateParameterExpressions(info.GetParameters(), resolvers));
try
{
return Expression.Call(
Expression.Convert(BuilderContextExpression.Existing, info.DeclaringType),
info, CreateParameterExpressions(info.GetParameters(), resolvers));
}
catch (ArgumentException ex)
{
throw new InvalidRegistrationException("Invalid Argument", ex);
}
}
#endregion

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

@ -48,13 +48,13 @@ namespace Unity.Storage
public void Add(Type type, string name, InternalRegistration registration)
{
var hashCode = ((type?.GetHashCode() ?? 0 + 37) ^ (name?.GetHashCode() ?? 0 + 17)) & 0x7FFFFFFF;
var hashCode = (37 ^ (name?.GetHashCode() ?? 0 + 17)) & 0x7FFFFFFF;
var bucket = hashCode % _buckets.Length;
var collisionCount = 0;
for (int i = _buckets[bucket]; --i >= 0; i = _entries[i].Next)
{
ref var entry = ref _entries[i];
if (entry.HashCode == hashCode && entry.RegisteredType == type)
if (entry.HashCode == hashCode && entry.Name == name)
{
entry.RegisteredType = type;
entry.Name = name;

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

@ -3,6 +3,7 @@ using System.Globalization;
using System.Linq;
using System.Reflection;
using Unity.Builder;
using Unity.Exceptions;
using Unity.Injection;
using Unity.Lifetime;
using Unity.Policy;
@ -47,16 +48,21 @@ namespace Unity.Strategies
if (null == resolver)
{
// Check if can create at all
if (!(context.Registration is ContainerRegistration) &&
#if NETCOREAPP1_0 || NETSTANDARD1_0
context.RegistrationType.GetTypeInfo().IsGenericTypeDefinition)
if (!(context.Registration is ContainerRegistration) && context.RegistrationType.GetTypeInfo().IsGenericTypeDefinition)
#else
context.RegistrationType.IsGenericTypeDefinition)
if (!(context.Registration is ContainerRegistration) && context.RegistrationType.IsGenericTypeDefinition)
#endif
{
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture,
"The type {0} is an open generic type. An open generic type cannot be resolved.",
context.RegistrationType.FullName));
context.RegistrationType.FullName), new InvalidRegistrationException());
}
else if (context.Type.IsArray && context.Type.GetArrayRank() > 1)
{
var message = $"Invalid array {context.Type}. Only arrays of rank 1 are supported";
throw new ArgumentException(message, new InvalidRegistrationException());
}
// Get resolver factory

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

@ -1,7 +1,10 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using Unity.Policy;
using Unity.Registration;
@ -18,6 +21,64 @@ namespace Unity
#endregion
#region Error Message
private static Func<Exception, string> CreateMessage = (Exception ex) =>
{
return $"Resolution failed with error: {ex.Message}\n\nFor more detailed information run Unity in debug mode: new UnityContainer(ModeFlags.Diagnostic)";
};
private static string CreateDiagnosticMessage(Exception ex)
{
const string line = "_____________________________________________________";
var builder = new StringBuilder();
builder.AppendLine(ex.Message);
builder.AppendLine(line);
builder.AppendLine("Exception occurred while:");
foreach (DictionaryEntry item in ex.Data)
builder.AppendLine(DataToString(item.Value));
return builder.ToString();
}
private static string DataToString(object value)
{
switch (value)
{
case ParameterInfo parameter:
return $" for parameter: {parameter.Name}";
case ConstructorInfo constructor:
var ctorSignature = string.Join(", ", constructor.GetParameters().Select(p => $"{p.ParameterType.Name} {p.Name}"));
return $" on constructor: {constructor.DeclaringType.Name}({ctorSignature})";
case MethodInfo method:
var methodSignature = string.Join(", ", method.GetParameters().Select(p => $"{p.ParameterType.Name} {p.Name}"));
return $" on method: {method.Name}({methodSignature})";
case PropertyInfo property:
return $" for property: {property.Name}";
case FieldInfo field:
return $" for field: {field.Name}";
case Type type:
return $"\n• while resolving: {type.Name}";
case Tuple<Type, string> tuple:
return $"\n• while resolving: {tuple.Item1.Name} registered with name: {tuple.Item2}";
case Tuple<Type, Type> tuple:
return $" mapped to: {tuple.Item1?.Name}";
}
return value.ToString();
}
#endregion
#region Debug Support
private string DebugName()

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

@ -27,20 +27,20 @@ namespace Unity
/// <inheritdoc />
IUnityContainer IUnityContainer.RegisterType(Type typeFrom, Type typeTo, string name, ITypeLifetimeManager lifetimeManager, InjectionMember[] injectionMembers)
{
var mappedToType = typeTo;
var registeredType = typeFrom ?? typeTo;
if (null == registeredType) throw new ArgumentNullException(nameof(typeTo));
// Validate if they are assignable
TypeValidator?.Invoke(typeFrom, typeTo);
try
{
var mappedToType = typeTo;
var registeredType = typeFrom ?? typeTo;
LifetimeManager manager = (null != lifetimeManager)
? (LifetimeManager)lifetimeManager
: TypeLifetimeManager.CreateLifetimePolicy();
// Validate input
if (null == typeTo) throw new ArgumentNullException(nameof(typeTo));
if (manager.InUse) throw new InvalidOperationException(LifetimeManagerInUse);
// Validate if they are assignable
TypeValidator?.Invoke(typeFrom, typeTo);
// Create registration and add to appropriate storage
var container = manager is SingletonLifetimeManager ? _root : this;
var registration = new ContainerRegistration(_validators, typeTo, manager, injectionMembers);
@ -232,7 +232,6 @@ namespace Unity
{
// Verify arguments
if (null == type) throw new ArgumentNullException(nameof(type));
name = string.IsNullOrEmpty(name) ? null : name;
var registration = (InternalRegistration)GetRegistration(type, name);
var context = new BuilderContext

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

@ -213,14 +213,26 @@ namespace Unity
var infoFrom = typeFrom.GetTypeInfo();
var infoTo = typeTo.GetTypeInfo();
if (typeFrom != null && !infoFrom.IsGenericType && !infoTo.IsGenericType && !infoFrom.IsAssignableFrom(infoTo))
if (null != typeFrom && typeFrom != null && !infoFrom.IsGenericType &&
null != typeTo && !infoTo.IsGenericType && !infoFrom.IsAssignableFrom(infoTo))
#else
if (typeFrom != null && !typeFrom.IsGenericType && !typeTo.IsGenericType && !typeFrom.IsAssignableFrom(typeTo))
if (null != typeFrom && typeFrom != null && !typeFrom.IsGenericType &&
null != typeTo && !typeTo.IsGenericType && !typeFrom.IsAssignableFrom(typeTo))
#endif
{
throw new ArgumentException($"The type {typeTo} cannot be assigned to variables of type {typeFrom}.");
}
#if NETSTANDARD1_0 || NETCOREAPP1_0
if (null != typeFrom && null != typeTo && infoFrom.IsGenericType && infoTo.IsArray &&
infoFrom.GetGenericTypeDefinition() == typeof(IEnumerable<>))
#else
if (null != typeFrom && null != typeTo && typeFrom.IsGenericType && typeTo.IsArray &&
typeFrom.GetGenericTypeDefinition() == typeof(IEnumerable<>))
#endif
throw new ArgumentException($"Type mapping of IEnumerable<T> to array T[] is not supported.");
#if NETSTANDARD1_0 || NETCOREAPP1_0
if (null == typeFrom && infoTo.IsInterface)
#else

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

@ -353,8 +353,14 @@ namespace Unity
{
context.RequiresRecovery?.Recover();
throw new ResolutionFailedException(context.RegistrationType, context.Name,
"For more information add Diagnostic extension: Container.AddExtension(new Diagnostic())", ex);
if (!(ex.InnerException is InvalidRegistrationException) &&
!(ex is InvalidRegistrationException) &&
!(ex is ObjectDisposedException) &&
!(ex is MemberAccessException) &&
!(ex is MakeGenericTypeFailedException))
throw;
throw new ResolutionFailedException(context.RegistrationType, context.Name, CreateMessage(ex), ex);
}
return context.Existing;
@ -380,6 +386,7 @@ namespace Unity
catch (Exception ex)
{
context.RequiresRecovery?.Recover();
ex.Data.Add(Guid.NewGuid(), null == context.Name
? context.RegistrationType == context.Type
? (object)context.Type
@ -388,64 +395,12 @@ namespace Unity
? (object)new Tuple<Type, string>(context.Type, context.Name)
: new Tuple<Type, Type, string>(context.RegistrationType, context.Type, context.Name));
var builder = new StringBuilder();
builder.AppendLine(ex.Message);
builder.AppendLine("_____________________________________________________");
builder.AppendLine("Exception occurred while:");
builder.AppendLine();
var indent = 0;
foreach (DictionaryEntry item in ex.Data)
{
for (var c = 0; c < indent; c++) builder.Append(" ");
builder.AppendLine(CreateErrorMessage(item.Value));
indent += 1;
}
var message = builder.ToString();
var message = CreateDiagnosticMessage(ex);
throw new ResolutionFailedException( context.RegistrationType, context.Name, message, ex);
}
return context.Existing;
string CreateErrorMessage(object value)
{
switch (value)
{
case ParameterInfo parameter:
return $" for parameter: '{parameter.Name}'";
case ConstructorInfo constructor:
var ctorSignature = string.Join(", ", constructor.GetParameters().Select(p => $"{p.ParameterType.Name} {p.Name}"));
return $"on constructor: {constructor.DeclaringType.Name}({ctorSignature})";
case MethodInfo method:
var methodSignature = string.Join(", ", method.GetParameters().Select(p => $"{p.ParameterType.Name} {p.Name}"));
return $" on method: {method.Name}({methodSignature})";
case PropertyInfo property:
return $" for property: '{property.Name}'";
case FieldInfo field:
return $" for field: '{field.Name}'";
case Type type:
return $"·resolving type: '{type.Name}'";
case Tuple<Type, string> tuple:
return $"•resolving type: '{tuple.Item1.Name}' registered with name: '{tuple.Item2}'";
case Tuple<Type, Type> tuple:
return $"•resolving type: '{tuple.Item1?.Name}' mapped to '{tuple.Item2?.Name}'";
case Tuple<Type, Type, string> tuple:
return $"•resolving type: '{tuple.Item1?.Name}' mapped to '{tuple.Item2?.Name}' and registered with name: '{tuple.Item3}'";
}
return value.ToString();
}
}
#endregion
@ -529,7 +484,7 @@ namespace Unity
if (LifetimeManager.NoValue != result) return result;
throw new InvalidOperationException($"Circular reference for Type: {parentRef.Type}, Name: {parentRef.Name}",
new CircularDependencyException());
new CircularDependencyException(parentRef.Type, parentRef.Name));
}
}
#endif
@ -552,8 +507,7 @@ namespace Unity
{
var parentRef = Unsafe.AsRef<BuilderContext>(parent.ToPointer());
if (thisContext.RegistrationType == parentRef.RegistrationType && thisContext.Name == parentRef.Name)
throw new InvalidOperationException($"Circular reference for Type: {thisContext.Type}, Name: {thisContext.Name}",
new CircularDependencyException());
throw new CircularDependencyException(thisContext.Type, thisContext.Name);
parent = parentRef.Parent;
}

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

@ -1,30 +0,0 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Unity;
namespace Compiled
{
[TestClass]
public class Exceptions : Unity.Specification.Diagnostic.Exceptions.SpecificationTests
{
public override IUnityContainer GetContainer()
{
return new UnityContainer().AddExtension(new ForceCompillation())
.AddExtension(new Diagnostic());
}
}
}
namespace Resolved
{
[TestClass]
public class Exceptions : Unity.Specification.Diagnostic.Exceptions.SpecificationTests
{
public override IUnityContainer GetContainer()
{
return new UnityContainer().AddExtension(new ForceActivation())
.AddExtension(new Diagnostic());
}
}
}

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

@ -1,7 +1,10 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using Unity;
using Unity.Builder;
using Unity.Extension;
using Unity.Specification.Diagnostic.Issues.GitHub;
using Unity.Strategies;
namespace GitHub
{
@ -16,4 +19,85 @@ namespace GitHub
}
}
internal class SpyExtension : UnityContainerExtension
{
private BuilderStrategy strategy;
private UnityBuildStage stage;
private object policy;
private Type policyType;
public SpyExtension(BuilderStrategy strategy, UnityBuildStage stage)
{
this.strategy = strategy;
this.stage = stage;
}
public SpyExtension(BuilderStrategy strategy, UnityBuildStage stage, object policy, Type policyType)
{
this.strategy = strategy;
this.stage = stage;
this.policy = policy;
this.policyType = policyType;
}
protected override void Initialize()
{
Context.Strategies.Add(this.strategy, this.stage);
if (this.policy != null)
{
Context.Policies.Set(null, null, this.policyType, this.policy);
}
}
}
internal class SpyStrategy : BuilderStrategy
{
private object existing = null;
private bool buildUpWasCalled = false;
public override void PreBuildUp(ref BuilderContext context)
{
this.buildUpWasCalled = true;
this.existing = context.Existing;
this.UpdateSpyPolicy(ref context);
}
public override void PostBuildUp(ref BuilderContext context)
{
this.existing = context.Existing;
}
public object Existing
{
get { return this.existing; }
}
public bool BuildUpWasCalled
{
get { return this.buildUpWasCalled; }
}
private void UpdateSpyPolicy(ref BuilderContext context)
{
SpyPolicy policy = (SpyPolicy)context.Get(null, null, typeof(SpyPolicy));
if (policy != null)
{
policy.WasSpiedOn = true;
}
}
}
internal class SpyPolicy
{
private bool wasSpiedOn;
public bool WasSpiedOn
{
get { return wasSpiedOn; }
set { wasSpiedOn = value; }
}
}
}

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

@ -4,7 +4,25 @@ using Unity;
namespace Registration
{
[TestClass]
public class Validation : Unity.Specification.Diagnostic.Registration.SpecificationTests
public class Types : Unity.Specification.Diagnostic.Registration.Types.SpecificationTests
{
public override IUnityContainer GetContainer()
{
return new UnityContainer().AddExtension(new Diagnostic());
}
}
[TestClass]
public class Instance : Unity.Specification.Diagnostic.Registration.Instance.SpecificationTests
{
public override IUnityContainer GetContainer()
{
return new UnityContainer().AddExtension(new Diagnostic());
}
}
[TestClass]
public class Factory : Unity.Specification.Diagnostic.Registration.Factory.SpecificationTests
{
public override IUnityContainer GetContainer()
{

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

@ -22,11 +22,39 @@ namespace Registration
}
[TestClass]
public class Validation : Unity.Specification.Registration.Syntax.SpecificationTests
public class Syntax : Unity.Specification.Registration.Syntax.SpecificationTests
{
public override IUnityContainer GetContainer()
{
return new UnityContainer();
}
}
[TestClass]
public class Factory : Unity.Specification.Registration.Factory.SpecificationTests
{
public override IUnityContainer GetContainer()
{
return new UnityContainer();
}
}
[TestClass]
public class Instance : Unity.Specification.Registration.Instance.SpecificationTests
{
public override IUnityContainer GetContainer()
{
return new UnityContainer();
}
}
[TestClass]
public class Types : Unity.Specification.Registration.Types.SpecificationTests
{
public override IUnityContainer GetContainer()
{
return new UnityContainer();
}
}
}

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

@ -1,15 +0,0 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Unity;
using Unity.Specification.Registration;
namespace Specification.Tests
{
[TestClass]
public class Registration : SpecificationTests
{
public override IUnityContainer GetContainer()
{
return new UnityContainer();
}
}
}

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

@ -1,5 +1,7 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Threading;
using Unity.Injection;
using Unity.Lifetime;
@ -17,6 +19,95 @@ namespace Unity.Tests.v5.CollectionSupport
}
[TestMethod]
public void ClosedGenericsWinInArray()
{
// Arrange
var Name = "name";
var instance = new Foo<IService>(new OtherService());
IUnityContainer Container = new UnityContainer();
Container.RegisterInstance<IFoo<IService>>(Name, instance)
.RegisterType(typeof(IFoo<>), typeof(Foo<>), Name)
.RegisterType<IFoo<IService>, Foo<IService>>("closed")
.RegisterType<IService, Service>();
// Act
var array = Container.Resolve<IFoo<IService>[]>();
// Assert
Assert.AreEqual(2, array.Length);
Assert.IsNotNull(array[0]);
Assert.IsNotNull(array[1]);
}
public interface IFoo<TEntity>
{
TEntity Value { get; }
}
public class Foo<TEntity> : IFoo<TEntity>
{
public Foo()
{
}
public Foo(TEntity value)
{
Value = value;
}
public TEntity Value { get; }
}
public interface IService
{
}
public interface IOtherService
{
}
public class Service : IService, IDisposable
{
public string Id { get; } = Guid.NewGuid().ToString();
public static int Instances;
public Service()
{
Interlocked.Increment(ref Instances);
}
public bool Disposed;
public void Dispose()
{
Disposed = true;
}
}
public class OtherService : IService, IOtherService, IDisposable
{
[InjectionConstructor]
public OtherService()
{
}
public OtherService(IUnityContainer container)
{
}
public bool Disposed = false;
public void Dispose()
{
Disposed = true;
}
}
[TestMethod]
public void ResolvingAnArrayTypeSucceedsIfItWasNotRegistered()
{

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

@ -197,7 +197,7 @@ namespace Unity.Tests.v5.Generics
public class ServiceB<T> : IService<T> { }
[TestMethod]
[ExpectedException(typeof(ResolutionFailedException))]
[ExpectedException(typeof(InvalidOperationException))]
public void FailedResolveAllTest()
{
var container = new UnityContainer();

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

@ -148,7 +148,7 @@ namespace Unity.Tests.v5.Injection
}
[TestMethod]
[ExpectedException(typeof(InvalidOperationException))]
[ExpectedException(typeof(ArgumentNullException))]
public void RegisterTypeThrowsIfTypeIsNull()
{
IUnityContainer container = new UnityContainer();

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

@ -401,28 +401,5 @@ namespace Unity.Tests.v5.Lifetime
Assert.IsNotNull(obj);
}
private class ATest
{
}
[TestMethod]
public void TestEmpty()
{
UnityContainer uc1 = new UnityContainer();
uc1.RegisterType<ATest>(new ContainerControlledLifetimeManager());
uc1.RegisterType<ATest>(String.Empty, new ContainerControlledLifetimeManager());
uc1.RegisterType<ATest>(null, new ContainerControlledLifetimeManager());
ATest a = uc1.Resolve<ATest>();
ATest b = uc1.Resolve<ATest>(String.Empty);
ATest c = uc1.Resolve<ATest>((string)null);
Assert.AreEqual(a, b);
Assert.AreEqual(b, c);
Assert.AreEqual(a, c);
}
}
}