/// <summary>
        /// Adds the interface layer for the given instance to the dynamic binding context.
        /// </summary>
        public static void AddInterfaceLayer(this IDynamicBindingContext context, IProductElement element)
        {
            Guard.NotNull(() => context, context);
            Guard.NotNull(() => element, element);

            //TODO: If we ever allow automation to be defined on an extension point,
            // then for every pattern that implments and extension point, we must add a key to the cache for that extensionPoint.DefinitionId
            var layer = default(object);
            var key   = new ToolkitInterfaceLayerCacheKey(element, element.DefinitionId);

            if (element.Root != null &&
                element.Root.ProductState != null &&
                element.Root.ProductState.PropertyBag != null &&
                !element.Root.ProductState.PropertyBag.TryGetValue(key, out layer))
            {
                layer = GetInterfaceLayer(element);
                if (layer != null)
                {
                    element.Root.ProductState.PropertyBag[key] = layer;
                }
            }

            if (layer != null)
            {
                context.AddExportsFromInterfaces(layer);
            }
        }
        /// <summary>
        /// Attempts to retrieve (from cache or by creating and caching it the first time)
        /// the typed interface layer proxy defined by the runtime instance, without knowing
        /// its type. This is used in the add rules to initialize the interface layers.
        /// </summary>
        public static IToolkitInterface GetInterfaceLayer(this IInstanceBase instance)
        {
            Guard.NotNull(() => instance, instance);

            if (instance.Info == null)
            {
                return(null);
            }

            var key      = new ToolkitInterfaceLayerCacheKey(instance, instance.DefinitionId);
            var layer    = default(object);
            var canCache = instance.Root != null &&
                           instance.Root.ProductState != null &&
                           instance.Root.ProductState.PropertyBag != null;

            if (canCache && instance.Root.ProductState.PropertyBag.TryGetValue(key, out layer))
            {
                return((IToolkitInterface)layer);
            }

            var interfaceService = default(IToolkitInterfaceService);

            if (!canCache || (interfaceService = instance.Root.ProductState.TryGetService <IToolkitInterfaceService>()) == null)
            {
                tracer.Warn(Resources.ToolkitInterfaceLayer_ServiceUnavailable);
                return(null);
            }

            var definitionId = Guid.Empty;
            var proxyType    = interfaceService.AllInterfaces
                               .Where(export => !string.IsNullOrEmpty(export.Metadata.DefinitionId) && Guid.TryParse(export.Metadata.DefinitionId, out definitionId))
                               .Where(export =>
                                      export.Metadata.ExtensionId == instance.Info.GetRoot().ExtensionId&&
                                      definitionId == instance.DefinitionId)
                               .Select(export => export.Metadata.ProxyType)
                               .FirstOrDefault();

            if (proxyType != null)
            {
                try
                {
                    layer = Activator.CreateInstance(proxyType, instance);
                    if (layer != null && canCache)
                    {
                        instance.Root.ProductState.PropertyBag[key] = layer;
                    }

                    return(layer as IToolkitInterface);
                }
                catch (System.Reflection.TargetInvocationException tie)
                {
                    tracer.Error(tie.InnerException, Resources.ToolkitInterfaceLayer_TraceFailedInstantiation, proxyType);
                }
            }

            return(null);
        }
        /// <summary>
        /// Retrieves a cached interface layer proxy or creates a new one using
        /// the toolkit specified and caches it the first time.
        /// </summary>
        private static TInterface GetInterfaceLayer <TInterface>(IInstanceBase element, Func <TInterface> toolkit)
            where TInterface : class
        {
            var typed = element as TInterface;

            if (typed != null)
            {
                return(typed);
            }

            // We'll never be able to cache in this case.
            if (element.Root == null ||
                element.Root.ProductState == null ||
                element.Root.ProductState.PropertyBag == null)
            {
                return(toolkit());
            }

            var layer = default(object);
            var key   = new ToolkitInterfaceLayerCacheKey(element, element.DefinitionId);

            if (element.Root.ProductState.PropertyBag.TryGetValue(key, out layer))
            {
                return(layer as TInterface);
            }
            else
            {
                var result = toolkit();
                if (result != default(TInterface))
                {
                    element.Root.ProductState.PropertyBag[key] = result;
                }

                return(result);
            }
        }