Skip to content

Commit

Permalink
Allow using enum tags
Browse files Browse the repository at this point in the history
  • Loading branch information
NikolayPianikov authored and NikolayPianikov committed Feb 8, 2021
1 parent 59f4a98 commit 711b0a3
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 20 deletions.
52 changes: 37 additions & 15 deletions IoC.Source/IoC.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5714,6 +5714,11 @@ public static ICompositionRoot<TInstance> BuildUp<TInstance>([NotNull] this IMut
return new CompositionRoot<TInstance>(new Token(container, Disposable.Empty), resolver(container, args));
}

if (typeof(TInstance).Descriptor().IsAbstract())
{
throw new InvalidOperationException("The composition root must be of a non-abstract type, or must be registered with the container.");
}

var buildId = Guid.NewGuid();
var token = container.Bind<TInstance>().Tag(buildId).To();
try
Expand Down Expand Up @@ -8700,12 +8705,13 @@ namespace IoC
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using Core;

/// <summary>
/// Represents a dependency key.
/// </summary>
[PublicAPI]
[DebuggerDisplay("Type = {" + nameof(Type) + "}, Tag = {" + nameof(Tag) + "}")]
[DebuggerDisplay("{ToString()}")]
public struct Key
{
/// <summary>
Expand Down Expand Up @@ -8736,7 +8742,7 @@ public Key([NotNull] Type type, [CanBeNull] object tag = null)

/// <inheritdoc />
[Pure]
public override string ToString() => $"[Type = {Type.FullName}, Tag = {Tag ?? "empty"}, HashCode = {GetHashCode()}]";
public override string ToString() => Tag != null ? $"{Type.GetShortName()}(tag: {Tag})" : Type.GetShortName();

/// <inheritdoc />
[Pure]
Expand Down Expand Up @@ -10186,7 +10192,6 @@ bool ILifetimeBuilder.TryBuildAfterCreation(IBuildContext context, Expression in
return true;
}

[SuppressMessage("ReSharper", "InconsistentlySynchronizedField")]
public virtual void Dispose()
{
Table<TKey, object> instances;
Expand Down Expand Up @@ -12151,7 +12156,7 @@ private CannotBuildExpression() { }
public Expression Resolve(IBuildContext buildContext, IDependency dependency, ILifetime lifetime, Exception error)
{
if (buildContext == null) throw new ArgumentNullException(nameof(buildContext));
throw new InvalidOperationException($"Cannot build expression for the key {buildContext.Key} in the container {buildContext.Container}.\n{buildContext}", error);
throw new InvalidOperationException($"Cannot build expression for {buildContext.Key} from {buildContext.Container}.\n{buildContext}", error);
}
}
}
Expand All @@ -12174,7 +12179,7 @@ public Resolver<T> Resolve<T>(IContainer container, Key key, Exception error)
{
if (container == null) throw new ArgumentNullException(nameof(container));
if (error == null) throw new ArgumentNullException(nameof(error));
throw new InvalidOperationException($"Cannot get resolver for the key {key} from the container {container}.", error);
throw new InvalidOperationException($"Cannot get resolver for {key} from {container}.", error);
}
}
}
Expand All @@ -12199,7 +12204,7 @@ public IToken Resolve(IContainer container, IEnumerable<Key> keys, IDependency d
{
if (container == null) throw new ArgumentNullException(nameof(container));
if (keys == null) throw new ArgumentNullException(nameof(keys));
throw new InvalidOperationException($"Keys {string.Join(", ", keys.Select(i => i.ToString()))} cannot be registered in the container {container}.");
throw new InvalidOperationException($"Keys {string.Join(", ", keys.Select(i => i.ToString()))} cannot be registered in {container}.");
}
}
}
Expand All @@ -12224,7 +12229,7 @@ private CannotResolveConstructor() { }
public IMethod<ConstructorInfo> Resolve(IBuildContext buildContext, IEnumerable<IMethod<ConstructorInfo>> constructors)
{
if (constructors == null) throw new ArgumentNullException(nameof(constructors));
throw new BuildExpressionException($"Cannot find a constructor for the type {buildContext.Key.Type}.\n{buildContext}", null);
throw new BuildExpressionException($"Cannot find a constructor for {buildContext.Key}.\n{buildContext}", null);
}
}
}
Expand All @@ -12246,7 +12251,7 @@ private CannotResolveDependency() { }
public DependencyDescription Resolve(IBuildContext buildContext)
{
if (buildContext == null) throw new ArgumentNullException(nameof(buildContext));
throw new InvalidOperationException($"Cannot find the dependency for the key {buildContext.Key} in the container {buildContext.Container}.\n{buildContext}");
throw new InvalidOperationException($"Cannot find a dependency for {buildContext.Key} in {buildContext.Container}.\n{buildContext}");
}
}
}
Expand All @@ -12267,7 +12272,7 @@ public Type Resolve(IBuildContext buildContext, Type type, int genericTypeArgPos
{
var typeDescriptor = type.Descriptor();
var genericTypeArgs = typeDescriptor.IsGenericTypeDefinition() ? typeDescriptor.GetGenericTypeParameters() : typeDescriptor.GetGenericTypeArguments();
throw new InvalidOperationException($"Cannot resolve the generic type argument \'{genericTypeArgs[genericTypeArgPosition]}\' at position {genericTypeArgPosition} of the type {typeDescriptor.Type}.");
throw new InvalidOperationException($"Cannot resolve the generic type argument \'{genericTypeArgs[genericTypeArgPosition]}\' at position {genericTypeArgPosition} of the type {typeDescriptor.Type.GetShortName()}.");
}
}
}
Expand All @@ -12291,7 +12296,7 @@ public Type Resolve(IBuildContext buildContext, Type registeredType, Type resolv
{
if (registeredType == null) throw new ArgumentNullException(nameof(registeredType));
if (resolvingType == null) throw new ArgumentNullException(nameof(resolvingType));
throw new InvalidOperationException($"Cannot resolve instance type based on the registered type {registeredType} for resolving type {registeredType}.\n{buildContext}");
throw new InvalidOperationException($"Cannot resolve instance type based on the registered type {registeredType.GetShortName()} for resolving type {registeredType.GetShortName()}.\n{buildContext}");
}
}
}
Expand Down Expand Up @@ -12772,15 +12777,15 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCall)
var type = (Type) ((ConstantExpression) Visit(methodCall.Arguments[1]) ?? throw InvalidExpressionError).Value ?? throw InvalidExpressionError;
var containerExpression = methodCall.Arguments[0];
var key = new Key(type);
return CreateDependencyExpression(key, containerExpression, null);
return CreateDependencyExpression(key, containerExpression, null).Convert(methodCall.Type);
}

