ServiceCollectionExtensions.cs 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Reflection;
  4. using Jellyfin.Database.Implementations;
  5. using Jellyfin.Database.Implementations.DbConfiguration;
  6. using Jellyfin.Database.Implementations.Locking;
  7. using Jellyfin.Database.Providers.Sqlite;
  8. using MediaBrowser.Common.Configuration;
  9. using MediaBrowser.Controller.Configuration;
  10. using Microsoft.EntityFrameworkCore;
  11. using Microsoft.Extensions.Configuration;
  12. using Microsoft.Extensions.DependencyInjection;
  13. using JellyfinDbProviderFactory = System.Func<System.IServiceProvider, Jellyfin.Database.Implementations.IJellyfinDatabaseProvider>;
  14. namespace Jellyfin.Server.Implementations.Extensions;
  15. /// <summary>
  16. /// Extensions for the <see cref="IServiceCollection"/> interface.
  17. /// </summary>
  18. public static class ServiceCollectionExtensions
  19. {
  20. private static IEnumerable<Type> DatabaseProviderTypes()
  21. {
  22. yield return typeof(SqliteDatabaseProvider);
  23. }
  24. private static IDictionary<string, JellyfinDbProviderFactory> GetSupportedDbProviders()
  25. {
  26. var items = new Dictionary<string, JellyfinDbProviderFactory>(StringComparer.InvariantCultureIgnoreCase);
  27. foreach (var providerType in DatabaseProviderTypes())
  28. {
  29. var keyAttribute = providerType.GetCustomAttribute<JellyfinDatabaseProviderKeyAttribute>();
  30. if (keyAttribute is null || string.IsNullOrWhiteSpace(keyAttribute.DatabaseProviderKey))
  31. {
  32. continue;
  33. }
  34. var provider = providerType;
  35. items[keyAttribute.DatabaseProviderKey] = (services) => (IJellyfinDatabaseProvider)ActivatorUtilities.CreateInstance(services, providerType);
  36. }
  37. return items;
  38. }
  39. /// <summary>
  40. /// Adds the <see cref="IDbContextFactory{TContext}"/> interface to the service collection with second level caching enabled.
  41. /// </summary>
  42. /// <param name="serviceCollection">An instance of the <see cref="IServiceCollection"/> interface.</param>
  43. /// <param name="configurationManager">The server configuration manager.</param>
  44. /// <param name="configuration">The startup Configuration.</param>
  45. /// <returns>The updated service collection.</returns>
  46. public static IServiceCollection AddJellyfinDbContext(
  47. this IServiceCollection serviceCollection,
  48. IServerConfigurationManager configurationManager,
  49. IConfiguration configuration)
  50. {
  51. var efCoreConfiguration = configurationManager.GetConfiguration<DatabaseConfigurationOptions>("database");
  52. var providers = GetSupportedDbProviders();
  53. JellyfinDbProviderFactory? providerFactory = null;
  54. if (efCoreConfiguration?.DatabaseType is null)
  55. {
  56. var cmdMigrationArgument = configuration.GetValue<string>("migration-provider");
  57. if (!string.IsNullOrWhiteSpace(cmdMigrationArgument))
  58. {
  59. efCoreConfiguration = new DatabaseConfigurationOptions()
  60. {
  61. DatabaseType = cmdMigrationArgument,
  62. };
  63. }
  64. else
  65. {
  66. // when nothing is setup via new Database configuration, fallback to SQLite with default settings.
  67. efCoreConfiguration = new DatabaseConfigurationOptions()
  68. {
  69. DatabaseType = "Jellyfin-SQLite",
  70. LockingBehavior = DatabaseLockingBehaviorTypes.NoLock
  71. };
  72. configurationManager.SaveConfiguration("database", efCoreConfiguration);
  73. }
  74. }
  75. if (!providers.TryGetValue(efCoreConfiguration.DatabaseType.ToUpperInvariant(), out providerFactory!))
  76. {
  77. throw new InvalidOperationException($"Jellyfin cannot find the database provider of type '{efCoreConfiguration.DatabaseType}'. Supported types are {string.Join(", ", providers.Keys)}");
  78. }
  79. serviceCollection.AddSingleton<IJellyfinDatabaseProvider>(providerFactory!);
  80. switch (efCoreConfiguration.LockingBehavior)
  81. {
  82. case DatabaseLockingBehaviorTypes.NoLock:
  83. serviceCollection.AddSingleton<IEntityFrameworkCoreLockingBehavior, NoLockBehavior>();
  84. break;
  85. case DatabaseLockingBehaviorTypes.Pessimistic:
  86. serviceCollection.AddSingleton<IEntityFrameworkCoreLockingBehavior, PessimisticLockBehavior>();
  87. break;
  88. case DatabaseLockingBehaviorTypes.Optimistic:
  89. serviceCollection.AddSingleton<IEntityFrameworkCoreLockingBehavior, OptimisticLockBehavior>();
  90. break;
  91. }
  92. serviceCollection.AddPooledDbContextFactory<JellyfinDbContext>((serviceProvider, opt) =>
  93. {
  94. var provider = serviceProvider.GetRequiredService<IJellyfinDatabaseProvider>();
  95. provider.Initialise(opt);
  96. var lockingBehavior = serviceProvider.GetRequiredService<IEntityFrameworkCoreLockingBehavior>();
  97. lockingBehavior.Initialise(opt);
  98. });
  99. return serviceCollection;
  100. }
  101. }