Fixing the unity container

This commit is contained in:
Zoe Roux 2021-05-01 02:30:57 +02:00
parent c0f1f80a40
commit 36220613ab
12 changed files with 179 additions and 64 deletions

View File

@ -32,8 +32,7 @@ namespace Kyoo.Controllers
/// A list of services that are provided by this service. This allow other plugins to declare dependencies.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
Type[] Provides { get; }

View File

@ -40,7 +40,7 @@ namespace Kyoo.Controllers
/// <summary>
/// Create a new <see cref="LibraryManager"/> instancce with every repository available.
/// Create a new <see cref="LibraryManager"/> instance with every repository available.
/// </summary>
/// <param name="repositories">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.</param>
@ -66,7 +66,7 @@ namespace Kyoo.Controllers
{
if (_repositories.FirstOrDefault(x => x.RepositoryType == typeof(T)) is IRepository<T> ret)
return ret;
throw new ItemNotFoundException();
throw new ItemNotFoundException($"No repository found for the type {typeof(T).Name}.");
}
/// <inheritdoc />

View File

@ -21,5 +21,46 @@ namespace Kyoo
container.RegisterType<ITask, T>();
return container;
}
/// <summary>
/// Register a new repository to the container.
/// </summary>
/// <param name="container">The container</param>
/// <typeparam name="T">The type of the repository.</typeparam>
/// <remarks>
/// If your repository implements a special interface, please use <see cref="RegisterRepository{T,T}"/>
/// </remarks>
/// <returns>The initial container.</returns>
public static IUnityContainer RegisterRepository<T>(this IUnityContainer container)
where T : IBaseRepository
{
Type repository = Utility.GetGenericDefinition(typeof(T), typeof(IRepository<>));
if (repository != null)
{
container.RegisterType(repository, typeof(T));
container.RegisterType<IBaseRepository, T>(repository.FriendlyName());
}
else
container.RegisterType<IBaseRepository, T>(typeof(T).FriendlyName());
return container;
}
/// <summary>
/// Register a new repository with a custom mapping to the container.
/// </summary>
/// <param name="container"></param>
/// <typeparam name="T">The custom mapping you have for your repository.</typeparam>
/// <typeparam name="T2">The type of the repository.</typeparam>
/// <remarks>
/// If your repository does not implements a special interface, please use <see cref="RegisterRepository{T}"/>
/// </remarks>
/// <returns>The initial container.</returns>
public static IUnityContainer RegisterRepository<T, T2>(this IUnityContainer container)
where T2 : IBaseRepository, T
{
container.RegisterType<T, T2>();
return container.RegisterRepository<T2>();
}
}
}

View File

@ -777,5 +777,19 @@ namespace Kyoo
return true;
return firstID == secondID;
}
/// <summary>
/// Get a friendly type name (supporting generics)
/// For example a list of string will be displayed as List&lt;string&gt; and not as List`1.
/// </summary>
/// <param name="type">The type to use</param>
/// <returns>The friendly name of the type</returns>
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}>";
}
}
}

View File

@ -83,25 +83,14 @@ namespace Kyoo
return Set<Link<T1, T2>>();
}
/// <summary>
/// A basic constructor that set default values (query tracker behaviors, mapping enums...)
/// Set basic configurations (like preventing query tracking)
/// </summary>
public DatabaseContext()
/// <param name="optionsBuilder">An option builder to fill.</param>
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
ChangeTracker.LazyLoadingEnabled = false;
}
/// <summary>
/// Create a new <see cref="DatabaseContext"/>.
/// </summary>
/// <param name="options">Connection options to use (witch database provider to use, connection strings...)</param>
public DatabaseContext(DbContextOptions<DatabaseContext> options)
: base(options)
{
ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
ChangeTracker.LazyLoadingEnabled = false;
base.OnConfiguring(optionsBuilder);
optionsBuilder.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
}
/// <summary>

View File

@ -51,6 +51,7 @@ namespace Kyoo.Postgresql
if (_debugMode)
optionsBuilder.EnableDetailedErrors()
.EnableSensitiveDataLogging();
base.OnConfiguring(optionsBuilder);
}
/// <summary>

View File

@ -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");
}

View File

