From 8545c361f7c53f250a9b82bfee1cb990238ff793 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Sat, 17 Dec 2016 23:53:47 +0300 Subject: [PATCH] Refactored ServiceCollectionRegistrationExtensions, added TryAdd methods. --- .../Volo/Abp/Uow/UnitOfWorkManager.cs | 2 +- ...ServiceCollectionRegistrationExtensions.cs | 121 ++++++++++++------ .../AutoRegistrationHelper.cs | 52 ++++++++ .../AutoRegistrationHelper_Tests.cs | 55 ++++++++ 4 files changed, 188 insertions(+), 42 deletions(-) create mode 100644 src/Volo.DependencyInjection/Volo/DependencyInjection/AutoRegistrationHelper.cs create mode 100644 test/Volo.DependencyInjection.Tests/Volo/DependencyInjection/AutoRegistrationHelper_Tests.cs diff --git a/src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkManager.cs b/src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkManager.cs index 82d6755512..094720f7fe 100644 --- a/src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkManager.cs +++ b/src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkManager.cs @@ -36,7 +36,7 @@ namespace Volo.Abp.Uow scope ); } - catch + catch(Exception ex) { scope.Dispose(); throw; diff --git a/src/Volo.DependencyInjection/Microsoft/Extensions/DependencyInjection/ServiceCollectionRegistrationExtensions.cs b/src/Volo.DependencyInjection/Microsoft/Extensions/DependencyInjection/ServiceCollectionRegistrationExtensions.cs index 5b340f78bb..291ee9a9d6 100644 --- a/src/Volo.DependencyInjection/Microsoft/Extensions/DependencyInjection/ServiceCollectionRegistrationExtensions.cs +++ b/src/Volo.DependencyInjection/Microsoft/Extensions/DependencyInjection/ServiceCollectionRegistrationExtensions.cs @@ -1,9 +1,9 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Reflection; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection.Extensions; using Volo.DependencyInjection; -using Volo.ExtensionMethods; using Volo.Internal; namespace Microsoft.Extensions.DependencyInjection @@ -12,83 +12,122 @@ namespace Microsoft.Extensions.DependencyInjection { //TODO: Check if assembly/type is added before or add TryAdd versions of them? //TODO: When to use Add, when to use TryAdd? We may think to add conventional interfaces to indicate and attributes to override them. + //TODO: Make this code extensible, so we can add other conventions! public static IServiceCollection AddAssemblyOf(this IServiceCollection services) { - return services.AddAssembly(typeof(T).GetTypeInfo().Assembly); + return services.AddAssemblyOf(false); + } + + public static IServiceCollection TryAddAssemblyOf(this IServiceCollection services) + { + return services.AddAssemblyOf(true); } public static IServiceCollection AddAssembly(this IServiceCollection services, Assembly assembly) { - var types = AssemblyHelper.GetAllTypes(assembly).Where(t => - { - var typeInfo = t.GetTypeInfo(); - return typeInfo.IsClass && !typeInfo.IsAbstract && !typeInfo.IsGenericType && !typeInfo.IsDefined(typeof(SkipAutoRegistrationAttribute)); - }); + return services.AddAssembly(assembly, false); + } - return services.AddTypes(types.ToArray()); + public static IServiceCollection TryAddAssembly(this IServiceCollection services, Assembly assembly) + { + return services.AddAssembly(assembly, true); } public static IServiceCollection AddTypes(this IServiceCollection services, params Type[] types) + { + return services.AddTypes(false, types); + } + + public static IServiceCollection TryAddTypes(this IServiceCollection services, params Type[] types) + { + return services.AddTypes(true, types); + } + + public static IServiceCollection AddType(this IServiceCollection services, Type type) + { + return services.AddType(type, false); + } + + public static IServiceCollection TryAddType(this IServiceCollection services, Type type) + { + return services.AddType(type, true); + } + + internal static IServiceCollection AddAssemblyOf(this IServiceCollection services, bool tryAdd) + { + return services.AddAssembly(typeof(T).GetTypeInfo().Assembly, tryAdd); + } + + internal static IServiceCollection AddAssembly(this IServiceCollection services, Assembly assembly, bool tryAdd) + { + var types = AssemblyHelper + .GetAllTypes(assembly) + .Where(t => + { + var typeInfo = t.GetTypeInfo(); + return typeInfo.IsClass && + !typeInfo.IsAbstract && + !typeInfo.IsGenericType && + !typeInfo.IsDefined(typeof(SkipAutoRegistrationAttribute)); + }); + + return services.AddTypes(tryAdd, types.ToArray()); + } + + internal static IServiceCollection AddTypes(this IServiceCollection services, bool tryAdd, params Type[] types) { foreach (var type in types) { - services.AddType(type); + services.AddType(type, tryAdd); } return services; } - public static IServiceCollection AddType(this IServiceCollection services, Type type) + internal static IServiceCollection AddType(this IServiceCollection services, Type type, bool tryAdd) { - //TODO: Make this code extensible, so we can add other conventions! - - foreach (var serviceType in FindServiceTypes(type)) + var lifeTime = GetServiceLifeTime(type); + if (lifeTime == null) { - if (typeof(ITransientDependency).GetTypeInfo().IsAssignableFrom(type)) - { - services.AddTransient(serviceType, type); - } - - if (typeof(ISingletonDependency).GetTypeInfo().IsAssignableFrom(type)) + return services; + } + + foreach (var serviceType in AutoRegistrationHelper.GetExposedServices(type)) + { + var serviceDescriptor = ServiceDescriptor.Describe(serviceType, type, lifeTime.Value); + if (tryAdd) { - services.AddSingleton(serviceType, type); + services.Add(serviceDescriptor); } - - if (typeof(IScopedDependency).GetTypeInfo().IsAssignableFrom(type)) + else { - services.AddScoped(serviceType, type); + services.TryAdd(serviceDescriptor); } } return services; } - private static IEnumerable FindServiceTypes(Type type) + [CanBeNull] + internal static ServiceLifetime? GetServiceLifeTime(Type type) { - var customExposedServices = type.GetTypeInfo().GetCustomAttributes().OfType().SelectMany(p => p.GetExposedServiceTypes()).ToList(); - if (customExposedServices.Any()) + if (typeof(ITransientDependency).GetTypeInfo().IsAssignableFrom(type)) { - return customExposedServices; + return ServiceLifetime.Transient; } - var serviceTypes = new List { type }; - - foreach (var interfaceType in type.GetTypeInfo().GetInterfaces()) + if (typeof(ISingletonDependency).GetTypeInfo().IsAssignableFrom(type)) { - var interfaceName = interfaceType.Name; - if (interfaceName.StartsWith("I")) - { - interfaceName = interfaceName.Right(interfaceName.Length - 1); - } + return ServiceLifetime.Singleton; + } - if (type.Name.EndsWith(interfaceName)) - { - serviceTypes.Add(interfaceType); - } + if (typeof(IScopedDependency).GetTypeInfo().IsAssignableFrom(type)) + { + return ServiceLifetime.Scoped; } - return serviceTypes; + return null; } } } diff --git a/src/Volo.DependencyInjection/Volo/DependencyInjection/AutoRegistrationHelper.cs b/src/Volo.DependencyInjection/Volo/DependencyInjection/AutoRegistrationHelper.cs new file mode 100644 index 0000000000..54a5bf01d7 --- /dev/null +++ b/src/Volo.DependencyInjection/Volo/DependencyInjection/AutoRegistrationHelper.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Volo.ExtensionMethods; + +namespace Volo.DependencyInjection +{ + public static class AutoRegistrationHelper + { + public static IEnumerable GetExposedServices(Type type) + { + var typeInfo = type.GetTypeInfo(); + + var customExposedServices = typeInfo + .GetCustomAttributes() + .OfType() + .SelectMany(p => p.GetExposedServiceTypes()) + .ToList(); + + if (customExposedServices.Any()) + { + return customExposedServices; + } + + return GetDefaultExposedServices(type); + } + + private static IEnumerable GetDefaultExposedServices(Type type) + { + var typeInfo = type.GetTypeInfo(); + + var serviceTypes = new List { type }; + + foreach (var interfaceType in typeInfo.GetInterfaces()) + { + var interfaceName = interfaceType.Name; + if (interfaceName.StartsWith("I")) + { + interfaceName = interfaceName.Right(interfaceName.Length - 1); + } + + if (type.Name.EndsWith(interfaceName)) + { + serviceTypes.Add(interfaceType); + } + } + + return serviceTypes; + } + } +} diff --git a/test/Volo.DependencyInjection.Tests/Volo/DependencyInjection/AutoRegistrationHelper_Tests.cs b/test/Volo.DependencyInjection.Tests/Volo/DependencyInjection/AutoRegistrationHelper_Tests.cs new file mode 100644 index 0000000000..801d5733b0 --- /dev/null +++ b/test/Volo.DependencyInjection.Tests/Volo/DependencyInjection/AutoRegistrationHelper_Tests.cs @@ -0,0 +1,55 @@ +using System.Linq; +using Shouldly; +using Xunit; + +namespace Volo.DependencyInjection +{ + public class AutoRegistrationHelper_Tests + { + [Fact] + public void Should_Get_Conventional_Exposed_Types_By_Default() + { + //Act + + var exposedServices = AutoRegistrationHelper.GetExposedServices(typeof(DefaultDerivedService)).ToList(); + + //Assert + + exposedServices.Count.ShouldBe(3); + exposedServices.ShouldContain(typeof(DefaultDerivedService)); + exposedServices.ShouldContain(typeof(IService)); + exposedServices.ShouldContain(typeof(IDerivedService)); + } + + [Fact] + public void Should_Get_Custom_Exposed_Types_If_Available() + { + //Act + + var exposedServices = AutoRegistrationHelper.GetExposedServices(typeof(ExplicitDerivedService)).ToList(); + + //Assert + + exposedServices.Count.ShouldBe(1); + exposedServices.ShouldContain(typeof(IDerivedService)); + } + + public class DefaultDerivedService : IDerivedService + { + } + + public interface IService + { + } + + public interface IDerivedService : IService + { + } + + [ExposeServices(typeof(IDerivedService))] + public class ExplicitDerivedService : IDerivedService + { + + } + } +}