2

Closed

View current instances in the Container

description

As an application grows so does the number of execution paths. Having the ability to review what and how many classes have been instantiated at any point of a lifetime would be helpful, as would being able to interrogate the internal state of any active instance.
Closed Oct 16, 2013 at 8:52 PM by dot_NET_Junkie

comments

dot_NET_Junkie wrote Jun 8, 2013 at 9:30 AM

How do you want to view those items? Some specific API you are thinking of? Or integrated in the debugger view (just as the diagnostic services are)?

There are two things that trouble me with such feature:
  1. Items must be both added and removed from the 'live instances' list and this has to be done in a deterministic way. Maintaining a list of active instance impacts performance.
  2. Transient instances are not tracked/cached by the container and don't have any reasonable 'scope' and adding them to a 'live instances' list is probably not feasible.

dot_NET_Junkie wrote Jun 8, 2013 at 9:33 AM

If you're not worried about performance, you can use the following workaround:

    private static IDictionary<Type, object[]> LiveInstances
    {
        get { return BuildLiveInstances(); }
    }

    private static readonly ConcurrentDictionary<Type, List<WeakReference>> liveInstances =
        new ConcurrentDictionary<Type, List<WeakReference>>();

    private static readonly Predicate<WeakReference> IsDeadReference = weak => !weak.IsAlive;
    private static readonly Func<WeakReference, bool> IsLiveReference = weak => weak.IsAlive;
    private static readonly Func<Type, List<WeakReference>> valueFactory =
        type => new List<WeakReference>();

    private static void TrackLiveInstance<TService>(TService instance)
    {
        var references = liveInstances.GetOrAdd(typeof(TService), valueFactory);
        
        lock (references)
        {
            if (references.Count > 100)
            {
                references.RemoveAll(IsDeadReference);
            }

            references.Add(new WeakReference(instance));
        }
    }

    private static object[] GetLiveInstancesFor(Type serviceType)
    {
        List<WeakReference> references;
        
        if (!liveInstances.TryGetValue(serviceType, out references))
        {
            return new object[0];
        }

        lock (references)
        {
            return (
                from reference in references
                let instance = reference.Target
                where instance != null
                select instance)
                .ToArray();
        }
    }

    private static IDictionary<Type, object[]> BuildLiveInstances()
    {
        return (
            from serviceType in liveInstances.Keys
            let instances = GetLiveInstancesFor(serviceType)
            where instances.Any()
            select new { serviceType, instances })
            .ToDictionary(x => x.serviceType, x => x.instances);
    }
}
You can use this code by registering an initializer for every type you wish to track. For instance:
container.RegisterInitializer<IUnitOfWork>(TrackLiveInstance);
By hovering over the static LiveInstances property you can see which instances are created by the container and are not yet garbage collected.

qujck wrote Jun 10, 2013 at 2:33 PM

I see this as a two pronged request:
  1. to be able to view any active objects (i.e. not transient) within the current lifetime scope - i.e. any current objects that would be injected into a service requesting an instance of that type (within the current lifetime). Being able to list the items in the debugger view would be ideal.
  2. to be able to see a list of all service requests during a lifetime scope - to get a feel for how many types have been instantiated and, more importantly, how many transient instances of the same service have been created. A journal of service requests for the current lifetime.

wrote Oct 1, 2013 at 8:15 PM

Associated with changeset 103612: Added an InstanceCreated event to the container to allow users to get notified of the creation of instances. Using some custom code, the currently active instance of the container can be viewed.

wrote Oct 5, 2013 at 2:14 PM

Associated with changeset 103689: Errors in XML fixed.

wrote Oct 16, 2013 at 8:52 PM

Resolved with changeset 103917: Changes.txt updated.

wrote Oct 27, 2013 at 6:11 PM

Associated with changeset 104185: The Container.InstanceCreated event has been refactored to a new RegisterInitializer overload. This makes the API more concise.