diff --git a/src/NServiceBus.Core/DeepCopy.cs b/src/NServiceBus.AcceptanceTesting/Support/DeepCopy.cs similarity index 98% rename from src/NServiceBus.Core/DeepCopy.cs rename to src/NServiceBus.AcceptanceTesting/Support/DeepCopy.cs index 90c28725d5a..4465722a41e 100644 --- a/src/NServiceBus.Core/DeepCopy.cs +++ b/src/NServiceBus.AcceptanceTesting/Support/DeepCopy.cs @@ -1,4 +1,6 @@ -/* This file is copied from https://github.com/Burtsev-Alexey/net-object-deep-copy +#nullable disable + +/* This file is copied from https://github.com/Burtsev-Alexey/net-object-deep-copy Source code is released under the MIT license. The MIT License(MIT) diff --git a/src/NServiceBus.Core/EndpointConfiguration.cs b/src/NServiceBus.Core/EndpointConfiguration.cs index 2524ab6495a..1061825d9a2 100644 --- a/src/NServiceBus.Core/EndpointConfiguration.cs +++ b/src/NServiceBus.Core/EndpointConfiguration.cs @@ -2,13 +2,16 @@ namespace NServiceBus; using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Runtime.CompilerServices; using System.Transactions; using Configuration.AdvancedExtensibility; using Features; using Microsoft.Extensions.DependencyInjection; using Pipeline; using Settings; +using Support; /// /// Configuration used to create an endpoint instance. @@ -99,10 +102,13 @@ internal void FinalizeConfiguration(IList availableTypes) { Settings.SetDefault(conventionsBuilder.Conventions); - ActivateAndInvoke(availableTypes, t => t.Customize(this)); + if (RuntimeFeature.IsDynamicCodeSupported) + { + ActivateAndInvoke(availableTypes, t => t.Customize(this)); #pragma warning disable CS0618 // Type or member is obsolete - ActivateAndInvoke(availableTypes, t => t.Run(Settings)); + ActivateAndInvoke(availableTypes, t => t.Run(Settings)); #pragma warning restore CS0618 // Type or member is obsolete + } } readonly ConventionsBuilder conventionsBuilder; @@ -120,6 +126,9 @@ static void ValidateEndpointName(string endpointName) } } +#pragma warning disable CS0618 // Type or member is obsolete + [RequiresDynamicCode($"Only used for {nameof(INeedInitialization)}) and {nameof(IWantToRunBeforeConfigurationIsFinalized)}")] +#pragma warning restore CS0618 // Type or member is obsolete static void ActivateAndInvoke(IList types, Action action) where T : class => ForAllTypes(types, t => { diff --git a/src/NServiceBus.Core/Features/ActivatorUtilityBasedFeatureStartupTaskController.cs b/src/NServiceBus.Core/Features/ActivatorUtilityBasedFeatureStartupTaskController.cs index f72108eebc0..50501ada3fe 100644 --- a/src/NServiceBus.Core/Features/ActivatorUtilityBasedFeatureStartupTaskController.cs +++ b/src/NServiceBus.Core/Features/ActivatorUtilityBasedFeatureStartupTaskController.cs @@ -2,8 +2,9 @@ namespace NServiceBus.Features; +using System.Diagnostics.CodeAnalysis; using Microsoft.Extensions.DependencyInjection; -sealed class ActivatorUtilityBasedFeatureStartupTaskController() : +sealed class ActivatorUtilityBasedFeatureStartupTaskController<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TTask>() : FeatureStartupTaskController(typeof(TTask).Name, static provider => ActivatorUtilities.CreateInstance(provider)) where TTask : FeatureStartupTask; \ No newline at end of file diff --git a/src/NServiceBus.Core/Features/Feature.cs b/src/NServiceBus.Core/Features/Feature.cs index 518a57c12d2..62657282f60 100644 --- a/src/NServiceBus.Core/Features/Feature.cs +++ b/src/NServiceBus.Core/Features/Feature.cs @@ -4,6 +4,7 @@ namespace NServiceBus.Features; using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using Particular.Obsoletes; using Settings; @@ -191,7 +192,7 @@ internal static string GetFeatureName() where TFeature : Feature static IEnabled Enables() where TFeature : Feature, new() => Enabled.Instance; static IDependency Depends() where TFeature : Feature, new() => Dependency.Instance; - static IDependency Depends(Type featureType) => !featureType.IsSubclassOf(baseFeatureType) ? throw new ArgumentException($"A Feature can only depend on another Feature. '{featureType.FullName}' is not a Feature", nameof(featureType)) : new TypeDependency(featureType); + static IDependency Depends([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.AllConstructors)] Type featureType) => !featureType.IsSubclassOf(baseFeatureType) ? throw new ArgumentException($"A Feature can only depend on another Feature. '{featureType.FullName}' is not a Feature", nameof(featureType)) : new TypeDependency(featureType); static IDependency Depends(string featureName) => new WeakDependency(featureName); readonly List> registeredDefaults = []; @@ -221,7 +222,7 @@ internal interface IDependency public static readonly IDependency Instance = new Dependency(); } - sealed class TypeDependency(Type featureType) : IDependency + sealed class TypeDependency([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.AllConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] Type featureType) : IDependency { public string FeatureName { get; } = GetFeatureName(featureType); public Feature Create(FeatureFactory factory) => factory.CreateFeature(featureType); diff --git a/src/NServiceBus.Core/Features/FeatureConfigurationContext.cs b/src/NServiceBus.Core/Features/FeatureConfigurationContext.cs index 48f1560b244..c9ec17bedd4 100644 --- a/src/NServiceBus.Core/Features/FeatureConfigurationContext.cs +++ b/src/NServiceBus.Core/Features/FeatureConfigurationContext.cs @@ -4,6 +4,7 @@ namespace NServiceBus.Features; using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using Microsoft.Extensions.DependencyInjection; using Pipeline; using Settings; @@ -80,7 +81,7 @@ public void AddSatelliteReceiver(string name, QueueAddress transportAddress, Pus /// /// The startup task will automatically have all it's constructor parameters resolved from the dependency injection container. /// The startup task type to register. - public void RegisterStartupTask() where TTask : FeatureStartupTask + public void RegisterStartupTask<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TTask>() where TTask : FeatureStartupTask => TaskControllers.Add(new ActivatorUtilityBasedFeatureStartupTaskController()); /// diff --git a/src/NServiceBus.Core/Features/FeatureFactory.cs b/src/NServiceBus.Core/Features/FeatureFactory.cs index 946619ec71c..0b60dbdc055 100644 --- a/src/NServiceBus.Core/Features/FeatureFactory.cs +++ b/src/NServiceBus.Core/Features/FeatureFactory.cs @@ -2,10 +2,11 @@ namespace NServiceBus.Features; using System; +using System.Diagnostics.CodeAnalysis; class FeatureFactory { - public virtual Feature CreateFeature(Type featureType) => !typeof(Feature).IsAssignableFrom(featureType) + public virtual Feature CreateFeature([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.AllConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] Type featureType) => !typeof(Feature).IsAssignableFrom(featureType) ? throw new ArgumentException( $"The provided type '{featureType.FullName}' is not a valid feature. All features must inherit from '{typeof(Feature).FullName}'.") : featureType.Construct(); diff --git a/src/NServiceBus.Core/Hosting/StartupDiagnostics/Host.cs b/src/NServiceBus.Core/Hosting/StartupDiagnostics/Host.cs index bec5e59e01a..11b67b413cc 100644 --- a/src/NServiceBus.Core/Hosting/StartupDiagnostics/Host.cs +++ b/src/NServiceBus.Core/Hosting/StartupDiagnostics/Host.cs @@ -2,9 +2,11 @@ namespace NServiceBus; using System; +using System.Diagnostics.CodeAnalysis; using System.Reflection; using IODirectory = System.IO.Directory; +[RequiresUnreferencedCode("Attempts to determine application path using Reflection to avoid referencing web assemblies.")] static class Host { public static string GetOutputDirectory() diff --git a/src/NServiceBus.Core/Installation/InstallerComponent.cs b/src/NServiceBus.Core/Installation/InstallerComponent.cs index b05daaf5c3a..4c04dfafb34 100644 --- a/src/NServiceBus.Core/Installation/InstallerComponent.cs +++ b/src/NServiceBus.Core/Installation/InstallerComponent.cs @@ -4,6 +4,7 @@ namespace NServiceBus; using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -44,7 +45,7 @@ public string InstallationUserName set => settings.Set(UsernameSettingsKey, value); } - public void Add() where TInstaller : class, INeedToInstallSomething => installers.Add(new Installer()); + public void Add<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TInstaller>() where TInstaller : class, INeedToInstallSomething => installers.Add(new Installer()); public void AddScannedInstallers(IEnumerable scannedTypes) { @@ -62,7 +63,7 @@ public void AddScannedInstallers(IEnumerable scannedTypes) static bool IsINeedToInstallSomething(Type t) => typeof(INeedToInstallSomething).IsAssignableFrom(t); - sealed class Installer : IInstaller where T : class, INeedToInstallSomething + sealed class Installer<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T> : IInstaller where T : class, INeedToInstallSomething { public async Task Install(IServiceProvider serviceProvider, string identity, CancellationToken cancellationToken = default) { diff --git a/src/NServiceBus.Core/NServiceBus.Core.csproj b/src/NServiceBus.Core/NServiceBus.Core.csproj index bf58099f57b..ad65a0b3a3a 100644 --- a/src/NServiceBus.Core/NServiceBus.Core.csproj +++ b/src/NServiceBus.Core/NServiceBus.Core.csproj @@ -6,6 +6,7 @@ true ..\NServiceBus.snk true + true diff --git a/src/NServiceBus.Core/Pipeline/PipelineExecutionExtensions.cs b/src/NServiceBus.Core/Pipeline/PipelineExecutionExtensions.cs index 1f6b5a1df74..6aabaeeaeb9 100644 --- a/src/NServiceBus.Core/Pipeline/PipelineExecutionExtensions.cs +++ b/src/NServiceBus.Core/Pipeline/PipelineExecutionExtensions.cs @@ -4,6 +4,7 @@ namespace NServiceBus; using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Linq.Expressions; using System.Reflection; @@ -17,6 +18,7 @@ static class PipelineExecutionExtensions { extension(IBehavior[] behaviors) { + [RequiresUnreferencedCode("Requires FastExpressionCompiler")] public Func CreatePipelineExecutionFuncFor(List? expressions = null) where TRootContext : IBehaviorContext => (Func)behaviors.CreatePipelineExecutionExpression(expressions); @@ -29,6 +31,7 @@ public Func CreatePipelineExecutionFuncFor(Lis /// context{N} => GetBehavior(context{N}, {N-1}).Invoke(context{N}, /// context{N+1} => TaskEx.Completed)) /// + [RequiresUnreferencedCode("Requires FastExpressionCompiler")] public Delegate CreatePipelineExecutionExpression(List? expressions = null) { Delegate? lambdaExpression = null; @@ -64,6 +67,7 @@ public Delegate CreatePipelineExecutionExpression(List? expressions /// /// context{i} => GetBehavior(context{i}, {i}).Invoke(context{i+1} => previous) /// > + [RequiresUnreferencedCode("Requires FastExpressionCompiler")] static Delegate CreateBehaviorCallDelegate(MethodInfo methodInfo, ParameterExpression outerContextParam, Type behaviorType, Delegate previous, int i, List? expressions = null) { MethodInfo getBehaviorMethodInfo = GetBehaviorMethodInfo.MakeGenericMethod(outerContextParam.Type, behaviorType); @@ -84,6 +88,7 @@ public static TBehavior GetBehavior(TContext context, int i /// /// context{i} => return TaskEx.CompletedTask; /// > + [RequiresUnreferencedCode("Requires FastExpressionCompiler")] static Delegate CreateDoneDelegate(Type inContextType, int i) { var innerContextParam = Expression.Parameter(inContextType, $"context{i + 1}"); diff --git a/src/NServiceBus.Core/Sagas/CustomFinderAdapter.cs b/src/NServiceBus.Core/Sagas/CustomFinderAdapter.cs index fc8e4ca645d..287e59d9dd4 100644 --- a/src/NServiceBus.Core/Sagas/CustomFinderAdapter.cs +++ b/src/NServiceBus.Core/Sagas/CustomFinderAdapter.cs @@ -2,6 +2,7 @@ namespace NServiceBus; using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; using Extensibility; @@ -9,7 +10,7 @@ namespace NServiceBus; using Persistence; using Sagas; -class CustomFinderAdapter : ICoreSagaFinder where TFinder : ISagaFinder where TSagaData : class, IContainSagaData +class CustomFinderAdapter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TFinder, TSagaData, TMessage> : ICoreSagaFinder where TFinder : ISagaFinder where TSagaData : class, IContainSagaData { public bool IsCustomFinder => true; diff --git a/src/NServiceBus.Core/Sagas/ExpressionBasedMessagePropertyAccessor.cs b/src/NServiceBus.Core/Sagas/ExpressionBasedMessagePropertyAccessor.cs index 3850b73ed96..72ebd9c0e91 100644 --- a/src/NServiceBus.Core/Sagas/ExpressionBasedMessagePropertyAccessor.cs +++ b/src/NServiceBus.Core/Sagas/ExpressionBasedMessagePropertyAccessor.cs @@ -2,6 +2,7 @@ namespace NServiceBus; using System; +using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using FastExpressionCompiler; using Sagas; @@ -9,6 +10,7 @@ namespace NServiceBus; sealed class ExpressionBasedMessagePropertyAccessor(Expression> propertyExpression) : MessagePropertyAccessor { + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "CompileFast not used when IsDynamicCodeSupported is true.")] readonly Func propertyAccessor = System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported ? propertyExpression.CompileFast() : // Fall back to Expression.Compile or reflection-based getter propertyExpression.Compile(); diff --git a/src/NServiceBus.Core/Sagas/IConfigureHowToFindSagaWithFinder.cs b/src/NServiceBus.Core/Sagas/IConfigureHowToFindSagaWithFinder.cs index 8dcf86a7202..d0a5d590b19 100644 --- a/src/NServiceBus.Core/Sagas/IConfigureHowToFindSagaWithFinder.cs +++ b/src/NServiceBus.Core/Sagas/IConfigureHowToFindSagaWithFinder.cs @@ -1,6 +1,7 @@ #nullable enable namespace NServiceBus; +using System.Diagnostics.CodeAnalysis; using Sagas; /// @@ -13,5 +14,5 @@ public interface IConfigureHowToFindSagaWithFinder /// /// Specify the custom saga finder to match the given message to a saga instance. /// - void ConfigureMapping() where TFinder : class, ISagaFinder where TSagaEntity : class, IContainSagaData; + void ConfigureMapping() where TFinder : class, ISagaFinder where TSagaEntity : class, IContainSagaData; } \ No newline at end of file diff --git a/src/NServiceBus.Core/Sagas/IConfigureSagaNotFoundHandler.cs b/src/NServiceBus.Core/Sagas/IConfigureSagaNotFoundHandler.cs index 768d34d7de5..12f13a1f146 100644 --- a/src/NServiceBus.Core/Sagas/IConfigureSagaNotFoundHandler.cs +++ b/src/NServiceBus.Core/Sagas/IConfigureSagaNotFoundHandler.cs @@ -1,6 +1,8 @@ #nullable enable namespace NServiceBus; +using System.Diagnostics.CodeAnalysis; + /// /// Implementation provided by the infrastructure - don't implement this /// unless you intend @@ -11,5 +13,5 @@ public interface IConfigureSagaNotFoundHandler /// /// Specifies the optional saga not found handler for this saga instance. /// - void ConfigureSagaNotFoundHandler() where TNotFoundHandler : ISagaNotFoundHandler; + void ConfigureSagaNotFoundHandler<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TNotFoundHandler>() where TNotFoundHandler : ISagaNotFoundHandler; } \ No newline at end of file diff --git a/src/NServiceBus.Core/Sagas/SagaMapper.cs b/src/NServiceBus.Core/Sagas/SagaMapper.cs index 5f96fee8156..5f6c9c42c8b 100644 --- a/src/NServiceBus.Core/Sagas/SagaMapper.cs +++ b/src/NServiceBus.Core/Sagas/SagaMapper.cs @@ -5,6 +5,7 @@ namespace NServiceBus; using System; using System.Collections.Frozen; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Linq.Expressions; using System.Reflection; @@ -55,14 +56,14 @@ void IConfigureHowToFindSagaWithMessageHeaders.ConfigureMapping() + void IConfigureHowToFindSagaWithFinder.ConfigureMapping() { AssertMessageCanBeMapped($"custom saga finder({typeof(TFinder).FullName})"); finders.Add(new SagaFinderDefinition(new CustomFinderAdapter(), typeof(TMessage))); } - void IConfigureSagaNotFoundHandler.ConfigureSagaNotFoundHandler() + void IConfigureSagaNotFoundHandler.ConfigureSagaNotFoundHandler<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TNotFoundHandler>() { if (notFoundHandler != null) { diff --git a/src/NServiceBus.Core/Sagas/SagaNotFoundHandlerInvocation.cs b/src/NServiceBus.Core/Sagas/SagaNotFoundHandlerInvocation.cs index a9a287d0198..28c28772956 100644 --- a/src/NServiceBus.Core/Sagas/SagaNotFoundHandlerInvocation.cs +++ b/src/NServiceBus.Core/Sagas/SagaNotFoundHandlerInvocation.cs @@ -2,10 +2,11 @@ namespace NServiceBus; using System; +using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; -sealed class SagaNotFoundHandlerInvocation : ISagaNotFoundHandlerInvocation where TSagaNotFoundHandler : ISagaNotFoundHandler +sealed class SagaNotFoundHandlerInvocation<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TSagaNotFoundHandler> : ISagaNotFoundHandlerInvocation where TSagaNotFoundHandler : ISagaNotFoundHandler { public async Task Invoke(IServiceProvider serviceProvider, object message, IMessageProcessingContext context) { diff --git a/src/NServiceBus.Core/Sagas/SagaPropertyMapper.cs b/src/NServiceBus.Core/Sagas/SagaPropertyMapper.cs index 0745747005d..0c6e8854168 100644 --- a/src/NServiceBus.Core/Sagas/SagaPropertyMapper.cs +++ b/src/NServiceBus.Core/Sagas/SagaPropertyMapper.cs @@ -3,6 +3,7 @@ namespace NServiceBus; using System; +using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using Sagas; @@ -20,7 +21,7 @@ internal SagaPropertyMapper(IConfigureHowToFindSagaWithMessage sagaMessageFindin /// /// The message type to map to. /// The saga finder that will return the saga. - public void ConfigureFinderMapping() where TFinder : class, ISagaFinder + public void ConfigureFinderMapping() where TFinder : class, ISagaFinder { if (sagaMessageFindingConfiguration is not IConfigureHowToFindSagaWithFinder sagaMapperFindingConfiguration) { diff --git a/src/NServiceBus.Core/Unicast/MessageHandlerRegistry.cs b/src/NServiceBus.Core/Unicast/MessageHandlerRegistry.cs index 59856c772f7..4b978cacce6 100644 --- a/src/NServiceBus.Core/Unicast/MessageHandlerRegistry.cs +++ b/src/NServiceBus.Core/Unicast/MessageHandlerRegistry.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; @@ -62,6 +63,7 @@ from typeHandled in messagesBeingHandled /// /// Registers the handler type. /// + [RequiresUnreferencedCode(WithReflection.TrimWarning)] public void AddHandler() where THandler : IHandleMessages { var handlerType = typeof(THandler); @@ -82,12 +84,12 @@ public void AddHandler() where THandler : IHandleMessages var messageType = interfaceType.GetGenericArguments()[0]; if (genericTypeDefinition == typeof(IHandleMessages<>)) { - _ = AddMessageHandlerForMessageMethod.InvokeGeneric(this, [handlerType, messageType]); + _ = WithReflection.AddMessageHandlerForMessageMethod.InvokeGeneric(this, [handlerType, messageType]); } if (genericTypeDefinition == typeof(IHandleTimeouts<>)) { - _ = AddTimeoutHandlerForMessageMethod.InvokeGeneric(this, [handlerType, messageType]); + _ = WithReflection.AddTimeoutHandlerForMessageMethod.InvokeGeneric(this, [handlerType, messageType]); } } } @@ -95,7 +97,7 @@ public void AddHandler() where THandler : IHandleMessages /// /// Add a handler for a specific message type. Should only be called by a source generator. /// - public void AddMessageHandlerForMessage() where THandler : class, IHandleMessages + public void AddMessageHandlerForMessage<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] THandler, TMessage>() where THandler : class, IHandleMessages { // We are keeping a small deduplication set to avoid registering the same handler+message combination multiple times // and are using a factory to avoid allocation the IMessageHandlerFactory unless it's needed since it can be expensive @@ -112,7 +114,7 @@ public void AddMessageHandlerForMessage() where THandler : c /// /// Add a handler for a specific timeout type. Should only be called by a source generator. /// - public void AddTimeoutHandlerForMessage() where THandler : class, IHandleTimeouts + public void AddTimeoutHandlerForMessage<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] THandler, TMessage>() where THandler : class, IHandleTimeouts { // We are keeping a small deduplication set to avoid registering the same handler+message combination multiple times // and are using a factory to avoid allocation the IMessageHandlerFactory unless it's needed since it can be expensive @@ -140,6 +142,7 @@ List GetOrCreate() /// Add handlers from types scanned at runtime. /// /// Scanned types, with "load handlers first" types ordered first. + [RequiresUnreferencedCode(WithReflection.TrimWarning)] public void AddScannedHandlers(IEnumerable orderedTypes) { foreach (var type in orderedTypes.Where(IsMessageHandler)) @@ -148,7 +151,7 @@ public void AddScannedHandlers(IEnumerable orderedTypes) } } - internal static bool IsMessageHandler(Type type) + internal static bool IsMessageHandler([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] Type type) { if (type.IsAbstract || type.IsGenericTypeDefinition) { @@ -170,18 +173,25 @@ public void Clear() deduplicationSet.Clear(); } + + [RequiresUnreferencedCode(WithReflection.TrimWarning)] void AddHandlerWithReflection(Type handlerType) => - AddHandlerWithReflectionMethod.InvokeGeneric(this, [handlerType]); + WithReflection.AddHandlerWithReflectionMethod.InvokeGeneric(this, [handlerType]); - static readonly MethodInfo AddHandlerWithReflectionMethod = typeof(MessageHandlerRegistry) - .GetMethod(nameof(AddHandler), BindingFlags.Public | BindingFlags.Instance, []) ?? throw new MissingMethodException(nameof(AddHandler)); + [RequiresUnreferencedCode(TrimWarning)] + static class WithReflection + { + public static readonly MethodInfo AddHandlerWithReflectionMethod = typeof(MessageHandlerRegistry) + .GetMethod(nameof(AddHandler), BindingFlags.Public | BindingFlags.Instance, []) ?? throw new MissingMethodException(nameof(AddHandler)); - static readonly MethodInfo AddMessageHandlerForMessageMethod = typeof(MessageHandlerRegistry) - .GetMethod(nameof(AddMessageHandlerForMessage)) ?? throw new MissingMethodException(nameof(AddMessageHandlerForMessage)); + public static readonly MethodInfo AddMessageHandlerForMessageMethod = typeof(MessageHandlerRegistry) + .GetMethod(nameof(AddMessageHandlerForMessage)) ?? throw new MissingMethodException(nameof(AddMessageHandlerForMessage)); - static readonly MethodInfo AddTimeoutHandlerForMessageMethod = typeof(MessageHandlerRegistry) - .GetMethod(nameof(AddTimeoutHandlerForMessage)) ?? throw new MissingMethodException(nameof(AddTimeoutHandlerForMessage)); + public static readonly MethodInfo AddTimeoutHandlerForMessageMethod = typeof(MessageHandlerRegistry) + .GetMethod(nameof(AddTimeoutHandlerForMessage)) ?? throw new MissingMethodException(nameof(AddTimeoutHandlerForMessage)); + public const string TrimWarning = "When adding handlers from assembly scanning using reflection, the methods cannot be trimmed."; + } readonly Dictionary> messageHandlerFactories = []; readonly HashSet deduplicationSet = []; @@ -200,7 +210,7 @@ interface IMessageHandlerFactory MessageHandler Create(); } - sealed class TimeoutHandlerFactory : IMessageHandlerFactory + sealed class TimeoutHandlerFactory<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] THandler, TMessage> : IMessageHandlerFactory where THandler : class { public Type MessageType { get; } = typeof(TMessage); @@ -220,7 +230,7 @@ public MessageHandler Create() => static (sp, args) => Unsafe.As>(factory(sp, args)); } - sealed class MessageHandlerFactory : IMessageHandlerFactory + sealed class MessageHandlerFactory<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] THandler, TMessage> : IMessageHandlerFactory where THandler : class { public Type MessageType { get; } = typeof(TMessage); diff --git a/src/NServiceBus.Core/Utils/Reflection/DelegateFactory.cs b/src/NServiceBus.Core/Utils/Reflection/DelegateFactory.cs index 2d088464b7e..c5c3fa56120 100644 --- a/src/NServiceBus.Core/Utils/Reflection/DelegateFactory.cs +++ b/src/NServiceBus.Core/Utils/Reflection/DelegateFactory.cs @@ -2,10 +2,13 @@ namespace NServiceBus; using System; using System.Collections.Concurrent; +using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using System.Reflection; using System.Reflection.Emit; +[RequiresUnreferencedCode("DelegateFactory is not supported in trimming scenarios. Only used by XmlSerializer.")] + static class DelegateFactory { public static Func CreateGet(PropertyInfo property) diff --git a/src/NServiceBus.Core/Utils/Reflection/MethodInfoExtensions.cs b/src/NServiceBus.Core/Utils/Reflection/MethodInfoExtensions.cs index 4effec2dfe8..16398c8853f 100644 --- a/src/NServiceBus.Core/Utils/Reflection/MethodInfoExtensions.cs +++ b/src/NServiceBus.Core/Utils/Reflection/MethodInfoExtensions.cs @@ -3,22 +3,29 @@ namespace NServiceBus; using System; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using System.Runtime.ExceptionServices; static class MethodInfoExtensions { + const string TrimWarning = "This method calls MakeGenericMethod and cannot be used in trimming."; extension(MethodInfo method) { + [RequiresUnreferencedCode(TrimWarning)] public T? InvokeGeneric(object? target, object?[]? args, Type[] genericTypes) => (T?)method.InvokeGeneric(target, args, genericTypes); + [RequiresUnreferencedCode(TrimWarning)] public T? InvokeGeneric(object?[]? args, Type[] genericTypes) => (T?)method.InvokeGeneric(null, args, genericTypes); + [RequiresUnreferencedCode(TrimWarning)] public T? InvokeGeneric(Type genericType) => (T?)method.InvokeGeneric(null, null, [genericType]); + [RequiresUnreferencedCode(TrimWarning)] public object? InvokeGeneric(object? target, Type[] genericTypes) => method.InvokeGeneric(target, null, genericTypes); + [RequiresUnreferencedCode(TrimWarning)] public object? InvokeGeneric(object? target, object?[]? args, Type[] genericTypes) { try diff --git a/src/NServiceBus.Core/Utils/Reflection/TypeExtensionMethods.cs b/src/NServiceBus.Core/Utils/Reflection/TypeExtensionMethods.cs index 0c613168ffa..e5e76872b13 100644 --- a/src/NServiceBus.Core/Utils/Reflection/TypeExtensionMethods.cs +++ b/src/NServiceBus.Core/Utils/Reflection/TypeExtensionMethods.cs @@ -7,9 +7,9 @@ namespace NServiceBus; static class TypeExtensionMethods { - extension(Type type) + extension([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] Type type) { - public T Construct<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] T>() => (T)Activator.CreateInstance(type, nonPublic: true); + public T Construct() => (T)Activator.CreateInstance(type, nonPublic: true); /// /// Returns true if the type can be serialized as is. diff --git a/src/NServiceBus.PersistenceTests/NServiceBus.PersistenceTests.csproj b/src/NServiceBus.PersistenceTests/NServiceBus.PersistenceTests.csproj index 34935d06db9..c0f9581d83a 100644 --- a/src/NServiceBus.PersistenceTests/NServiceBus.PersistenceTests.csproj +++ b/src/NServiceBus.PersistenceTests/NServiceBus.PersistenceTests.csproj @@ -29,8 +29,12 @@ - + + + + +