< Summary

Information
Class: MyNet.Utilities.Plugins.PluginsProvider
Assembly: MyNet.Utilities
File(s): https://raw.githubusercontent.com/sandre58/MyNet/85372080fe102cd9ee155ceab49ae000e7f66103/src/MyNet.Utilities/Plugins/PluginsProvider.cs
Tag: 323_28699572109
Line coverage
0%
Covered lines: 0
Uncovered lines: 8
Coverable lines: 8
Total lines: 97
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 4
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%210%
GetTypes()100%210%
FindType(...)0%620%
Create(...)0%620%

File(s)

https://raw.githubusercontent.com/sandre58/MyNet/85372080fe102cd9ee155ceab49ae000e7f66103/src/MyNet.Utilities/Plugins/PluginsProvider.cs

#LineLine coverage
 1// -----------------------------------------------------------------------
 2// <copyright file="PluginsProvider.cs" company="Stéphane ANDRE">
 3// Copyright (c) Stéphane ANDRE. All rights reserved.
 4// </copyright>
 5// -----------------------------------------------------------------------
 6
 7using System;
 8using System.Collections.Concurrent;
 9using System.Collections.Generic;
 10using System.Linq;
 11
 12namespace MyNet.Utilities.Plugins;
 13
 14/// <summary>
 15/// A scoped, caching facade over <see cref="PluginService"/> that targets a fixed
 16/// <see cref="Root"/> plugins directory.
 17/// <para>
 18/// All public members are thread-safe.
 19/// </para>
 20/// </summary>
 21/// <param name="root">
 22/// The root directory that contains plugin subdirectories.
 23/// See <see cref="PluginService"/> for the expected directory/DLL naming convention.
 24/// </param>
 25public class PluginsProvider(string root)
 26{
 27    /// <summary>
 28    /// Thread-safe cache of discovered plugin types, keyed by the contract type.
 29    /// Using <see cref="ConcurrentDictionary{TKey,TValue}"/> avoids the heavyweight
 30    /// expiry machinery of <c>CacheStorage</c>, which is unnecessary here because
 31    /// plugin types never expire during a process lifetime.
 32    /// </summary>
 033    private readonly ConcurrentDictionary<Type, IReadOnlyList<Type>> _cache = new();
 34
 35    /// <summary>
 36    /// Gets the root plugins directory that this provider is scoped to.
 37    /// </summary>
 38    public string Root { get; } = root;
 39
 40    /// <summary>
 41    /// Returns all concrete types found in the plugins under <see cref="Root"/> that
 42    /// implement or inherit <typeparamref name="T"/>.
 43    /// Results are cached after the first call so subsequent invocations are O(1).
 44    /// </summary>
 45    /// <typeparam name="T">The contract type (base class or interface) to look for.</typeparam>
 46    /// <returns>
 47    /// A read-only list of matching concrete types; never <c>null</c>, possibly empty.
 48    /// </returns>
 49    public IReadOnlyList<Type> GetTypes<T>() =>
 050        _cache.GetOrAdd(typeof(T), _ => [.. PluginService.GetTypes<T>(Root)]);
 51
 52    /// <summary>
 53    /// Returns the <see cref="Type"/> of the plugin that implements <typeparamref name="T"/>,
 54    /// optionally filtered by <paramref name="assemblyName"/>.
 55    /// </summary>
 56    /// <typeparam name="T">The contract type (base class or interface) to look for.</typeparam>
 57    /// <param name="assemblyName">
 58    /// When provided, only types whose declaring assembly name matches
 59    /// (case-insensitive) are considered. When <c>null</c> or empty, the first
 60    /// discovered type is returned regardless of its assembly.
 61    /// </param>
 62    /// <returns>
 63    /// The matching <see cref="Type"/>, or <c>null</c> when no plugin satisfies the criteria.
 64    /// </returns>
 65    public Type? FindType<T>(string? assemblyName = null)
 66    {
 067        var types = GetTypes<T>();
 68
 069        return !string.IsNullOrEmpty(assemblyName)
 070            ? types.FirstOrDefault(t => string.Equals(t.Assembly.GetName().Name, assemblyName, StringComparison.OrdinalI
 071            : types.FirstOrDefault();
 72    }
 73
 74    /// <summary>
 75    /// Creates an instance of the plugin that implements <typeparamref name="T"/>,
 76    /// optionally filtered by <paramref name="assemblyName"/>.
 77    /// </summary>
 78    /// <typeparam name="T">The expected type of the created instance.</typeparam>
 79    /// <param name="assemblyName">
 80    /// When provided, limits the search to the plugin assembly with this name
 81    /// (case-insensitive). When <c>null</c> or empty, the first available plugin is used.
 82    /// </param>
 83    /// <param name="constructorParameters">
 84    /// Optional arguments forwarded verbatim to the plugin type's constructor via
 85    /// <see cref="Activator.CreateInstance(Type, object[])"/>.
 86    /// </param>
 87    /// <returns>
 88    /// A new instance of <typeparamref name="T"/>, or <c>null</c> when no matching plugin
 89    /// was found or instantiation failed.
 90    /// </returns>
 91    public T? Create<T>(string? assemblyName = null, params object[] constructorParameters)
 92    {
 093        var type = FindType<T>(assemblyName);
 094        return type is null ? default : (T?)Activator.CreateInstance(type, constructorParameters);
 95    }
 96}
 97