autofac / Autofac

An addictive .NET IoC container
https://autofac.org
MIT License
4.44k stars 836 forks source link

Breaking changes upgrading from 5.1.2 to 7.0.0 - missing type IInstanceLookup #1378

Closed dos-ise closed 1 year ago

dos-ise commented 1 year ago

Describe the Bug

I am trying to upgrade our quite large project from 5.1.2 to 7.0.0 and have encountered an problem with the changes. We used an extension to pass parameters from the top level resolve operation (typically a delegate factory call) to a nested component activation.

Now it can't find the class IInstanceLookup. Is it possible to migrate this to 7.0.0 or is there a feature we could use to achieve this with build-in functionality?

Steps to Reproduce

Our Extension:

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Linq;
using Autofac;
using Autofac.Core;
using Autofac.Core.Resolving;

namespace ViewModel
{
  public static class AutofacExtensions
  {
    private static readonly FieldInfo ContextFieldInfo;
    private static readonly FieldInfo ActivationStackFieldInfo;

    static AutofacExtensions()
    {
      var autofacAssembly = typeof(IInstanceLookup).Assembly;
      Type instanceLookupType = autofacAssembly.GetType("Autofac.Core.Resolving.InstanceLookup");
      ContextFieldInfo = instanceLookupType.GetField("_context", BindingFlags.Instance | BindingFlags.NonPublic);
      Type resolveOperationType = autofacAssembly.GetType("Autofac.Core.Resolving.ResolveOperation");
      ActivationStackFieldInfo = resolveOperationType.GetField("_activationStack", BindingFlags.Instance | BindingFlags.NonPublic);
    }

    public static IResolveOperation Context(this IInstanceLookup instanceLookup)
    {
      return (IResolveOperation)ContextFieldInfo.GetValue(instanceLookup);
    }

    public static IEnumerable<IInstanceLookup> ActivationStack(this IResolveOperation resolveOperation)
    {
      return (IEnumerable<IInstanceLookup>)ActivationStackFieldInfo.GetValue(resolveOperation);
    }

    /// <summary>
    /// Pass parameters from the top level resolve operation (typically a delegate factory call)
    /// to a nested component activation.
    /// </summary>
    public static void ForwardFactoryParameters(PreparingEventArgs e)
    {
      var delegateFactoryActivation = ((IInstanceLookup)e.Context).Context().ActivationStack().Last();
      e.Parameters = e.Parameters.Concat(delegateFactoryActivation.Parameters);
    }

    public static T TryTypedAs<T>(this IEnumerable<Parameter> parameters)
    {
      return parameters.ToList().TryTypedAs<T>();
    }

    public static T TryTypedAs<T>(this ICollection<Parameter> parameters)
    {
      if (parameters
        .OfType<TypedParameter>()
        .Any(p => p.Type == typeof(T)))
      {
        return parameters.TypedAs<T>();
      }

      return default;
    }
  }
}

Usage:

      builder
        .RegisterType<AttachmentService>()
        .As<IAttachmentService>()
        .OnPreparing(AutofacExtensions.ForwardFactoryParameters);

Expected Behavior

I could not find information for this problem here: https://autofac.readthedocs.io/en/latest/whats-new/upgradingfrom5to6.html

Dependency Versions

Autofac: 5.1.2 -> 7.0.0

tillig commented 1 year ago

OK, so you're...

...and you're hoping we can offer some free consulting hours to refactor your code?

(I'm sorry if that sounds kind of pointed, I don't mean it to be, but that's kind of what this request is, right? "How do I refactor my code that does a bunch of unsupported stuff?")

The docs you pointed to do indicate that we changed almost all the internals. There wasn't a literal list of every interface or internal that changed because we focused on the 90% case. There was almost no use case for someone to tie to IInstanceLookup and even less to address "how to migrate code that pokes into private fields."

What I would do if this was me:

Figure out what I'm actually trying to do and focus less on the exact code. "I need to get the type of the thing that was just activated." Or maybe, "I need to get the type that's being activated right now." Or something like that. (I'm reading through the code but not running it in a compiler, I'm not entirely sure what's going on.)

Next, I'd probably think about other things that do something similar. For example, I know logging frameworks usually need to know the type being instantiated so they can get an ILogger<TypeBeingInstantiated>. We have an example for log4net that shows how to do stuff like that. If that doesn't work, maybe I need to think about a different way to accomplish the same thing - introduce a factory the way Microsoft logging does, for example.

But the key, for me, would be less about "how do I figure out where IInstanceLookup went" and instead "starting from scratch, what's the right way to go here?"

I'm guessing you'll be getting into resolve pipelines where you can inspect the things that are happening and manipulate the resolution chain (which is how that log4net example works).

But I can't lie, I don't have a whole lot of time to dedicate to Autofac as it is, so I can't personally promise free consulting hours to solve your specific upgrade problem. Perhaps someone else on the team or in the community can. This may be something you want to post to Stack Overflow and tag it autofac so you can get a wider set of eyes on it. While the Autofac team of ~3 monitors Stack Overflow, so do a LOT more people than are here, and that set of people may have more time to allocate to you.

dos-ise commented 1 year ago

Unfortunately a very similar question was posted already more then 2 years ago on stackoverflow without a solution.

See here: https://stackoverflow.com/questions/65229420/converting-diagnostic-code-for-autofac-from-v5-to-v6

Thanks for pointing out the log4net example. I will take a look at that.

Best regards