Skip to content

Commit

Permalink
Update to 2.1.0 (The Abstactions Update)
Browse files Browse the repository at this point in the history
 - Refactor: Extracted Abstractions into a separate assembly.
 - Refactor: Reworked `RegisteredServiceAttribute` to `SingletonServiceAttribute`, and `TransientServiceAttribute`.
 - Refactor: Added overloads for annotated service extension methods.
  • Loading branch information
ApacheTech committed Jul 29, 2022
1 parent 13fa5ec commit e0da3b0
Show file tree
Hide file tree
Showing 28 changed files with 360 additions and 165 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,13 @@
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.ExceptionServices;
using ApacheTech.Common.DependencyInjection.Abstractions;
using ApacheTech.Common.DependencyInjection.Annotation;
using ApacheTech.Common.Extensions.Reflection;

// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable UnusedType.Global
// ReSharper disable UnusedMember.Global

namespace ApacheTech.Common.DependencyInjection
namespace ApacheTech.Common.DependencyInjection.Abstractions
{
/// <summary>
/// Helper code for the various activator services.
Expand Down Expand Up @@ -43,7 +41,7 @@ public static object CreateInstance(IServiceProvider provider, Type instanceType
{
var matcher = new ConstructorMatcher(constructor);

var isPreferred = constructor.HasCustomAttribute<ServiceProviderConstructorAttribute>();
var isPreferred = constructor.HasCustomAttribute<InjectableConstructorAttribute>();
var length = matcher.Match(parameters);

if (isPreferred)
Expand Down Expand Up @@ -256,7 +254,7 @@ private static bool TryFindPreferredConstructor(
continue;
}

if (!constructor.HasCustomAttribute<ServiceProviderConstructorAttribute>()) continue;
if (!constructor.HasCustomAttribute<InjectableConstructorAttribute>()) continue;
if (seenPreferred)
{
ThrowMultipleConstructorsMarkedWithAttributeException();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using System;
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global

namespace ApacheTech.Common.DependencyInjection.Abstractions.Annotation
{
/// <summary>
/// Denotes that this class should be registered as a service within the IOC container.
/// </summary>
/// <seealso cref="System.Attribute" />
[AttributeUsage(AttributeTargets.Class)]
public abstract class AnnotatedServiceAttribute : Attribute
{
private readonly ServiceLifetime _serviceLifetime;
private readonly Type _serviceType;

/// <summary>
/// Initialises a new instance of the <see cref="AnnotatedServiceAttribute"/> class.
/// </summary>
/// <param name="serviceLifetime">The service lifetime.</param>
protected AnnotatedServiceAttribute(ServiceLifetime serviceLifetime)
{
_serviceLifetime = serviceLifetime;
}

/// <summary>
/// Initialises a new instance of the <see cref="SingletonServiceAttribute"/> class.
/// </summary>
/// <param name="serviceLifetime">The service lifetime.</param>
/// <param name="serviceType">The type of the representation of the registered class within the IOC Container.</param>
protected AnnotatedServiceAttribute(ServiceLifetime serviceLifetime, Type serviceType) : this(serviceLifetime)
{
_serviceType = serviceType;
}

/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified <paramref name="implementationType"/>.
/// </summary>
/// <param name="implementationType">The type of the implementation.</param>
public ServiceDescriptor Describe(Type implementationType)
{
return ServiceDescriptor.Describe(_serviceType ?? implementationType, implementationType, _serviceLifetime);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using System;
using System.Linq;
using System.Reflection;
using ApacheTech.Common.DependencyInjection.Abstractions.Extensions;
using ApacheTech.Common.Extensions.Reflection;

// ReSharper disable UnusedType.Global

namespace ApacheTech.Common.DependencyInjection.Abstractions.Annotation
{
/// <summary>
/// Extensions methods to aid in the addition of annotated services.
/// </summary>
public static class AnnotationExtensions
{
/// <summary>
/// Registers classes, annotate with <see cref="SingletonServiceAttribute"/>,
/// or <see cref="TransientServiceAttribute"/> within the given assembly.
/// </summary>
/// <param name="services">The service collection to add the services to.</param>
/// <param name="assembly">The assembly to scan for annotated service classes.</param>
public static void AddAnnotatedServicesFromAssembly(this IServiceCollection services, Assembly assembly)
{
var descriptors = assembly
.GetTypesWithAttribute<AnnotatedServiceAttribute>()
.Select(x => x.Attribute.Describe(x.Type));

services.Add(descriptors);
}

/// <summary>
/// Registers classes, annotate with <see cref="SingletonServiceAttribute"/>,
/// or <see cref="TransientServiceAttribute"/> within the given assembly.
/// </summary>
/// <param name="services">The service collection to add the services to.</param>
public static void AddAnnotatedServicesFromAssembly(this IServiceCollection services)
{
var descriptors = Assembly
.GetCallingAssembly()
.GetTypesWithAttribute<AnnotatedServiceAttribute>()
.Select(x => x.Attribute.Describe(x.Type));

services.Add(descriptors);
}

/// <summary>
/// Registers classes, annotate with <see cref="SingletonServiceAttribute"/>,
/// or <see cref="TransientServiceAttribute"/> within the given assembly.
/// </summary>
/// <param name="services">The service collection to add the services to.</param>
/// <param name="assemblyMarkers">The assemblies to scan for annotated service classes.</param>
public static void AddAnnotatedServicesFromAssembly(this IServiceCollection services, params Type[] assemblyMarkers)
{
var descriptors = assemblyMarkers
.Select(marker => marker.Assembly)
.SelectMany(assembly => assembly
.GetTypesWithAttribute<AnnotatedServiceAttribute>()
.Select(t => t.Attribute.Describe(t.Type)));

services.Add(descriptors);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System;

// ReSharper disable UnusedType.Global
// ReSharper disable ClassNeverInstantiated.Global
// ReSharper disable UnusedMember.Global

namespace ApacheTech.Common.DependencyInjection.Abstractions.Annotation
{
/// <summary>
/// Denotes that this class should be registered as a singleton service within the IOC container.
/// </summary>
/// <seealso cref="Attribute" />
[AttributeUsage(AttributeTargets.Class)]
public class SingletonServiceAttribute : AnnotatedServiceAttribute
{
/// <summary>
/// Initialises a new instance of the <see cref="SingletonServiceAttribute"/> class.
/// </summary>
public SingletonServiceAttribute()
: base(ServiceLifetime.Singleton)
{
}

/// <summary>
/// Initialises a new instance of the <see cref="SingletonServiceAttribute"/> class.
/// </summary>
/// <param name="serviceType">Type of the service.</param>
public SingletonServiceAttribute(Type serviceType)
: base(ServiceLifetime.Singleton, serviceType)
{
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System;

// ReSharper disable UnusedType.Global
// ReSharper disable ClassNeverInstantiated.Global
// ReSharper disable UnusedMember.Global

namespace ApacheTech.Common.DependencyInjection.Abstractions.Annotation
{
/// <summary>
/// Denotes that this class should be registered as a transient service within the IOC container.
/// </summary>
/// <seealso cref="Attribute" />
[AttributeUsage(AttributeTargets.Class)]
public class TransientServiceAttribute : AnnotatedServiceAttribute
{
/// <summary>
/// Initialises a new instance of the <see cref="TransientServiceAttribute"/> class.
/// </summary>
public TransientServiceAttribute()
: base(ServiceLifetime.Singleton)
{
}

/// <summary>
/// Initialises a new instance of the <see cref="TransientServiceAttribute"/> class.
/// </summary>
/// <param name="serviceType">Type of the service.</param>
public TransientServiceAttribute(Type serviceType)
: base(ServiceLifetime.Singleton, serviceType)
{
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<Version>2.1.0.0</Version>

<ApplicationIcon>__Icon.ico</ApplicationIcon>
<AssemblyName>ApacheTech.Common.DependencyInjection.Abstractions</AssemblyName>
<AssemblyVersion>$(Version)</AssemblyVersion>
<AssemblyFileVersion>$(Version)</AssemblyFileVersion>
<Authors>ApacheTech Solutions</Authors>

<Company>ApacheTech Solutions</Company>
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
<Copyright>Copyright © ApacheTech Solutons, 2022. All Rights Reserved.</Copyright>

<DebugSymbols>true</DebugSymbols>
<DebugType>embedded</DebugType>
<Deterministic>true</Deterministic>
<Description>A minimal implmentation of the `Microsoft.Extensions.DependencyInjection` package, including the `ActivatorUtilities` class.</Description>

<EmbedUntrackedSources>true</EmbedUntrackedSources>

<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<GenerateSerializationAssemblies>On</GenerateSerializationAssemblies>

<LangVersion>latest</LangVersion>

<PackageIcon>__PreviewImage.png</PackageIcon>
<PackageLicenseFile>LICENCE.md</PackageLicenseFile>
<PackageProjectUrl>https://apachetech.co.uk</PackageProjectUrl>
<PackageReleaseNotes>Initial Release</PackageReleaseNotes>
<PackageTags>C#</PackageTags>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PublishRepositoryUrl>true</PublishRepositoryUrl>

<RepositoryType>git</RepositoryType>
<RepositoryUrl>https://github.com/ApacheTechSolutions/ApacheTech.Common.DependencyInjection</RepositoryUrl>
<RootNamespace>ApacheTech.Common.DependencyInjection.Abstractions</RootNamespace>

<TargetFramework>netstandard2.0</TargetFramework>
<Title>Minimal Dependency Injection Engine</Title>
<AllowUnsafeBlocks>False</AllowUnsafeBlocks>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
</PropertyGroup>

<ItemGroup>
<None Include="README.md">
<Pack>True</Pack>
<PackagePath></PackagePath>
</None>
<None Include="LICENCE.md">
<Pack>True</Pack>
<PackagePath></PackagePath>
</None>
<None Include="__PreviewImage.png">
<Pack>True</Pack>
<PackagePath></PackagePath>
</None>
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<PackageReference Include="ApacheTech.Common.Extensions" Version="1.2.0" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;

// ReSharper disable UnusedType.Global
// ReSharper disable UnusedMember.Global

namespace ApacheTech.Common.DependencyInjection.Abstractions.Extensions
{
/// <summary>
/// Provides extension methods for hosting services.
/// </summary>
public static class CollectionExtensions
{
/// <summary>
/// Attempts to add an element, with the provided key and value, to the <see cref="IDictionary{TKey,TValue}"/>.
/// </summary>
/// <typeparam name="TKey">The type of the key.</typeparam>
/// <typeparam name="TValue">The type of the value.</typeparam>
/// <param name="dictionary">The dictionary.</param>
/// <param name="key">The key.</param>
/// <param name="value">The value.</param>
/// <returns><c>true</c>, if the element was successfully added to the collection; otherwise, <c>false</c>.</returns>
/// <exception cref="ArgumentNullException">dictionary</exception>
public static bool TryAdd<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, TValue value)
{
if (dictionary is null) throw new ArgumentNullException(nameof(dictionary));
if (dictionary.ContainsKey(key)) return false;
dictionary.Add(key, value);
return true;

}

/// <summary>
/// Attempts to add an element, with the provided key and value, to the <see cref="IDictionary{TKey,TValue}"/>.
/// </summary>
/// <typeparam name="TValue">The type of the value.</typeparam>
/// <param name="collection">The collection.</param>
/// <param name="value">The value.</param>
/// <returns><c>true</c>, if the element was successfully added to the collection; otherwise, <c>false</c>.</returns>
/// <exception cref="ArgumentNullException">dictionary</exception>
public static bool TryAdd<TValue>(this IList<TValue> collection, TValue value)
{
if (collection is null) throw new ArgumentNullException(nameof(collection));
if (collection.Contains(value)) return false;
collection.Add(value);
return true;

}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System;
using System.Runtime.CompilerServices;

namespace ApacheTech.Common.DependencyInjection.Extensions
namespace ApacheTech.Common.DependencyInjection.Abstractions.Extensions
{
/// <summary>
/// Extension methods for objects.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using ApacheTech.Common.DependencyInjection.Abstractions;

// ReSharper disable PossibleMultipleEnumeration
// ReSharper disable UnusedMethodReturnValue.Global
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable UnusedMember.Global

namespace ApacheTech.Common.DependencyInjection.Extensions
namespace ApacheTech.Common.DependencyInjection.Abstractions.Extensions
{
/// <summary>
/// Extension methods for adding and removing services to an <see cref="IServiceCollection" />.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
using System;
using ApacheTech.Common.DependencyInjection.Abstractions;

namespace ApacheTech.Common.DependencyInjection.Extensions
// ReSharper disable UnusedType.Global
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable UnusedMember.Global

namespace ApacheTech.Common.DependencyInjection.Abstractions.Extensions
{
/// <summary>
/// Extension methods for adding services to an <see cref="IServiceCollection" />.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
using System;

// ReSharper disable UnusedType.Global
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable UnusedMemberInSuper.Global
// ReSharper disable UnusedMember.Global

namespace ApacheTech.Common.DependencyInjection.Extensions
namespace ApacheTech.Common.DependencyInjection.Abstractions.Extensions
{
/// <summary>
/// Defines a mechanism for retrieving a service object; that is, an object that provides custom support to other objects.
Expand Down
Loading

0 comments on commit e0da3b0

Please sign in to comment.