From 1e3ec1cc220bfbab6570dc2740c5bfa487c7d914 Mon Sep 17 00:00:00 2001 From: Meik Date: Wed, 1 Jul 2026 00:29:04 +0200 Subject: [PATCH] fix: avoid journal service webapi explorer dependency --- F4SDM42WebApi/F4SD - M42WebApi.csproj | 2 +- F4SDM42WebApi/F4SDHelperService.cs | 2 +- F4SDM42WebApi/F4SDM42WebApiController.cs | 97 ++++++++++++++++++++++++ 3 files changed, 99 insertions(+), 2 deletions(-) diff --git a/F4SDM42WebApi/F4SD - M42WebApi.csproj b/F4SDM42WebApi/F4SD - M42WebApi.csproj index d37bdb6..340913d 100644 --- a/F4SDM42WebApi/F4SD - M42WebApi.csproj +++ b/F4SDM42WebApi/F4SD - M42WebApi.csproj @@ -13,7 +13,7 @@ false 6673a25d-33d6-4072-91d9-abed4be1a966 C4ITF4SD*.* - 1.4.0.26 + 1.4.0.27 true diff --git a/F4SDM42WebApi/F4SDHelperService.cs b/F4SDM42WebApi/F4SDHelperService.cs index a37e549..f6f2040 100644 --- a/F4SDM42WebApi/F4SDHelperService.cs +++ b/F4SDM42WebApi/F4SDHelperService.cs @@ -1448,7 +1448,7 @@ namespace C4IT.F4SD private static IReadOnlyList LoadJournalEntries(Guid activityEOID) { - var journalService = F4SDM42WebApiController.defaultInstance.GetJournalService(); + var journalService = F4SDM42WebApiController.defaultInstance.GetJournalServiceInstance(); var serviceType = journalService.GetType(); var objectLinkTemplate = "{2}"; var methods = serviceType diff --git a/F4SDM42WebApi/F4SDM42WebApiController.cs b/F4SDM42WebApi/F4SDM42WebApiController.cs index f78f88b..9440a6f 100644 --- a/F4SDM42WebApi/F4SDM42WebApiController.cs +++ b/F4SDM42WebApi/F4SDM42WebApiController.cs @@ -96,6 +96,103 @@ namespace C4IT.F4SD return GetRequiredService(); } + internal object GetJournalServiceInstance() + { + try + { + return GetRequiredService(); + } + catch (Exception ex) + { + LogEntry($"IJournalService could not be resolved by the Matrix42 container. Falling back to direct JournalService construction. {ex.GetType().FullName}: {ex.Message}", LogLevels.Warning); + return CreateJournalServiceInstance(); + } + } + + private object CreateJournalServiceInstance() + { + var journalServiceType = FindLoadedType("Matrix42.ServiceManager.BizLogic.Services.JournalService") + ?? Type.GetType("Matrix42.ServiceManager.BizLogic.Services.JournalService, Matrix42.ServiceManager.BizLogic", true); + var constructor = journalServiceType + .GetConstructors() + .OrderByDescending(item => item.GetParameters().Length) + .FirstOrDefault(); + + if (constructor == null) + throw new InvalidOperationException($"No public constructor was found for {journalServiceType.FullName}."); + + var parameters = constructor.GetParameters(); + var arguments = new object[parameters.Length]; + for (int i = 0; i < parameters.Length; i++) + arguments[i] = ResolveJournalServiceConstructorParameter(parameters[i]); + + return constructor.Invoke(arguments); + } + + private object ResolveJournalServiceConstructorParameter(System.Reflection.ParameterInfo parameter) + { + if (parameter.ParameterType == typeof(IDependencyResolver)) + return _resolver; + + if (parameter.ParameterType.FullName == "Matrix42.Contracts.Platform.MsTeams.ITeamsNotificationService") + return CreateNoOpInterfaceProxy(parameter.ParameterType); + + var service = _resolver.TryGet(parameter.ParameterType); + if (service != null) + return service; + + throw new InvalidOperationException($"Required JournalService constructor dependency is not registered: {parameter.ParameterType.FullName} ({parameter.Name})."); + } + + private static Type FindLoadedType(string fullName) + { + return AppDomain.CurrentDomain + .GetAssemblies() + .Select(assembly => assembly.GetType(fullName, false)) + .FirstOrDefault(type => type != null); + } + + private static object CreateNoOpInterfaceProxy(Type interfaceType) + { + var createMethod = typeof(DispatchProxy) + .GetMethods(BindingFlags.Public | BindingFlags.Static) + .Single(method => method.Name == nameof(DispatchProxy.Create) && method.IsGenericMethodDefinition); + + return createMethod + .MakeGenericMethod(interfaceType, typeof(NoOpInterfaceProxy)) + .Invoke(null, null); + } + + private sealed class NoOpInterfaceProxy : DispatchProxy + { + protected override object Invoke(MethodInfo targetMethod, object[] args) + { + return GetDefaultValue(targetMethod?.ReturnType); + } + } + + private static object GetDefaultValue(Type returnType) + { + if (returnType == null || returnType == typeof(void)) + return null; + + if (returnType == typeof(Task)) + return Task.CompletedTask; + + if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Task<>)) + { + var resultType = returnType.GetGenericArguments()[0]; + var result = resultType.IsValueType ? Activator.CreateInstance(resultType) : null; + return typeof(Task) + .GetMethods(BindingFlags.Public | BindingFlags.Static) + .Single(method => method.Name == nameof(Task.FromResult) && method.IsGenericMethodDefinition) + .MakeGenericMethod(resultType) + .Invoke(null, new[] { result }); + } + + return returnType.IsValueType ? Activator.CreateInstance(returnType) : null; + } + [Route("getDirectLinkCreateTicket"), HttpGet] public async Task getDirectLinkCreateTicket(string sid = "", string assetname = "") {