1

We are transitioning from Xamarin.Forms to .Net MAUI but our project uses Prism.Unity.Forms. We have a lot of code that basically uses the IContainer.Resolve() passing in a collection of ParameterOverrides with some primitives but some are interfaces/objects. The T we are resolving is usually a registered View which may or may not be the correct way of doing this but it's what I'm working with and we are doing it in backend code (sometimes a service). What is the correct way of doing this Unity thing in DryIoC? Note these parameters are being set at runtime and may only be part of the parameters a constructor takes in (some may be from already registered dependencies).

Example of the scenario:

//Called from service into custom resolver method
var parameterOverrides = new[]
            {
                new ParameterOverride("productID", 8675309),
                new ParameterOverride("objectWithData", IObjectWithData)
        };

//Custom resolver method example
var resolverOverrides = new List<ResolverOverride>();

foreach(var parameterOverride in parameterOverrides)
{
    resolverOverrides.Add(parameterOverride);
}

return _container.Resolve<T>(resolverOverrides.ToArray());
mnx
  • 11
  • 1

2 Answers2

0

You've found out why you don't use the container outside of the resolution root. I recommend not trying to replicate this error with another container but rather fixing it - use handcoded factories:

internal class SomeFactory : IProductViewFactory
{
    public SomeFactory( IService dependency )
    {
        _dependency = dependency ?? throw new ArgumentNullException( nameof(dependency) );
    }

    #region IProductViewFactory
    public IProductView Create( int productID, IObjectWithData objectWithData ) => new SomeProduct( productID, objectWithData, _dependency );
    #endregion

    #region private
    private readonly IService _dependency;
    #endregion
}

See this, too:

For dependencies that are independent of the instance you're creating, inject them into the factory and store them until needed.

For dependencies that are independent of the context of creation but need to be recreated for each created instance, inject factories into the factory and store them.

For dependencies that are dependent on the context of creation, pass them into the Create method of the factory.

Also, be aware of potential subtle differences in container behaviours: Unity's ResolverOverride works for the whole call to resolve, i.e. they override parameters of dependencies, too, whatever happens to match by name. This could very well be handled very differently by DryIOC.

Haukinger
  • 10,420
  • 2
  • 15
  • 28
0

First, I would agree with the @haukinger answer to rethink how do you pass the runtime information into the services. The most transparent and simple way in my opinion is by passing it via parameters into the consuming methods.

Second, here is a complete example in DryIoc to solve it head-on + the live code to play with.

using System;
using DryIoc;
                    
public class Program
{
    record ParameterOverride(string Name, object Value);
    
    record Product(int productID);
    
    public static void Main()
    {
        // get container somehow, 
        // if you don't have an access to it directly then you may resolve it from your service provider
        IContainer c = new Container();
        c.Register<Product>();
        
        var parameterOverrides = new[]
        {
            new ParameterOverride("productID", 8675309),
            new ParameterOverride("objectWithData", "blah"),
        };
        
        var parameterRules = Parameters.Of;
        foreach (var po in parameterOverrides)
        {
            parameterRules = parameterRules.Details((_, x) => x.Name.Equals(po.Name) ? ServiceDetails.Of(po.Value) : null);
        }
        
        c = c.With(rules => rules.With(parameters: parameterRules));
        
        var s = c.Resolve<Product>();
        
        Console.WriteLine(s.productID);
    }
}
dadhi
  • 4,807
  • 19
  • 25
  • To use different values for the overrides, you'd use `c.With(r=>r.With(rulesForFirstParameterSet)).Resolve()` and `c.With(r=>r.With(rulesForSecondParameterSet)).Resolve()`, right? – Haukinger Feb 03 '23 at 17:59
  • Yes, you may do it like this. – dadhi Feb 11 '23 at 14:38
  • You may do it differently by supplying a single Parameters rules, that will check the original `parameterOverrides` list in runtime. And then modify the `parameterOverrides` list. – dadhi Feb 11 '23 at 14:41