0

What I'm trying to do is similar to the question bellow:

How do I use reflection to call a generic method?

but the function I'm trying to call needs a lambda input

(I'm adding masstransit consumers from domain assemblies by selecting assemblies where they are inheriting different interfaces)

The code:

        private static void AddLimitedConcurrencyMessageRecievers(IServiceCollectionBusConfigurator x, Assembly[] assemblies)
        {
            IEnumerable<Type> types = assemblies.SelectMany(s => s.GetExportedTypes())
                .Where(w => w.IsClass && !w.IsAbstract && w.IsPublic && typeof(IMessageReciever).IsAssignableFrom(w)).ToList();
            
            foreach (var item in types)
            {
                //x.AddConsumer<item>(y => y.UseConcurrentMessageLimit(1)); this is how I wished it would be like
                MethodInfo method = typeof(IRegistrationConfigurator).GetMethod(nameof(IRegistrationConfigurator.AddConsumer));
                MethodInfo generic = method.MakeGenericMethod(item);
                generic.Invoke(null, y => y.UseConcurrentMessageLimit(1)); //this still does not work
            }
                
        }
  • The important thing to note is that you're not using `x` anywhere. Since you're supposed to be calling a method on `x`, surely it should be referenced somewhere? The other thing to note is that you're passing `null` as the `obj` parameter to `generic.Invoke`, which means you're trying to invoke `generic` as a static method. But it's not a static method, it's an instance method. Try passing `x` instead of `null` there. – canton7 Sep 19 '21 at 09:26
  • Also, calling `GetMethod` inside that loop will be significantly more expensive than it needs to be: move it to before the loop – canton7 Sep 19 '21 at 09:26
  • The other other thing is that lambdas don't have an inherent type in C# - you can't write `var o = y => y.UseConcurrentMessageLimit(1)` for example, as the compiler can't work out which of the many possible delegate types `o` should be. You'll have to construct it as `new Action(...)` for some `T`, which makes life a bit more complex since `T` isn't known – canton7 Sep 19 '21 at 09:28
  • You're probably best off writing your own generic `AddConsumer` method, which looks like `static void AddConsumer(IServiceCollectionBusConfigurator x) => x.AddConsumer(y => y.UseConcurrentMessageLimit(1));`, and then calling that using reflection from your loop: `var method = typeof(ContainingType).GetMethod("AddConsumer"); method.MakeGenericMethod(item).Invoke(null, x)` – canton7 Sep 19 '21 at 09:35
  • @canton7 thanks for the help. do you know any resources so I can learn more about using reflections on these types of problems? – Sein Gerivani Sep 19 '21 at 11:59

2 Answers2

0

You shouldn't need to use any of the generic/invoke wizardry to add your consumers. There is a non-generic version of AddConsumer that accepts a Type, and you can use either consumer definitions or IConfigureReceiveEndpoint to add additional configuration when the receive endpoint is created.

There are plenty of configuration methods to support generic and non-generic scenarios, so don't try to force yourself into using the generic methods resulting code that the next developer will hate you for writing.

Chris Patterson
  • 28,659
  • 3
  • 47
  • 59
  • Thanks for the quick reply and for the great tool you've provided. but a task is a task and I have to go with the generic wizardry approach. – Sein Gerivani Sep 19 '21 at 17:05
0

Thanks to @canton7 and Chris Patterson's responses this is how I managed to solve the problem:


        public static void AddLimitedConcurrencyMessageRecievers<TMessageReciever>(this IServiceCollectionBusConfigurator x, params Assembly[] assemblies)
        {
            IEnumerable<Type> types = assemblies.SelectMany(s => s.GetExportedTypes())
                .Where(w => w.IsClass && !w.IsAbstract && w.IsPublic && typeof(TMessageReciever).IsAssignableFrom(w)).ToList();

            foreach (var item in types)
            {
                var method = typeof(MessageServices).GetMethod(nameof(AddLimitedConcurrencyConsumer));
                method.MakeGenericMethod(item).Invoke(null, new object[] { x });
            }

        }
        public static void AddLimitedConcurrencyConsumer<T>(this IServiceCollectionBusConfigurator x) where T : class, IConsumer
        {
            x.AddConsumer<T>(y => y.UseConcurrentMessageLimit(1));
        }