Search code examples
c#delegatesautofacfunc

How to Wrap a Function Func<ISomething,T> delegate


I want to wrap a library using a Func < T , ISomething > as a method parameter, with ISomething being part of the library. My wrapper should have all the references to the library and somebody who uses the wrapper should not need to reference the library.

I want to wrap the Autofac Register method (with lambda expressions), like here https://autofaccn.readthedocs.io/en/latest/register/registration.html#lambda-expression-components

I somehow need to convert the

Func<IDependencyContainer, T> delegate1

to a

Func<IComponentContext, T> delegate1

 This is what my current try looks like:

public IDependencyContainerToAddOptions<T> WrapperRegister<T>
(
  Func<IDependencyContainer, T> delegate1
)
{
  return new DependencyContainerToAddOptions<T>(_containerBuilder.Register(delegate1));
}

TLDR:

How to wrap the Register method of Autofac, which can be called like this:

  _builder.Register(ctx =>
        { var profileService = ctx.Resolve<IUserProfileService>();

and which is defined like that:

/// <summary>
/// Register a delegate as a component.
/// </summary>
/// <typeparam name="T">The type of the instance.</typeparam>
/// <param name="builder">Container builder.</param>
/// <param name="delegate">The delegate to register.</param>
/// <returns>Registration builder allowing the registration to be configured.</returns>
public static IRegistrationBuilder<T, SimpleActivatorData, SingleRegistrationStyle>
    Register<T>(
        this ContainerBuilder builder,
        Func<IComponentContext, T> delegate1)
{
    if (delegate1 == null) throw new ArgumentNullException(nameof(delegate1));

    return builder.Register((c, p) => delegate1(c));
}

The user of the wrapper must not have to add dependencies to Autofac.


Solution

  • It looks like what you're trying to do overlaps a lot with this FAQ in the Autofac docs asking how to keep Autofac references out of your app. In particular, you're asking how to effectively replicate Autofac some registration syntax, I'm guessing so your apps can register things without referencing Autofac.

    There are some good reasons to do that, but I might try to convince you not to. As noted in that FAQ:

    You will potentially sink a lot of time into writing abstractions and wrappers around things only to replicate a lot of Autofac-specific syntax and capabilities.

    This same sort of "isolate oneself from references" also happens a lot with logging, and there aren't a lot of super great solutions for that, either.

    With the emergence of .NET Core one way to go would be to use the Microsoft.Extensions.DependencyInjection abstractions. Register things with the IServiceCollection there and then you can use the Autofac.Extensions.DependencyInjection package to push those registrations into an Autofac container. This limits you to what the abstractions support, but it gives you abstractions you don't have to maintain.

    But, again, let's say you really want to maintain your own. If you want people to register Func<IComponentContext, T> then you should realize that IComponentContext is an Autofac thing and that breaks your isolation. You mentioned that in the update at the bottom, that you want your users to not have references to Autofac... but consider, again, that you'll need to mirror all the Autofac behavior and syntax.

    So if you're going to do that, you'll need to do create wrappers and abstractions. A LOT of wrappers and abstractions. More than will be worth maintaining. The pattern will be:

    • Create an interface that exposes the functionality you want to provide your clients.
    • Create a wrapper/proxy class that wraps the Autofac stuff and implements the interface.
    • In your composition root (e.g., app startup, etc.) wrap the Autofac implementations and do all the work.

    Something like:

    public interface IMyComponentContext
    {
      T Resolve<T>();
    }
    public class ComponentContextWrapper : IMyComponentContext
    {
      private readonly IComponentContext _autofacContext;
      public ComponentContextWrapper(IComponentContext autofacContext)
      {
        _autofacContext = autofacContext;
      }
      public T Resolve<T>()
      {
        return this._autofacContext.Resolve<T>();
      }
    }
    

    It's going to be a lot. Then...

    • Create a wrapper for IComponentContext.
    • Create the wrappers for all the registration bits.
    • Implement basically already what you have up there - except you'll need to wrap/unwrap as needed. Something like:
    // In the place where you actually register your delegates, you'll have
    // to provide wrappers for your clients:
    builder.Register((c, p) => delegate1(new ComponentContextWrapper(c)));
    

    If I haven't already made it clear, I have a pretty strong opinion that this is a Bad Idea. It's a lot of work and unless there's really some potential need to move away from Autofac, there's no real payoff for maintaining the abstractions, testing them, etc. However, that's what you'll need to do if you want to keep that separation. Good luck!