diff --git a/Kyoo.Common/Controllers/IPlugin.cs b/Kyoo.Common/Controllers/IPlugin.cs index 56797bb2..a2f9d1b0 100644 --- a/Kyoo.Common/Controllers/IPlugin.cs +++ b/Kyoo.Common/Controllers/IPlugin.cs @@ -32,8 +32,7 @@ namespace Kyoo.Controllers /// A list of services that are provided by this service. This allow other plugins to declare dependencies. /// /// - /// You should put directly the type that you will register in configure, Kyoo will detect by itself which - /// interfaces are implemented by your type. + /// You should put the type's interface that will be register in configure. /// Type[] Provides { get; } diff --git a/Kyoo.Common/Controllers/Implementations/LibraryManager.cs b/Kyoo.Common/Controllers/Implementations/LibraryManager.cs index dcf35813..ce34f267 100644 --- a/Kyoo.Common/Controllers/Implementations/LibraryManager.cs +++ b/Kyoo.Common/Controllers/Implementations/LibraryManager.cs @@ -40,7 +40,7 @@ namespace Kyoo.Controllers /// - /// Create a new instancce with every repository available. + /// Create a new instance with every repository available. /// /// The list of repositories that this library manager should manage. /// If a repository for every base type is not available, this instance won't be stable. @@ -66,7 +66,7 @@ namespace Kyoo.Controllers { if (_repositories.FirstOrDefault(x => x.RepositoryType == typeof(T)) is IRepository ret) return ret; - throw new ItemNotFoundException(); + throw new ItemNotFoundException($"No repository found for the type {typeof(T).Name}."); } /// diff --git a/Kyoo.Common/Module.cs b/Kyoo.Common/Module.cs index 639e7c2a..5ce34cbe 100644 --- a/Kyoo.Common/Module.cs +++ b/Kyoo.Common/Module.cs @@ -21,5 +21,46 @@ namespace Kyoo container.RegisterType(); return container; } + + /// + /// Register a new repository to the container. + /// + /// The container + /// The type of the repository. + /// + /// If your repository implements a special interface, please use + /// + /// The initial container. + public static IUnityContainer RegisterRepository(this IUnityContainer container) + where T : IBaseRepository + { + Type repository = Utility.GetGenericDefinition(typeof(T), typeof(IRepository<>)); + + if (repository != null) + { + container.RegisterType(repository, typeof(T)); + container.RegisterType(repository.FriendlyName()); + } + else + container.RegisterType(typeof(T).FriendlyName()); + return container; + } + + /// + /// Register a new repository with a custom mapping to the container. + /// + /// + /// The custom mapping you have for your repository. + /// The type of the repository. + /// + /// If your repository does not implements a special interface, please use + /// + /// The initial container. + public static IUnityContainer RegisterRepository(this IUnityContainer container) + where T2 : IBaseRepository, T + { + container.RegisterType(); + return container.RegisterRepository(); + } } } \ No newline at end of file diff --git a/Kyoo.Common/Utility.cs b/Kyoo.Common/Utility.cs index 3e4bba15..2469afbc 100644 --- a/Kyoo.Common/Utility.cs +++ b/Kyoo.Common/Utility.cs @@ -777,5 +777,19 @@ namespace Kyoo return true; return firstID == secondID; } + + /// + /// Get a friendly type name (supporting generics) + /// For example a list of string will be displayed as List<string> and not as List`1. + /// + /// The type to use + /// The friendly name of the type + public static string FriendlyName(this Type type) + { + if (!type.IsGenericType) + return type.Name; + string generics = string.Join(", ", type.GetGenericArguments().Select(x => x.FriendlyName())); + return $"{type.Name[..type.Name.IndexOf('`')]}<{generics}>"; + } } } \ No newline at end of file diff --git a/Kyoo.CommonAPI/DatabaseContext.cs b/Kyoo.CommonAPI/DatabaseContext.cs index caf8933e..997b7bd4 100644 --- a/Kyoo.CommonAPI/DatabaseContext.cs +++ b/Kyoo.CommonAPI/DatabaseContext.cs @@ -82,28 +82,17 @@ namespace Kyoo { return Set>(); } - - - /// - /// A basic constructor that set default values (query tracker behaviors, mapping enums...) - /// - public DatabaseContext() - { - ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; - ChangeTracker.LazyLoadingEnabled = false; - } /// - /// Create a new . + /// Set basic configurations (like preventing query tracking) /// - /// Connection options to use (witch database provider to use, connection strings...) - public DatabaseContext(DbContextOptions options) - : base(options) + /// An option builder to fill. + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { - ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; - ChangeTracker.LazyLoadingEnabled = false; + base.OnConfiguring(optionsBuilder); + optionsBuilder.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking); } - + /// /// Set database parameters to support every types of Kyoo. /// diff --git a/Kyoo.Postgresql/PostgresContext.cs b/Kyoo.Postgresql/PostgresContext.cs index 7ee49adb..826b88e5 100644 --- a/Kyoo.Postgresql/PostgresContext.cs +++ b/Kyoo.Postgresql/PostgresContext.cs @@ -51,6 +51,7 @@ namespace Kyoo.Postgresql if (_debugMode) optionsBuilder.EnableDetailedErrors() .EnableSensitiveDataLogging(); + base.OnConfiguring(optionsBuilder); } /// diff --git a/Kyoo/Controllers/TaskManager.cs b/Kyoo/Controllers/TaskManager.cs index 6f9c184b..89cff352 100644 --- a/Kyoo/Controllers/TaskManager.cs +++ b/Kyoo/Controllers/TaskManager.cs @@ -66,6 +66,11 @@ namespace Kyoo.Controllers _configuration = configuration.GetSection("scheduledTasks"); _logger = logger; _tasks = tasks.Select(x => (x, DateTime.Now + GetTaskDelay(x.Slug))).ToList(); + + if (_tasks.Any()) + _logger.LogTrace("Task manager initiated with: {Tasks}", _tasks.Select(x => x.task.Name)); + else + _logger.LogInformation("Task manager initiated without any tasks"); } diff --git a/Kyoo/CoreModule.cs b/Kyoo/CoreModule.cs index 5916f33f..a32689a5 100644 --- a/Kyoo/CoreModule.cs +++ b/Kyoo/CoreModule.cs @@ -23,23 +23,23 @@ namespace Kyoo /// public Type[] Provides => new[] { - typeof(FileManager), - typeof(Transcoder), - typeof(ThumbnailsManager), - typeof(ProviderManager), - typeof(TaskManager), - typeof(LibraryManager), - typeof(LibraryRepository), - typeof(LibraryItemRepository), - typeof(CollectionRepository), - typeof(ShowRepository), - typeof(SeasonRepository), - typeof(EpisodeRepository), - typeof(TrackRepository), - typeof(PeopleRepository), - typeof(StudioRepository), - typeof(GenreRepository), - typeof(ProviderRepository), + typeof(IFileManager), + typeof(ITranscoder), + typeof(IThumbnailsManager), + typeof(IProviderManager), + typeof(ITaskManager), + typeof(ILibraryManager), + typeof(ILibraryRepository), + typeof(ILibraryItemRepository), + typeof(ICollectionRepository), + typeof(IShowRepository), + typeof(ISeasonRepository), + typeof(IEpisodeRepository), + typeof(ITrackRepository), + typeof(IPeopleRepository), + typeof(IStudioRepository), + typeof(IGenreRepository), + typeof(IProviderRepository) }; /// @@ -62,17 +62,17 @@ namespace Kyoo container.RegisterType(new HierarchicalLifetimeManager()); - container.RegisterType(new HierarchicalLifetimeManager()); - container.RegisterType(new HierarchicalLifetimeManager()); - container.RegisterType(new HierarchicalLifetimeManager()); - container.RegisterType(new HierarchicalLifetimeManager()); - container.RegisterType(new HierarchicalLifetimeManager()); - container.RegisterType(new HierarchicalLifetimeManager()); - container.RegisterType(new HierarchicalLifetimeManager()); - container.RegisterType(new HierarchicalLifetimeManager()); - container.RegisterType(new HierarchicalLifetimeManager()); - container.RegisterType(new HierarchicalLifetimeManager()); - container.RegisterType(new HierarchicalLifetimeManager()); + container.RegisterRepository(); + container.RegisterRepository(); + container.RegisterRepository(); + container.RegisterRepository(); + container.RegisterRepository(); + container.RegisterRepository(); + container.RegisterRepository(); + container.RegisterRepository(); + container.RegisterRepository(); + container.RegisterRepository(); + container.RegisterRepository(); container.RegisterTask(); } diff --git a/Kyoo/Program.cs b/Kyoo/Program.cs index e5053735..4d749a0f 100644 --- a/Kyoo/Program.cs +++ b/Kyoo/Program.cs @@ -1,6 +1,7 @@ using System; using System.IO; using System.Threading.Tasks; +using Kyoo.UnityExtensions; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting.StaticWebAssets; using Microsoft.Extensions.Configuration; @@ -8,7 +9,6 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Unity; -using Unity.Microsoft.DependencyInjection; namespace Kyoo { @@ -46,16 +46,16 @@ namespace Kyoo #endif Console.WriteLine($"Running as {Environment.UserName}."); - IWebHostBuilder host = CreateWebHostBuilder(args); + IWebHostBuilder builder = CreateWebHostBuilder(args); if (debug != null) - host = host.UseEnvironment(debug == true ? "Development" : "Production"); + builder = builder.UseEnvironment(debug == true ? "Development" : "Production"); try { - await host.Build().RunAsync(); + await builder.Build().RunAsync(); } catch (Exception ex) { - new Logger(new LoggerFactory()).LogCritical(ex, "Unhandled exception"); + await Console.Error.WriteLineAsync($"Unhandled exception: {ex}"); } } @@ -71,7 +71,7 @@ namespace Kyoo .AddEnvironmentVariables() .AddCommandLine(args); } - + /// /// Create a a web host /// @@ -80,8 +80,8 @@ namespace Kyoo private static IWebHostBuilder CreateWebHostBuilder(string[] args) { UnityContainer container = new(); - container.EnableDebugDiagnostic(); - + // container.EnableDebugDiagnostic(); + return new WebHostBuilder() .UseContentRoot(AppDomain.CurrentDomain.BaseDirectory) .UseConfiguration(SetupConfig(new ConfigurationBuilder(), args).Build()) @@ -99,7 +99,7 @@ namespace Kyoo if (context.HostingEnvironment.IsDevelopment()) StaticWebAssetsLoader.UseStaticWebAssets(context.HostingEnvironment, context.Configuration); }) - .UseUnityServiceProvider(container) + .UseUnityProvider(container) .ConfigureServices(x => x.AddRouting()) .UseKestrel(options => { options.AddServerHeader = false; }) .UseIIS() diff --git a/Kyoo/Startup.cs b/Kyoo/Startup.cs index 0fc391d1..1bdf23b2 100644 --- a/Kyoo/Startup.cs +++ b/Kyoo/Startup.cs @@ -142,11 +142,18 @@ namespace Kyoo { AllowedOrigins = { new Uri(publicUrl).GetLeftPart(UriPartial.Authority) } }); + + services.AddSingleton(); + services.AddHostedService(x => x.GetService() as TaskManager); } - public void ConfigureContainer(IUnityContainer container) + public void ConfigureContainer(UnityContainer container) { // TODO move this to the configure section and figure out a way to reload ControllerActivators with the updated unity container + + // TODO the reload should re inject components from the constructor. + // TODO fin a way to inject tasks without a IUnityContainer. + // container.RegisterFactory(c => c.Resolve(), new SingletonLifetimeManager()); } public void Configure(IUnityContainer container, IApplicationBuilder app, IWebHostEnvironment env) @@ -213,12 +220,9 @@ namespace Kyoo }); container.RegisterType(new SingletonLifetimeManager()); - IPluginManager pluginManager = new PluginManager(container, _configuration, new Logger(_loggerFactory)); + // container.Resolve(); + IPluginManager pluginManager = container.Resolve(); pluginManager.ReloadPlugins(); - - // TODO the reload should re inject components from the constructor. - // TODO fin a way to inject tasks without a IUnityContainer. - container.RegisterFactory(c => c.Resolve(), new SingletonLifetimeManager()); } } } diff --git a/Kyoo/UnityExtensions/UnityExtensions.cs b/Kyoo/UnityExtensions/UnityExtensions.cs new file mode 100644 index 00000000..b351ec8e --- /dev/null +++ b/Kyoo/UnityExtensions/UnityExtensions.cs @@ -0,0 +1,32 @@ +using System.Reflection; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Unity; +using Unity.Microsoft.DependencyInjection; + +namespace Kyoo.UnityExtensions +{ + public static class UnityExtensions + { + public static IWebHostBuilder UseUnityProvider(this IWebHostBuilder host, UnityContainer container) + { + UnityProvider factory = new(container); + + return host.ConfigureServices((_, services) => + { + services.Replace(ServiceDescriptor.Singleton>(factory)); + services.Replace(ServiceDescriptor.Singleton>(factory)); + services.Replace(ServiceDescriptor.Singleton>(factory)); + }); + } + + public static IUnityContainer AddServices(this IUnityContainer container, IServiceCollection services) + { + return (IUnityContainer)typeof(ServiceProviderExtensions).Assembly + .GetType("Unity.Microsoft.DependencyInjection.Configuration") + !.GetMethod("AddServices", BindingFlags.Static | BindingFlags.NonPublic) + !.Invoke(null, new object[] {container, services}); + } + } +} \ No newline at end of file diff --git a/Kyoo/UnityExtensions/UnityProvider.cs b/Kyoo/UnityExtensions/UnityProvider.cs new file mode 100644 index 00000000..93d460f0 --- /dev/null +++ b/Kyoo/UnityExtensions/UnityProvider.cs @@ -0,0 +1,30 @@ +using System; +using Microsoft.Extensions.DependencyInjection; +using Unity; +using Unity.Microsoft.DependencyInjection; + +namespace Kyoo.UnityExtensions +{ + public class UnityProvider : ServiceProviderFactory, IServiceProviderFactory + { + private readonly UnityContainer _container; + + + public UnityProvider(UnityContainer container) + : base(container) + { + _container = container; + } + + public UnityContainer CreateBuilder(IServiceCollection services) + { + _container.AddServices(services); + return _container; + } + + public IServiceProvider CreateServiceProvider(UnityContainer containerBuilder) + { + return CreateServiceProvider(containerBuilder as IUnityContainer); + } + } +} \ No newline at end of file