Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
  • 12:28
    mysticmind labeled #1568
  • 12:28
    mysticmind commented #1568
  • 12:18
    kasparurban commented #1568
  • Oct 23 09:08
    srasch edited #1588
  • Oct 23 09:07
    srasch synchronize #1588
  • Oct 23 09:05
    srasch synchronize #1588
  • Oct 23 08:56
    dnfadmin commented #1588
  • Oct 23 08:54
    dnfadmin commented #1588
  • Oct 23 08:54
    srasch opened #1588
  • Oct 22 22:06
    dnfadmin commented #1587
  • Oct 22 22:05
    markjerz commented #1582
  • Oct 22 22:04
    dnfadmin commented #1587
  • Oct 22 22:04
    markjerz opened #1587
  • Oct 22 20:03

    oskardudycz on merge_3_x_to_master

    (compare)

  • Oct 22 20:03

    oskardudycz on master

    Introduce DefaultRetryPolicy (#… (compare)

  • Oct 22 20:03
    oskardudycz closed #1586
  • Oct 22 19:14
    oskardudycz synchronize #1586
  • Oct 22 19:14

    oskardudycz on merge_3_x_to_master

    Introduce DefaultRetryPolicy (#… (compare)

  • Oct 22 19:08
    oskardudycz opened #1586
  • Oct 22 19:08

    oskardudycz on merge_3_x_to_master

    Introduce DefaultRetryPolicy (#… (compare)

Doug Slater
@slater1

Hi,

I'm using SM 4.7, and I'm looking to create a custom lifecycle which returns a singleton scoped to the requesting type, for example:

an instance of type B requests an A -> build an A
an instance of type B requests an A -> return same instance of A
an instance of type C requests an A -> build an A

My implementation fails in nested composition scenarios, for example:

an instance of type B injected into instance of type C requests an A -> build an A
an instance of type B injected into instance of type D requests an A -> should return same instance of A. My implementation creates a new instance.

Below is the implementation and above failing test case. As you can see, I'm relying on BuildSession.RootType. I found that ParentType was always null or equal to the RootType rather than the requesting type.

How would you change RequesterLifecycleObjectCache to make the test pass?

Thank you in advance,

Doug Slater

using Shouldly;
using StructureMap.Pipeline;
using System;
using System.Collections.Concurrent;
using System.Linq;
using Xunit;

namespace StructureMap.Testing.Acceptance
{
    /// <summary>
    /// Lifecycle which maintains a singleton instance 
    /// of each requested type A 
    /// for each requesting type B
    /// 
    /// <para/>
    /// See RequesterLifecycleObjectCache for details.
    /// </summary>
    public class RequesterLifecycle : ILifecycle
    {
        public string Description => "Lifecycle which maintains a singleton instance for each requested type A for each requesting type B";

        private readonly IObjectCache _cache = new RequesterLifecycleObjectCache();

        public void EjectAll(ILifecycleContext context)
        {
            FindCache(context).DisposeAndClear();
        }

        public IObjectCache FindCache(ILifecycleContext context)
        {
            return _cache;
        }
    } 

    /// <summary>
    /// IObjectCache which maintains a singleton instance 
    /// of each requested type A 
    /// for each requesting type B
    /// </summary>
    /// 
    /// Example:
    /// 
    ///    class A implements interface IA
    ///    In StructureMap, IA resolves to A with RequesterLifecycle
    /// 
    ///    Later...
    ///     
    ///    an instance of type B requests an IA (key: B:IA) -> build an A
    ///    an instance of type B requests an IA (key: B:IA) -> return same instance of A
    ///    an instance of type C requests an IA (key: C:IA) -> build an A
    public class RequesterLifecycleObjectCache : IObjectCache
    {
        /// <summary>
        /// Maps each unique requester/requested pair to its object instance.
        /// </summary>
        private readonly ConcurrentDictionary<string, object> _objects = new ConcurrentDictionary<string, object>();

        public int Count => _objects.Count;

        public bool Has(Type pluginType, Instance instance)
        {
            return false;
        }

        public void Eject(Type pluginType, Instance instance)
        {
        }

        public object Get(Type pluginType, Instance instance, IBuildSession session)
        {
            var buildSession = session as BuildSession;
            var requester = buildSession.ParentType ?? buildSession.RootType;

            var key = GetKey(requester, pluginType);
            return _objects.GetOrAdd(key, _ => buildWithSession(pluginType, instance, session));
        }

        protected virtual object buildWithSession(Type pluginType, Instance instance, IBuildSession session)
        {
            return session.BuildNewInOriginalContext(pluginType, instance);
        }

        public void DisposeAndClear()
        {
            var keys = _objects.Keys.ToList();

            foreach (var key in keys)
            {
                if (_objects.TryRemove(key, out object toDispose))
                {
                    if (toDispose != null && !(toDispose is Container))
                    {
                        SafeDispose(toDispose);
                    }
                }
            }
        }

        private static void SafeDispose(object target)
        {
            if (!(target is IDisposable disposable)) return;

            try
            {
                disposable.Dispose();
            }
            catch (Exception)
            {
            }
        }

        /// <summary>
        /// Creates a key that is unique for the given requesting and requested type pair.
        /// 
        /// <para/>
        /// e.g. "B:IA" reads as "an instance of type B requests an instance of type IA"
        /// </summary>
        private string GetKey(Type requester, Type requested)
        {
            var requesterName = requester?.AssemblyQualifiedName ?? string.Empty;
            var requestedName = requested?.AssemblyQualifiedName ?? string.Empty;

            return $"{requesterName}:{requestedName}";
        }
    }
    public class Test
    {
        public interface IA { }
        public class A : IA { }

        public interface IHasA
        {
            IA A { get; set; }
        }

        public class HasA : IHasA
        {
            public IA A { get; set; }

            public HasA(IA a)
            {
                A = a;
            }
        }

        public interface IB
        {
            IHasA HasA { get; set; }
        }

        public class B : IB
        {
            public IHasA HasA { get; set; }

            public B(IHasA hasA)
            {
                HasA = hasA;
            }
        }

        public interface IC
        {
            IHasA HasA { get; set; }
        }

        public class C : IC
        {
            public IHasA HasA { get; set; }

            public C(IHasA hasA)
            {
                HasA = hasA;
            }
        }

        private IContainer _container;

        public Test()
        {
            _container = new Container(config =>
            {
                config.For<IA>().Use<A>().LifecycleIs<RequesterLifecycle>();

                config.For<IHasA>().Use<HasA>();
                config.For<IB>().Use<B>();
                config.For<IC>().Use<C>();
            });
        }

        [Fact]
        public void HandlesNestedComposition()
        {
            var hasA = _container.GetInstance<IHasA>();
            var hasAtoo = _container.GetInstance<IHasA>();

            var b = _container.GetInstance<IB>();
            var c = _container.GetInstance<IC>();

            hasA.ShouldNotBeSameAs(hasAtoo); // Ok
            hasA.A.ShouldBeSameAs(hasAtoo.A); // Ok

            b.HasA.A.ShouldBeSameAs(c.HasA.A); // Fails
        }
    }
}
Jeremy D. Miller
@jeremydmiller
I think you’d be better off just using some kind of ISomething<T> where T is the concrete class it’s being injected into. Then make the ISomething<> registration a singleton and off you go. Failing that, you could use a combination of a “missing name instance” and a policy that adds an inline dependency by name I suppose. I wouldn’t try to use the custom life cycle for this.
Doug Slater
@slater1
Jeremy, thanks for your feedback. Based on that, I'll abandon the custom lifecycle approach and try something else. I also saw the link about Lamar replacing StructureMap. I'll see if my team can begin a migration plan. Thanks for your time.
Master Whiz
@whizkidwwe1217
image.png
@jeremydmiller Hi, is there a way to resolve a service through Registry?
Jeremy D. Miller
@jeremydmiller
Registry is for configuration, not resolution. You know you can register services by lambdas that can in turn use the container later at runtime?
Master Whiz
@whizkidwwe1217
Ohh I see. I actually tried the lambdas but it didn't allow me to put a block statement until I figured out that I need to provide a description. Thanks Jeremy.
whatneuron
@whatneuron
Morning all
Anyone active ?
Jeremy D. Miller
@jeremydmiller
What’s up?
Anish1987
@Anish1987
Hi All, I wanted to pass constructor argument in container.GetInstance but unable to find it. can anyone help?.
Jeremy D. Miller
@jeremydmiller
Look for “explicit arguments” in the documentation
Jeremy D. Miller
@jeremydmiller
image.png
Anish1987
@Anish1987
Thanks Jeremy
rafae11
@rafae11
Hi wanted to ask how do i inject a mock object?
container.Inject<IAzureCloudStorageWrapper>(() => mockAzureStorage.Object);
I get the error cannot convert lambda expression to type 'IAzureCloudStorageWrapper' because it is not a delegate type.
TarunKumarG
@TarunKumarG

Hi Team,

I am facing an issue with dependency registration.

Structuremap is asking us to register the dependcies which are part of nuget package. We are not suppose to register the nuget package or any other third party packages dependencies in our projects as these are already registered in their own projects.
Is there a way to tell structuremap that it is already registered in packages.

Please help.

Issue/Exception:
StructureMap.StructureMapConfigurationException: No default Instance is registered and cannot be automatically determined for type 'HM.Disco.PurchaseProduct.DataProviders.IPurchaseProductDataProvider'

There is no configuration specified for HM.Disco.PurchaseProduct.DataProviders.IPurchaseProductDataProvider

1.) new PreAdvicesApprovedEventHandler(Default of IPurchaseProductDataProvider, Default of ISecurityDataProvider, Default of IHmOrderUpdater, Default of IHmOrderUpdateEmailComposer, Default of IFeatureAccessManager)
2.) HM.Disco.ItOps.Gateway.HMOrder.PreAdvicesApprovedEventHandler
3.) Instance of HM.Disco.ItOps.Gateway.HMOrder.PreAdvicesApprovedEventHandler
4.) Container.GetInstance(HM.Disco.ItOps.Gateway.HMOrder.PreAdvicesApprovedEventHandler)

