From c9124cd25e78527e2fa34714c5a4d3d54f39dbc0 Mon Sep 17 00:00:00 2001 From: Maksym Pavlenko Date: Thu, 5 Jan 2017 17:25:17 -0800 Subject: [PATCH] Detect RSS builders automatically via reflection --- .../Services/Builder/CompositeRssBuilder.cs | 24 +++++---- src/Podsync/Services/Builder/IRssBuilder.cs | 6 +++ src/Shared/ServiceProviderExtensions.cs | 50 +++++++++++++++++++ 3 files changed, 69 insertions(+), 11 deletions(-) diff --git a/src/Podsync/Services/Builder/CompositeRssBuilder.cs b/src/Podsync/Services/Builder/CompositeRssBuilder.cs index 592d5d9..fea6c53 100644 --- a/src/Podsync/Services/Builder/CompositeRssBuilder.cs +++ b/src/Podsync/Services/Builder/CompositeRssBuilder.cs @@ -1,4 +1,8 @@ using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Reflection; using System.Threading.Tasks; using Podsync.Services.Feed; using Podsync.Services.Links; @@ -10,13 +14,15 @@ namespace Podsync.Services.Builder // ReSharper disable once ClassNeverInstantiated.Global public class CompositeRssBuilder : RssBuilderBase { - private readonly YouTubeRssBuilder _youTubeBuilder; - private readonly VimeoRssBuilder _vimeoBuilder; + private readonly IDictionary _builders; public CompositeRssBuilder(IServiceProvider serviceProvider, IStorageService storageService) : base(storageService) { - _youTubeBuilder = serviceProvider.CreateInstance(); - _vimeoBuilder = serviceProvider.CreateInstance(); + // Find all RSS builders (all implementations of IRssBuilder), create instances and make dictionary for fast search by Provider type + var buildTypes = serviceProvider.FindAllImplementationsOf(Assembly.GetEntryAssembly()).Where(x => x != typeof(CompositeRssBuilder)); + var builders = buildTypes.Select(builderType => (IRssBuilder)serviceProvider.CreateInstance(builderType)).ToDictionary(builder => builder.Provider); + + _builders = new ReadOnlyDictionary(builders); } public override Provider Provider @@ -26,14 +32,10 @@ namespace Podsync.Services.Builder public override Task Query(Uri baseUrl, string feedId, FeedMetadata feed) { - if (feed.Provider == Provider.YouTube) + IRssBuilder builder; + if (_builders.TryGetValue(feed.Provider, out builder)) { - return _youTubeBuilder.Query(baseUrl, feedId, feed); - } - - if (feed.Provider == Provider.Vimeo) - { - return _vimeoBuilder.Query(baseUrl, feedId, feed); + return builder.Query(baseUrl, feedId, feed); } throw new NotSupportedException("Not supported provider"); diff --git a/src/Podsync/Services/Builder/IRssBuilder.cs b/src/Podsync/Services/Builder/IRssBuilder.cs index deb6004..e6f2586 100644 --- a/src/Podsync/Services/Builder/IRssBuilder.cs +++ b/src/Podsync/Services/Builder/IRssBuilder.cs @@ -1,11 +1,17 @@ using System; using System.Threading.Tasks; using Podsync.Services.Feed; +using Podsync.Services.Links; +using Podsync.Services.Storage; namespace Podsync.Services.Builder { public interface IRssBuilder { + Provider Provider { get; } + Task Query(Uri baseUrl, string feedId); + + Task Query(Uri baseUrl, string feedId, FeedMetadata metadata); } } \ No newline at end of file diff --git a/src/Shared/ServiceProviderExtensions.cs b/src/Shared/ServiceProviderExtensions.cs index 5eae046..82a9182 100644 --- a/src/Shared/ServiceProviderExtensions.cs +++ b/src/Shared/ServiceProviderExtensions.cs @@ -1,4 +1,7 @@ using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; using Microsoft.Extensions.DependencyInjection; namespace Shared @@ -19,5 +22,52 @@ namespace Shared { return ActivatorUtilities.CreateInstance(serviceProvider, type); } + + public static IEnumerable FindAllImplementationsOf(this IServiceProvider serviceProvider, Type interfaceType, Assembly assembly) + { + if (!interfaceType.GetTypeInfo().IsInterface) + { + throw new ArgumentException("T should be an interface"); + } + + return GetLoadableTypes(assembly).Where(type => IsAssignableFrom(interfaceType, type)); + } + + public static IEnumerable FindAllImplementationsOf(this IServiceProvider serviceProvider, Assembly assembly) + { + return serviceProvider.FindAllImplementationsOf(typeof(T), assembly); + } + + private static bool IsAssignableFrom(Type interfaceType, Type serviceType) + { + var serviceTypeInfo = serviceType.GetTypeInfo(); + if (serviceTypeInfo.IsInterface || serviceTypeInfo.IsAbstract) + { + return false; + } + + var interfaceTypeInfo = interfaceType.GetTypeInfo(); + if (!interfaceTypeInfo.IsGenericType) + { + return interfaceType.IsAssignableFrom(serviceType); + } + + return serviceType + .GetInterfaces() + .Where(type => type.GetTypeInfo().IsGenericType) + .Any(type => type.GetGenericTypeDefinition() == interfaceType); + } + + private static IEnumerable GetLoadableTypes(Assembly assembly) + { + try + { + return assembly.GetTypes(); + } + catch (ReflectionTypeLoadException e) + { + return e.Types.Where(x => x != null); + } + } } } \ No newline at end of file