lundi 20 avril 2020

Create instance of Generic Type from an assembly of other context in C# using refection

I have a Type from some AssemblyContext (plugin code). I know the same assembly is also loaded in default context. I want to have a method that returns the object of the Type from the corresponding assembly in Default Context.

Here is my code

            public object GetService(Type serviceType, IServiceProvider provider)
            {
                try
                {
                    // If same context
                    var defaultService = provider.GetService(serviceType);
                    if (defaultService != null)
                    {
                        return defaultService;
                    }

                }
                catch (InvalidOperationException ex)
                {

                }

                /// The <see cref="serviceType"/> might come from an external assembly.
                /// Try to find a matching type within the assemblies loaded with the <see cref="Provider"/>'s <see cref="AssemblyLoadContext"/>.

                var providerContext = AssemblyLoadContext.GetLoadContext(provider.GetType().Assembly);
                var resolvedAssembly = FindBestMatch(providerContext.Assemblies.ToList(), serviceType.Assembly.GetName());
                Type resolvedType = null;

                if (serviceType.IsGenericType)
                {
                    resolvedType = resolvedAssembly
                                    .GetType(serviceType.GetGenericTypeDefinition().FullName, throwOnError: true)
                                    .MakeGenericType(serviceType.GetGenericArguments());
                }
                else
                {
                    resolvedType = resolvedAssembly.GetType(serviceType.FullName, throwOnError: true);
                }
                var resolvedService = provider.GetService(resolvedType);
                return ImpromptuInterface.Impromptu.DynamicActLike(resolvedService, serviceType);
            }

The FindBestMatch just looks for the assembly though the list-

        Assembly FindBestMatch(IReadOnlyCollection<Assembly> choices, AssemblyName hint)
        {
            if (choices == null) throw new ArgumentNullException(nameof(choices));
            if (hint == null) throw new ArgumentNullException(nameof(hint));

            return choices.FirstOrDefault(choice => choice.GetName() == hint)
                ?? choices.FirstOrDefault(choice => choice.GetName().FullName == hint.FullName)
                ?? choices.FirstOrDefault(choice => choice.GetName().Name == hint.Name);
        }

Now it works fine for non-generics. For generics like ILogger it does create object of type Logger but it throws RunTimeBinderException while trying to invoke methods on those objects. For example, for logger the exception message reads- "'Logger' does not contain a definition for 'LogDebug'".

So, 1. what's going on here?, 2. is there a better way to achieve this?





Aucun commentaire:

Enregistrer un commentaire