Thanks,
Jeremy D. Miller
@jeremydmiller
@TarunKumarG It’s clearly not registered, or you wouldn’t be getting that exception
TarunKumarG
@TarunKumarG
Even we thought the same, but these depedencies were registered in the nuget packages that we were referring to
if they were already registered in the package itself, why there is a need to register them in the project that the package is used.
@jeremydmiller
Jeremy D. Miller
@jeremydmiller
Not in the StructureMap container you’re actually using. Have you checked the WhatDoIHave() diagnostics to verify that?
TarunKumarG
@TarunKumarG
No, I didnt check WhatDoIHave(), let me check now .
CarlosOnline
@CarlosOnline

Looking to use StructureMap in Azure Function to do multi tenancy. Found a sample that replaces the ServiceProvider in a Web Api (4 years old), wondering if that approach would work for Azure Functions.

Also is there a recent sample that works with .net core 3.1 Azure Functions?

Mark Warpool
@CodingGorilla
@CarlosOnline Consider using https://github.com/jasperfx/lamar instead, it's the official replacement for StructureMap. Although I can't speak to how it would work with Azure functions (haven't tried it).
TarunKumarG
@TarunKumarG
@jeremydmiller , i just verified with WhatDoIHave(), i have the dependencies which are needed in the container, but why it is explicitly asking us to register again ?
please suggest
Jeremy D. Miller
@jeremydmiller
I don’t know to be honest. There’s something else going on I can’t see.
TarunKumarG
@TarunKumarG
ok, Can anyone give any suggestions
Mark Warpool
@CodingGorilla
@TarunKumarG How does it "ask us to register again"?
TarunKumarG
@TarunKumarG
@CodingGorilla
i dont why but it is throwing below exception

