< Summary - Jellyfin

Information
Class: Jellyfin.Server.Implementations.Extensions.ServiceCollectionExtensions
Assembly: Jellyfin.Server.Implementations
File(s): /srv/git/jellyfin/Jellyfin.Server.Implementations/Extensions/ServiceCollectionExtensions.cs
Line coverage
58%
Covered lines: 33
Uncovered lines: 23
Coverable lines: 56
Total lines: 149
Line coverage: 58.9%
Branch coverage
50%
Covered branches: 14
Total branches: 28
Branch coverage: 50%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
GetSupportedDbProviders()100%66100%
LoadDatabasePlugin(...)0%4260%
AddJellyfinDbContext(...)50%231670.27%

File(s)

/srv/git/jellyfin/Jellyfin.Server.Implementations/Extensions/ServiceCollectionExtensions.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.IO;
 4using System.Linq;
 5using System.Reflection;
 6using Jellyfin.Database.Implementations;
 7using Jellyfin.Database.Implementations.DbConfiguration;
 8using Jellyfin.Database.Implementations.Locking;
 9using Jellyfin.Database.Providers.Sqlite;
 10using MediaBrowser.Common.Configuration;
 11using MediaBrowser.Controller.Configuration;
 12using Microsoft.EntityFrameworkCore;
 13using Microsoft.Extensions.Configuration;
 14using Microsoft.Extensions.DependencyInjection;
 15using JellyfinDbProviderFactory = System.Func<System.IServiceProvider, Jellyfin.Database.Implementations.IJellyfinDataba
 16
 17namespace Jellyfin.Server.Implementations.Extensions;
 18
 19/// <summary>
 20/// Extensions for the <see cref="IServiceCollection"/> interface.
 21/// </summary>
 22public static class ServiceCollectionExtensions
 23{
 24    private static IEnumerable<Type> DatabaseProviderTypes()
 25    {
 26        yield return typeof(SqliteDatabaseProvider);
 27    }
 28
 29    private static IDictionary<string, JellyfinDbProviderFactory> GetSupportedDbProviders()
 30    {
 4231        var items = new Dictionary<string, JellyfinDbProviderFactory>(StringComparer.InvariantCultureIgnoreCase);
 16832        foreach (var providerType in DatabaseProviderTypes())
 33        {
 4234            var keyAttribute = providerType.GetCustomAttribute<JellyfinDatabaseProviderKeyAttribute>();
 4235            if (keyAttribute is null || string.IsNullOrWhiteSpace(keyAttribute.DatabaseProviderKey))
 36            {
 37                continue;
 38            }
 39
 4240            var provider = providerType;
 4241            items[keyAttribute.DatabaseProviderKey] = (services) => (IJellyfinDatabaseProvider)ActivatorUtilities.Create
 42        }
 43
 4244        return items;
 45    }
 46
 47    private static JellyfinDbProviderFactory? LoadDatabasePlugin(CustomDatabaseOptions customProviderOptions, IApplicati
 48    {
 049        var plugin = Directory.EnumerateDirectories(applicationPaths.PluginsPath)
 050            .Where(e => Path.GetFileName(e)!.StartsWith(customProviderOptions.PluginName, StringComparison.OrdinalIgnore
 051            .Order()
 052            .FirstOrDefault()
 053            ?? throw new InvalidOperationException($"The requested custom database plugin with the name '{customProvider
 54
 055        var dbProviderAssembly = Path.Combine(plugin, Path.ChangeExtension(customProviderOptions.PluginAssembly, "dll"))
 056        if (!File.Exists(dbProviderAssembly))
 57        {
 058            throw new InvalidOperationException($"Could not find the requested assembly at '{dbProviderAssembly}'");
 59        }
 60
 61        // we have to load the assembly without proxy to ensure maximum performance for this.
 062        var assembly = Assembly.LoadFrom(dbProviderAssembly);
 063        var dbProviderType = assembly.GetExportedTypes().FirstOrDefault(f => f.IsAssignableTo(typeof(IJellyfinDatabasePr
 064            ?? throw new InvalidOperationException($"Could not find any type implementing the '{nameof(IJellyfinDatabase
 65
 066        return (services) => (IJellyfinDatabaseProvider)ActivatorUtilities.CreateInstance(services, dbProviderType);
 67    }
 68
 69    /// <summary>
 70    /// Adds the <see cref="IDbContextFactory{TContext}"/> interface to the service collection with second level caching
 71    /// </summary>
 72    /// <param name="serviceCollection">An instance of the <see cref="IServiceCollection"/> interface.</param>
 73    /// <param name="configurationManager">The server configuration manager.</param>
 74    /// <param name="configuration">The startup Configuration.</param>
 75    /// <returns>The updated service collection.</returns>
 76    public static IServiceCollection AddJellyfinDbContext(
 77        this IServiceCollection serviceCollection,
 78        IServerConfigurationManager configurationManager,
 79        IConfiguration configuration)
 80    {
 4281        var efCoreConfiguration = configurationManager.GetConfiguration<DatabaseConfigurationOptions>("database");
 4282        JellyfinDbProviderFactory? providerFactory = null;
 83
 4284        if (efCoreConfiguration?.DatabaseType is null)
 85        {
 2186            var cmdMigrationArgument = configuration.GetValue<string>("migration-provider");
 2187            if (!string.IsNullOrWhiteSpace(cmdMigrationArgument))
 88            {
 089                efCoreConfiguration = new DatabaseConfigurationOptions()
 090                {
 091                    DatabaseType = cmdMigrationArgument,
 092                };
 93            }
 94            else
 95            {
 96                // when nothing is setup via new Database configuration, fallback to SQLite with default settings.
 2197                efCoreConfiguration = new DatabaseConfigurationOptions()
 2198                {
 2199                    DatabaseType = "Jellyfin-SQLite",
 21100                    LockingBehavior = DatabaseLockingBehaviorTypes.NoLock
 21101                };
 21102                configurationManager.SaveConfiguration("database", efCoreConfiguration);
 103            }
 104        }
 105
 42106        if (efCoreConfiguration.DatabaseType.Equals("PLUGIN_PROVIDER", StringComparison.OrdinalIgnoreCase))
 107        {
 0108            if (efCoreConfiguration.CustomProviderOptions is null)
 109            {
 0110                throw new InvalidOperationException("The custom database provider must declare the custom provider optio
 111            }
 112
 0113            providerFactory = LoadDatabasePlugin(efCoreConfiguration.CustomProviderOptions, configurationManager.Applica
 114        }
 115        else
 116        {
 42117            var providers = GetSupportedDbProviders();
 42118            if (!providers.TryGetValue(efCoreConfiguration.DatabaseType.ToUpperInvariant(), out providerFactory!))
 119            {
 0120                throw new InvalidOperationException($"Jellyfin cannot find the database provider of type '{efCoreConfigu
 121            }
 122        }
 123
 42124        serviceCollection.AddSingleton<IJellyfinDatabaseProvider>(providerFactory!);
 125
 42126        switch (efCoreConfiguration.LockingBehavior)
 127        {
 128            case DatabaseLockingBehaviorTypes.NoLock:
 42129                serviceCollection.AddSingleton<IEntityFrameworkCoreLockingBehavior, NoLockBehavior>();
 42130                break;
 131            case DatabaseLockingBehaviorTypes.Pessimistic:
 0132                serviceCollection.AddSingleton<IEntityFrameworkCoreLockingBehavior, PessimisticLockBehavior>();
 0133                break;
 134            case DatabaseLockingBehaviorTypes.Optimistic:
 0135                serviceCollection.AddSingleton<IEntityFrameworkCoreLockingBehavior, OptimisticLockBehavior>();
 136                break;
 137        }
 138
 42139        serviceCollection.AddPooledDbContextFactory<JellyfinDbContext>((serviceProvider, opt) =>
 42140        {
 42141            var provider = serviceProvider.GetRequiredService<IJellyfinDatabaseProvider>();
 42142            provider.Initialise(opt);
 42143            var lockingBehavior = serviceProvider.GetRequiredService<IEntityFrameworkCoreLockingBehavior>();
 42144            lockingBehavior.Initialise(opt);
 42145        });
 146
 42147        return serviceCollection;
 148    }
 149}