/// <summary>
        /// Serializes this node to DOT graph format. This is generally only done after
        /// the overall resolve operation is complete.
        /// </summary>
        /// <param name="stringBuilder">
        /// The <see cref="StringBuilder"/> to which the node content should be written.
        /// </param>
        /// <param name="allRequests">
        /// A <see cref="RequestDictionary"/> that contains all the other resolve requests
        /// that occurred in the complete resolve operation. It is used to ensure
        /// graph edges are drawn to the correct destination service with the proper
        /// success/failure formatting.
        /// </param>
        public void ToString(StringBuilder stringBuilder, RequestDictionary allRequests)
        {
            var shape           = DecoratorTarget == null ? "component" : "box3d";
            var allServiceTypes = Services.Keys.OfType <IServiceWithType>().Select(s => s.ServiceType).ToArray();
            var deferred        = allServiceTypes.Contains(typeof(ILifetimeScope)) || allServiceTypes.Contains(typeof(IComponentContext));

            stringBuilder.StartNode(Id, shape, Success, deferred);
            foreach (var service in Services.Keys)
            {
                stringBuilder.AppendServiceRow(service.GraphDisplayName(), Services[service]);
            }

            stringBuilder.AppendTableRow(TracerMessages.ComponentDisplay, Component);

            if (DecoratorTarget is object)
            {
                stringBuilder.AppendTableRow(TracerMessages.TargetDisplay, DecoratorTarget);
            }

            // Only write the instance info IF
            // - There IS an instance AND
            //   - There's more than one service exposed (which means there's at least one service)
            //     not matching the instance type OR
            //   - There's only one service exposed and that one doesn't match the instance type.
            if (Instance is object &&
                (Services.Count != 1 || !(Services.First().Key is IServiceWithType swt) || swt.ServiceType != Instance.GetType()))
            {
                stringBuilder.AppendTableRow(TracerMessages.InstanceDisplay, Instance.GetType().CSharpName());
            }

            if (Exception is object)
            {
                stringBuilder.AppendTableErrorRow(Exception.GetType().CSharpName(), Exception.Message);
            }

            if (deferred)
            {
                stringBuilder.AppendTableRow(TracerMessages.DeferredOperation);
            }

            stringBuilder.EndNode();
            foreach (var edge in Edges)
            {
                // Connect into a table with the ID format "parent:tablerow"
                var destination = allRequests[edge.Request];
                var edgeId      = destination.Id.NodeId() + ":" + destination.Services[edge.Service].NodeId();

                // Shorter type name for line descriptions where possible. Using
                // the type name here rather than the C# name because C# will
                // include the namespace and will be longer than raw type name.
                // It will still be "pretty printed" in the individual nodes.
                var description = edge.Service is IServiceWithType edgeSwt ? edgeSwt.ServiceType.Name : edge.Service.Description;
                stringBuilder.ConnectNodes(Id.NodeId(), edgeId, description, !destination.Success);
            }
        }
 /// <summary>
 /// Initializes a new instance of the <see cref="DotGraphBuilder"/> class.
 /// </summary>
 public DotGraphBuilder()
 {
     Operation      = new OperationNode();
     Requests       = new RequestDictionary();
     CurrentRequest = new Stack <Guid>();
 }