Issue/Exception:
StructureMap.StructureMapConfigurationException: No default Instance is registered and cannot be automatically determined for type 'HM.Disco.PurchaseProduct.DataProviders.IPurchaseProductDataProvider'

There is no configuration specified for HM.Disco.PurchaseProduct.DataProviders.IPurchaseProductDataProvider

1.) new PreAdvicesApprovedEventHandler(Default of IPurchaseProductDataProvider, Default of ISecurityDataProvider, Default of IHmOrderUpdater, Default of IHmOrderUpdateEmailComposer, Default of IFeatureAccessManager)
2.) HM.Disco.ItOps.Gateway.HMOrder.PreAdvicesApprovedEventHandler
3.) Instance of HM.Disco.ItOps.Gateway.HMOrder.PreAdvicesApprovedEventHandler
4.) Container.GetInstance(HM.Disco.ItOps.Gateway.HMOrder.PreAdvicesApprovedEventHandler)

Jeremy D. Miller
@jeremydmiller
“cannot be automatically determined for type “ — might be the key thing there. Whatever that service is, does SM have all its dependencies? It might not be able to determine a usable constructor function
yuren1978
@yuren1978
Hello, we are using nested container in asp.net webapi (NOT .net core), and we are seeing ObjectDisposedException("StructureMap Nested Container") in the middle of http request, does anybody has any idea what will cause that.
we are using structure map through out the application, but it does seem only happening at one particular call, I don't see how this call is different with other calls, by the way, this only happens under the load.
we have tried to upgrade to latest version of structure map, and still seeing the same issue.
yuren1978
@yuren1978
Here is the exact error : Cannot access a disposed object. Object name: 'StructureMap Nested Container'. StackTrace: at StructureMap.Container.assertNotDisposed() at StructureMap.Container.GetInstanceT at * our application code.
Jeremy D. Miller
@jeremydmiller
Something is hanging on to the nested container when it shouldn’t be. Are you injecting the IContainer into something?
yuren1978
@yuren1978
yes, we inject into constructructor , so it can be used to resolve other instance with _container.GetInstance, and it's where it bombs, is this not the right way of doing it?
yuren1978
@yuren1978
There is a shared document library we use which takes IContainer in constructor, and we subsequently use that injected container to resolve other object
Ben Collins
@aggieben
I'm having some trouble getting .Inject() to work like I think it should with SM 4.7, and I was wondering if anyone here could offer some insight. For some reason, it appears as though child.Inject has no effect at all. I have a linqpad script here: https://gist.github.com/aggieben/a389b01b7ba637a265299c1b2f9337d0
Ben Collins
@aggieben
I'm still playing around with examples, but here's a pretty good simple repro:
public void TestSimpleDependency()
{
    /*
        Dependency hierarchy:
            IFooRepository
                => IFoo
    */

    var container = new Container(cfg =>
    {
       cfg.For<IFoo>().Singleton().Use<Foo>();
       cfg.For<IFooRepository>().Singleton().Use<FooRepository>();
    });

    var child = container.CreateChildContainer();

    var substituteFoo = Substitute.For<IFoo>();
    child.Inject(substituteFoo);

    var fooRepo = child.GetInstance<IFooRepository>();
    fooRepo.Foo.Should().BeSameAs(substituteFoo); // fails assertion here
}
Jeremy D. Miller
@jeremydmiller
I’d guess that the bug is due to the foo repository being a singleton. YOu might try it ContainerScoped() instead
Ben Collins
@aggieben
hmm. if I configure IFooRepository repository like this: cfg.For<IFooRepository>().ConatinerScoped().Use<FooRepository>(), then the test works
I also tested it with child.EjectAllInstances<IFooRepository>() in the singleton case, and it still failed
my problem here is that I'm going to be stuck with using singletons; that's not something I can change in the real code :-(