diff --git a/WireMock.Net Solution.sln.DotSettings b/WireMock.Net Solution.sln.DotSettings
index ace990494..d26576695 100644
--- a/WireMock.Net Solution.sln.DotSettings
+++ b/WireMock.Net Solution.sln.DotSettings
@@ -23,6 +23,7 @@
WWW
XMS
XUA
+ True
True
True
True
diff --git a/src/WireMock.Net/Plugin/PluginLoader.cs b/src/WireMock.Net/Plugin/PluginLoader.cs
deleted file mode 100644
index b6f724ac3..000000000
--- a/src/WireMock.Net/Plugin/PluginLoader.cs
+++ /dev/null
@@ -1,56 +0,0 @@
-using System;
-using System.Collections.Concurrent;
-using System.IO;
-using System.Linq;
-using System.Reflection;
-
-namespace WireMock.Plugin;
-
-internal static class PluginLoader
-{
- private static readonly ConcurrentDictionary Assemblies = new();
-
- public static T Load(params object[] args) where T : class
- {
- var foundType = Assemblies.GetOrAdd(typeof(T), (type) =>
- {
- var files = Directory.GetFiles(Directory.GetCurrentDirectory(), "*.dll");
-
- Type? pluginType = null;
- foreach (var file in files)
- {
- try
- {
- var assembly = Assembly.Load(new AssemblyName
- {
- Name = Path.GetFileNameWithoutExtension(file)
- });
-
- pluginType = GetImplementationTypeByInterface(assembly);
- if (pluginType != null)
- {
- break;
- }
- }
- catch
- {
- // no-op: just try next .dll
- }
- }
-
- if (pluginType != null)
- {
- return pluginType;
- }
-
- throw new DllNotFoundException($"No dll found which implements type '{type}'");
- });
-
- return (T)Activator.CreateInstance(foundType, args);
- }
-
- private static Type? GetImplementationTypeByInterface(Assembly assembly)
- {
- return assembly.GetTypes().FirstOrDefault(t => typeof(T).IsAssignableFrom(t) && !t.GetTypeInfo().IsInterface);
- }
-}
\ No newline at end of file
diff --git a/src/WireMock.Net/Serialization/MatcherMapper.cs b/src/WireMock.Net/Serialization/MatcherMapper.cs
index 5298c0276..94c068d29 100644
--- a/src/WireMock.Net/Serialization/MatcherMapper.cs
+++ b/src/WireMock.Net/Serialization/MatcherMapper.cs
@@ -8,7 +8,6 @@
using WireMock.Extensions;
using WireMock.Matchers;
using WireMock.Models;
-using WireMock.Plugin;
using WireMock.Settings;
using WireMock.Util;
@@ -53,7 +52,7 @@ public MatcherMapper(WireMockServerSettings settings)
case "CSharpCodeMatcher":
if (_settings.AllowCSharpCodeMatcher == true)
{
- return PluginLoader.Load(matchBehaviour, matchOperator, stringPatterns);
+ return TypeLoader.Load(matchBehaviour, matchOperator, stringPatterns);
}
throw new NotSupportedException("It's not allowed to use the 'CSharpCodeMatcher' because WireMockServerSettings.AllowCSharpCodeMatcher is not set to 'true'.");
diff --git a/src/WireMock.Net/Util/TypeLoader.cs b/src/WireMock.Net/Util/TypeLoader.cs
new file mode 100644
index 000000000..fcd27e779
--- /dev/null
+++ b/src/WireMock.Net/Util/TypeLoader.cs
@@ -0,0 +1,89 @@
+using System;
+using System.Collections.Concurrent;
+using System.Diagnostics.CodeAnalysis;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Stef.Validation;
+
+namespace WireMock.Util;
+
+internal static class TypeLoader
+{
+ private static readonly ConcurrentDictionary Assemblies = new();
+
+ public static TInterface Load(params object[] args) where TInterface : class
+ {
+ var key = typeof(TInterface).FullName!;
+
+ var pluginType = Assemblies.GetOrAdd(key, _ =>
+ {
+ if (TryFindTypeInDlls(null, out var foundType))
+ {
+ return foundType;
+ }
+
+ throw new DllNotFoundException($"No dll found which implements Interface '{key}'.");
+ });
+
+ return (TInterface)Activator.CreateInstance(pluginType, args)!;
+ }
+
+ public static TInterface LoadByFullName(string implementationTypeFullName, params object[] args) where TInterface : class
+ {
+ Guard.NotNullOrEmpty(implementationTypeFullName);
+
+ var @interface = typeof(TInterface).FullName;
+ var key = $"{@interface}_{implementationTypeFullName}";
+
+ var pluginType = Assemblies.GetOrAdd(key, _ =>
+ {
+ if (TryFindTypeInDlls(implementationTypeFullName, out var foundType))
+ {
+ return foundType;
+ }
+
+ throw new DllNotFoundException($"No dll found which implements Interface '{@interface}' and has FullName '{implementationTypeFullName}'.");
+ });
+
+ return (TInterface)Activator.CreateInstance(pluginType, args)!;
+ }
+
+ private static bool TryFindTypeInDlls(string? implementationTypeFullName, [NotNullWhen(true)] out Type? pluginType) where TInterface : class
+ {
+ foreach (var file in Directory.GetFiles(Directory.GetCurrentDirectory(), "*.dll"))
+ {
+ try
+ {
+ var assembly = Assembly.Load(new AssemblyName
+ {
+ Name = Path.GetFileNameWithoutExtension(file)
+ });
+
+ if (TryGetImplementationTypeByInterfaceAndOptionalFullName(assembly, implementationTypeFullName, out pluginType))
+ {
+ return true;
+ }
+ }
+ catch
+ {
+ // no-op: just try next .dll
+ }
+ }
+
+ pluginType = null;
+ return false;
+ }
+
+ private static bool TryGetImplementationTypeByInterfaceAndOptionalFullName(Assembly assembly, string? implementationTypeFullName, [NotNullWhen(true)] out Type? type)
+ {
+ type = assembly
+ .GetTypes()
+ .FirstOrDefault(t =>
+ typeof(T).IsAssignableFrom(t) && !t.GetTypeInfo().IsInterface &&
+ (implementationTypeFullName == null || string.Equals(t.FullName, implementationTypeFullName, StringComparison.OrdinalIgnoreCase))
+ );
+
+ return type != null;
+ }
+}
\ No newline at end of file
diff --git a/test/WireMock.Net.Tests/Plugin/PluginLoaderTests.cs b/test/WireMock.Net.Tests/Plugin/PluginLoaderTests.cs
deleted file mode 100644
index 25476def4..000000000
--- a/test/WireMock.Net.Tests/Plugin/PluginLoaderTests.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-using System;
-using AnyOfTypes;
-using FluentAssertions;
-using WireMock.Matchers;
-using WireMock.Models;
-using WireMock.Plugin;
-using Xunit;
-
-namespace WireMock.Net.Tests.Plugin;
-
-public class PluginLoaderTests
-{
- public interface IDummy
- {
- }
-
- [Fact]
- public void Load_Valid()
- {
- // Act
- AnyOf pattern = "x";
- var result = PluginLoader.Load(MatchBehaviour.AcceptOnMatch, MatchOperator.Or, pattern);
-
- // Assert
- result.Should().NotBeNull();
- }
-
- [Fact]
- public void Load_Invalid_ThrowsException()
- {
- // Act
- Action a = () => PluginLoader.Load();
-
- // Assert
- a.Should().Throw();
- }
-}
\ No newline at end of file
diff --git a/test/WireMock.Net.Tests/Util/TypeLoaderTests.cs b/test/WireMock.Net.Tests/Util/TypeLoaderTests.cs
new file mode 100644
index 000000000..d289e5483
--- /dev/null
+++ b/test/WireMock.Net.Tests/Util/TypeLoaderTests.cs
@@ -0,0 +1,65 @@
+using System;
+using AnyOfTypes;
+using FluentAssertions;
+using WireMock.Matchers;
+using WireMock.Models;
+using WireMock.Util;
+using Xunit;
+
+namespace WireMock.Net.Tests.Util;
+
+public class TypeLoaderTests
+{
+ public interface IDummyInterfaceNoImplementation
+ {
+ }
+
+ public interface IDummyInterfaceWithImplementation
+ {
+ }
+
+ public class DummyClass : IDummyInterfaceWithImplementation
+ {
+ }
+
+ [Fact]
+ public void Load_ByInterface()
+ {
+ // Act
+ AnyOf pattern = "x";
+ var result = TypeLoader.Load(MatchBehaviour.AcceptOnMatch, MatchOperator.Or, pattern);
+
+ // Assert
+ result.Should().NotBeNull();
+ }
+
+ [Fact]
+ public void Load_ByInterfaceAndFullName()
+ {
+ // Act
+ var result = TypeLoader.LoadByFullName(typeof(DummyClass).FullName!);
+
+ // Assert
+ result.Should().BeOfType();
+ }
+
+ [Fact]
+ public void Load_ByInterface_ButNoImplementationFoundForInterface_ThrowsException()
+ {
+ // Act
+ Action a = () => TypeLoader.Load();
+
+ // Assert
+ a.Should().Throw().WithMessage("No dll found which implements Interface 'WireMock.Net.Tests.Util.TypeLoaderTests+IDummyInterfaceNoImplementation'.");
+ }
+
+ [Fact]
+ public void Load_ByInterfaceAndFullName_ButNoImplementationFoundForInterface_ThrowsException()
+ {
+ // Act
+ Action a = () => TypeLoader.LoadByFullName("xyz");
+
+ // Assert
+ a.Should().Throw().WithMessage("No dll found which implements Interface 'WireMock.Net.Tests.Util.TypeLoaderTests+IDummyInterfaceWithImplementation' and has FullName 'xyz'.");
+ }
+}
\ No newline at end of file