if (Equals(methodCall.Method, Injections.TryInjectMethodInfo))
{
var type = (Type)((ConstantExpression)Visit(methodCall.Arguments[1]) ?? throw InvalidExpressionError).Value ?? throw InvalidExpressionError;
var containerExpression = methodCall.Arguments[0];
var key = new Key(type);
return CreateDependencyExpression(key, containerExpression, Expression.Default(type));
return CreateDependencyExpression(key, containerExpression, Expression.Default(type)).Convert(methodCall.Type);
}

if (argumentsCount > 3)
Expand All @@ -12795,7 +12800,7 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCall)
var argsExpression = Visit(methodCall.Arguments[3]);

var key = new Key(type, tag);
return OverrideArgsAndCreateDependencyExpression(argsExpression, key, containerExpression, null);
return OverrideArgsAndCreateDependencyExpression(argsExpression, key, containerExpression, null).Convert(methodCall.Type);
}

if (Equals(methodCall.Method, Injections.TryInjectWithTagMethodInfo))
Expand All @@ -12807,7 +12812,7 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCall)
var argsExpression = Visit(methodCall.Arguments[3]);

var key = new Key(type, tag);
return OverrideArgsAndCreateDependencyExpression(argsExpression, key, containerExpression, Expression.Default(type));
return OverrideArgsAndCreateDependencyExpression(argsExpression, key, containerExpression, Expression.Default(type)).Convert(methodCall.Type);
}
}
}
Expand Down Expand Up @@ -13591,7 +13596,7 @@ public void SetDependency(int parameterPosition, Type dependencyType, object dep
injectMethod,
ExpressionBuilderExtensions.ContainerExpression,
Expression.Constant(dependencyType),
Expression.Constant(dependencyTag),
Expression.Constant(dependencyTag).Convert(typeof(object)),
Expression.NewArrayInit(typeof(object), args.Select(Expression.Constant)))
.Convert(dependencyType);

