Search code examples
c#dependency-injectionautofacasp.net-core-2.0castle

Can not resolve a generic service using manual interception with ASP.NET Core 2 and Castle.Core


I know there are a lot of question similar to this one but actually none of them solved my issue. I created a new Asp.Net Core 2 application. Now I am trying to use an intercepter for a specific service to fetch some data into this service(I am using Castle.Core nuget package). I have a generic IConfigurationInterceptor<> and a real implementation ConfigurationInterceptor<>

Here is the interface:

public interface IConfigurationInterceptor<T> : IInterceptor where T : class { }

public class ConfigurationInterceptor<T> : IConfigurationInterceptor<T> where T : class
{
     public ConfigurationInterceptor(ConfigurationInfo<T> configurationInfo, 
        some other services)
    {
        _configurationInfo = configurationInfo;  
       //.....
    }
    public void Intercept(IInvocation invocation)
    {
        invocation.ReturnValue = somefunc(someconfig, invocation.Arguments);
    }
}

Then I have an extension method like below:

public static void AddSingletonConfiguration<TInterface, TImplementation>(
    this IServiceCollection services, string featureName) 
    where TImplementation : class, TInterface where TInterface : class
{
    var info = new ConfigurationInfo<TImplementation>(featureName, typeof(TInterface));
    var generator = new ProxyGenerator();

    services.AddSingleton(x =>
    {
        var ic = x.GetService<Func<ConfigurationInfo<TImplementation>, 
            IConfigurationInterceptor<TImplementation>>>();

        var icTemp = ic.Invoke(info);

        return (TInterface) generator.CreateInterfaceProxyWithoutTarget(
            info.ServiceType, icTemp);
    });
}

But when I get to this line of code:

 var ic = x.GetService<Func<ConfigurationInfo<TImplementation>, 
    IConfigurationInterceptor<TImplementation>>>();

it returns me a null value for ic:

ConfigurationInfo class is just a simple class I create for storing some extra data.

public sealed class ConfigurationInfo<TImpl>
{
    public Type ServiceType { get; }
    public string FeatureName { get; }

    public ConfigurationInfo(string featureName, Type serviceType)
    {
        FeatureName = featureName;
        ServiceType = serviceType;
    }

    public override string ToString() 
        => $"{FeatureName} ({ServiceType} -> {typeof(TImpl)})";
}

In my ConfigureServices I have these both lines:

services.AddSingleton(typeof(IConfigurationInterceptor<>), 
    typeof(ConfigurationInterceptor<>));
services.AddSingletonConfiguration<IStaticDataConfiguration, StaticDataConfiguration>(
    "SomeFeatureKey");

I am not sure why ic variable is null because previously another project was using Autofac and was working perfectly but in the startup you would find something like this:

builder.RegisterGeneric(typeof(ConfigurationInterceptor<>))
    .As(typeof(IConfigurationInterceptor<>)).SingleInstance();

builder.RegisterConfiguration<IStaticDataConfiguration, StaticDataConfiguration>(
    "SomeFeatureKey");

and the extension method was like this one:

public static void RegisterConfiguration<TInterface, TImplementation>(
    this ContainerBuilder builder, string featureName) 
    where TImplementation : class, TInterface
{
    var info = new ConfigurationInfo<TImplementation>(featureName, typeof(TInterface));
    var generator = new ProxyGenerator();

    builder
        .Register(c =>
        {
            var ic = c.Resolve<Func<ConfigurationInfo<TImplementation>, 
                IConfigurationInterceptor<TImplementation>>>()(info);
            return generator.CreateInterfaceProxyWithoutTarget(info.ServiceType, ic);
        })
        .As<TInterface>()
        .SingleInstance();
}

Any help would be appreaciated.

EDIT 1:

Now I changed from method GetService<> to method GetRequiredService<> and throws an exception like below:

No service for type 'System.Func'2[StaticDataProvider.DomainModel.ConfigurationInfo'1[StaticDataProvider.Services.StaticDataConfiguration],StaticDataProvider.Services.Interfaces.IConfigurationInterceptor'1[StaticDataProvider.Services.StaticDataConfiguration]]' has been registered.

EDIT 2:

To wrap it up here is the issue: In my current project in Asp.Net core I can not get a Func<X, B> while in the Asp.Net MVC 5 project(It is a whole different project) I can get a Func<X, B> using Autofac. I think this has to do with parametrized instantiation feature in Autofac provided by default: here

Now, I dont know if in Asp.Net Core default DI container has something like this 'parametrized instantiation' feature where it allows me resolving Func<X, B> instead of B.


Solution

  • Here is what I had to do: Modified ConfigureService method like below:

    public void ConfigureServices(IServiceCollection services)
    {
        IConfigurationInterceptor<T> GetConfigurationInterceptor<T>(ConfigurationInfo<T> info) where T : class 
        { 
            return new ConfigurationInterceptor<T>(info, services.GetService<IConfigurationProvider>(), Configuration);
        }
    
        services.AddSingletonConfiguration<IStaticDataConfiguration, StaticDataConfiguration>("someFeatureKey", GetConfigurationInterceptor);
     }
    

    Then modified extension methods like below:

    public static void AddSingletonConfiguration<TInterface, TImplementation>(this IServiceCollection services, 
        string featureName, Func<ConfigurationInfo<TImplementation>, IConfigurationInterceptor<TImplementation>> ic) where TImplementation : class, TInterface where TInterface : class
    { 
        var info = new ConfigurationInfo<TImplementation>(featureName, typeof(TInterface));
        var generator = new ProxyGenerator();
        services.AddSingleton(x =>
        {
            var icTemp = ic.Invoke(info);
            return (TInterface) generator.CreateInterfaceProxyWithoutTarget(info.ServiceType, icTemp);
        });
    }
    public static TInterface GetService<TInterface>(this IServiceCollection services) where TInterface : class
    { 
        var serviceProvider = services.BuildServiceProvider();
        return serviceProvider.GetRequiredService<TInterface>();
    }
    

    Now its working fine but the idea is that I had to create Func<X, B> myself and pass as a parameter to extension method.