@ -23,23 +23,23 @@ namespace Kyoo
/// <inheritdoc />
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)
};
/// <inheritdoc />
@ -62,17 +62,17 @@ namespace Kyoo
container.RegisterType<ILibraryManager, LibraryManager>(new HierarchicalLifetimeManager());
container.RegisterType<ILibraryRepository, LibraryRepository>(new HierarchicalLifetimeManager());
container.RegisterType<ILibraryItemRepository, LibraryItemRepository>(new HierarchicalLifetimeManager());
container.RegisterType<ICollectionRepository, CollectionRepository>(new HierarchicalLifetimeManager());
container.RegisterType<IShowRepository, ShowRepository>(new HierarchicalLifetimeManager());
container.RegisterType<ISeasonRepository, SeasonRepository>(new HierarchicalLifetimeManager());
container.RegisterType<IEpisodeRepository, EpisodeRepository>(new HierarchicalLifetimeManager());
container.RegisterType<ITrackRepository, TrackRepository>(new HierarchicalLifetimeManager());
container.RegisterType<IPeopleRepository, PeopleRepository>(new HierarchicalLifetimeManager());
container.RegisterType<IStudioRepository, StudioRepository>(new HierarchicalLifetimeManager());
container.RegisterType<IGenreRepository, GenreRepository>(new HierarchicalLifetimeManager());
container.RegisterType<IProviderRepository, ProviderRepository>(new HierarchicalLifetimeManager());
container.RegisterRepository<ILibraryRepository, LibraryRepository>();
container.RegisterRepository<ILibraryItemRepository, LibraryItemRepository>();
container.RegisterRepository<ICollectionRepository, CollectionRepository>();
container.RegisterRepository<IShowRepository, ShowRepository>();
container.RegisterRepository<ISeasonRepository, SeasonRepository>();
container.RegisterRepository<IEpisodeRepository, EpisodeRepository>();
container.RegisterRepository<ITrackRepository, TrackRepository>();
container.RegisterRepository<IPeopleRepository, PeopleRepository>();
container.RegisterRepository<IStudioRepository, StudioRepository>();
container.RegisterRepository<IGenreRepository, GenreRepository>();
container.RegisterRepository<IProviderRepository, ProviderRepository>();
container.RegisterTask<Crawler>();
}

View File

@ -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<Startup>(new LoggerFactory()).LogCritical(ex, "Unhandled exception");
await Console.Error.WriteLineAsync($"Unhandled exception: {ex}");
}
}
@ -80,7 +80,7 @@ namespace Kyoo
private static IWebHostBuilder CreateWebHostBuilder(string[] args)
{
UnityContainer container = new();
container.EnableDebugDiagnostic();
// container.EnableDebugDiagnostic();
return new WebHostBuilder()
.UseContentRoot(AppDomain.CurrentDomain.BaseDirectory)
@ -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()

View File

@ -142,11 +142,18 @@ namespace Kyoo
{
AllowedOrigins = { new Uri(publicUrl).GetLeftPart(UriPartial.Authority) }
});
services.AddSingleton<ITaskManager, TaskManager>();
services.AddHostedService(x => x.GetService<ITaskManager>() 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<IHostedService>(c => c.Resolve<ITaskManager>(), new SingletonLifetimeManager());
}
public void Configure(IUnityContainer container, IApplicationBuilder app, IWebHostEnvironment env)
@ -213,12 +220,9 @@ namespace Kyoo
});
container.RegisterType<IPluginManager, PluginManager>(new SingletonLifetimeManager());
IPluginManager pluginManager = new PluginManager(container, _configuration, new Logger<PluginManager>(_loggerFactory));
// container.Resolve<IConfiguration>();
IPluginManager pluginManager = container.Resolve<IPluginManager>();
pluginManager.ReloadPlugins();
// TODO the reload should re inject components from the constructor.
// TODO fin a way to inject tasks without a IUnityContainer.
container.RegisterFactory<IHostedService>(c => c.Resolve<ITaskManager>(), new SingletonLifetimeManager());
}
}
}

View File

@ -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<IServiceProviderFactory<UnityContainer>>(factory));
services.Replace(ServiceDescriptor.Singleton<IServiceProviderFactory<IUnityContainer>>(factory));
services.Replace(ServiceDescriptor.Singleton<IServiceProviderFactory<IServiceCollection>>(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});
}
}
}

View File

@ -0,0 +1,30 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using Unity;
using Unity.Microsoft.DependencyInjection;
namespace Kyoo.UnityExtensions
{
public class UnityProvider : ServiceProviderFactory, IServiceProviderFactory<UnityContainer>
{
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);
}
}
}