Expand Down Expand Up @@ -14835,6 +14840,18 @@ public static Type ToGenericType([NotNull] this Type type)

return type;
}

[MethodImpl((MethodImplOptions)0x100)]
[NotNull]
public static string GetShortName([NotNull] this Type type)
{
if (!TypeToStringConverter.Shared.TryConvert(type, type, out var typeName))
{
typeName = type.FullName;
}

return typeName ?? "UnknownType";
}
}
}

Expand Down Expand Up @@ -15000,6 +15017,11 @@ protected override Expression VisitMember(MemberExpression node)
}

var newMember = newDeclaringTypeDescriptor.GetDeclaredMembers().Single(i => i.Name == node.Member.Name);
if (node.Expression == null)
{
return Expression.MakeMemberAccess(node.Expression, newMember);
}

var newExpression = Visit(node.Expression);
return newExpression == null ? base.VisitMember(node) : Expression.MakeMemberAccess(newExpression, newMember);
}
Expand Down
16 changes: 12 additions & 4 deletions IoC.Tests/IntegrationTests/AspectOrientedAutowiringTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ public void ShouldUseAspectOrientedAutowiringStrategy()
// Configure the child container by the custom aspect oriented autowiring strategy
using (childContainer.Bind<IAutowiringStrategy>().To(ctx => autowiringStrategy))
// Configure the child container
using (childContainer.Bind<IConsole>().Tag("MyConsole").To(ctx => console.Object))
using (childContainer.Bind<IConsole>().Tag(Tags.MyConsole).To(ctx => console.Object))
using (childContainer.Bind<Clock>().To<Clock>())
using (childContainer.Bind<string>().Tag("Prefix").To(ctx => "info"))
using (childContainer.Bind<string>().Tag(Tags.Prefix).To(ctx => "info"))
using (childContainer.Bind<ILogger>().To<Logger>())
{
// Create a logger
Expand All @@ -49,6 +49,14 @@ public void ShouldUseAspectOrientedAutowiringStrategy()
console.Verify(i => i.WriteLine(It.IsRegex(".+ - info: Hello")));
}

// This enum is not required and created just to manage all tags.
// It is possible to use tags of any type.
public enum Tags
{
MyConsole,
Prefix
}

// Represents the tag attribute to specify `tag` for injection.
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Method)]
public class TypeAttribute : Attribute
Expand Down Expand Up @@ -107,7 +115,7 @@ public class Logger : ILogger
private readonly IConsole _console;
private IClock _clock;

public Logger([Tag("MyConsole")] IConsole console) => _console = console;
public Logger([Tag(Tags.MyConsole)] IConsole console) => _console = console;

// Method injection
[Order(1)]
Expand All @@ -123,7 +131,7 @@ public bool Initialize(
}

// Property injection
public string Prefix { get; [Order(2), Tag("Prefix")] set; }
public string Prefix { get; [Order(2), Tag(Tags.Prefix)] set; }

// Adds current time and prefix before a message and writes it to console
public void Log(string message) => _console?.WriteLine($"{_clock.Now} - {Prefix}: {message}");
Expand Down
2 changes: 1 addition & 1 deletion IoC/Core/Method.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ public void SetDependency(int parameterPosition, Type dependencyType, object dep
injectMethod,
ExpressionBuilderExtensions.ContainerExpression,
Expression.Constant(dependencyType),
Expression.Constant(dependencyTag),
Expression.Constant(dependencyTag).Convert(typeof(object)),
Expression.NewArrayInit(typeof(object), args.Select(Expression.Constant)))
.Convert(dependencyType);

Expand Down

0 comments on commit 711b0a3

Please sign in to comment.