示例#1
0
        /// <summary>
        ///     <para>
        ///         Creates a human-readable representation of the given metadata.
        ///     </para>
        ///     <para>
        ///         Warning: Do not rely on the format of the returned string.
        ///         It is designed for debugging only and may change arbitrarily between releases.
        ///     </para>
        /// </summary>
        /// <param name="changeTracker"> The metadata item. </param>
        /// <param name="options"> Options for generating the string. </param>
        /// <param name="indent"> The number of indent spaces to use before each new line. </param>
        /// <returns> A human-readable representation. </returns>
        public static string ToDebugString(
            this ChangeTracker changeTracker,
            ChangeTrackerDebugStringOptions options = ChangeTrackerDebugStringOptions.LongDefault,
            int indent = 0)
        {
            var builder      = new StringBuilder();
            var indentString = new string(' ', indent);

            var stateManager = changeTracker.Context.GetService <IStateManager>();

            foreach (var entry in stateManager.Entries.OrderBy(e => e, EntityEntryComparer.Instance))
            {
                builder.Append(indentString).AppendLine(entry.ToDebugString(options, indent));
            }

            return(builder.ToString());
        }
示例#2
0
        /// <summary>
        ///     <para>
        ///         Creates a human-readable representation of the given <see cref="IUpdateEntry" />.
        ///     </para>
        ///     <para>
        ///         Warning: Do not rely on the format of the returned string.
        ///         It is designed for debugging only and may change arbitrarily between releases.
        ///     </para>
        /// </summary>
        /// <param name="updateEntry"> The entry. </param>
        /// <param name="options"> Options for generating the string. </param>
        /// <param name="indent"> The number of indent spaces to use before each new line. </param>
        /// <returns> A human-readable representation. </returns>
        public static string ToDebugString(
            this IUpdateEntry updateEntry,
            ChangeTrackerDebugStringOptions options,
            int indent = 0)
        {
            var builder      = new StringBuilder();
            var indentString = new string(' ', indent);

            var entry = (InternalEntityEntry)updateEntry;

            var keyString = entry.BuildCurrentValuesString(entry.EntityType.FindPrimaryKey() !.Properties);

            builder
            .Append(entry.EntityType.DisplayName())
            .Append(' ')
            .Append(entry.SharedIdentityEntry != null ? "(Shared) " : "")
            .Append(keyString)
            .Append(' ')
            .Append(entry.EntityState.ToString());

            if ((options & ChangeTrackerDebugStringOptions.IncludeProperties) != 0)
            {
                foreach (var property in entry.EntityType.GetProperties())
                {
                    builder.AppendLine().Append(indentString);

                    var currentValue = entry.GetCurrentValue(property);
                    builder
                    .Append("  ")
                    .Append(property.Name)
                    .Append(": ");

                    AppendValue(currentValue);

                    if (property.IsPrimaryKey())
                    {
                        builder.Append(" PK");
                    }
                    else if (property.IsKey())
                    {
                        builder.Append(" AK");
                    }

                    if (property.IsForeignKey())
                    {
                        builder.Append(" FK");
                    }

                    if (entry.IsModified(property))
                    {
                        builder.Append(" Modified");
                    }

                    if (entry.HasTemporaryValue(property))
                    {
                        builder.Append(" Temporary");
                    }

                    if (entry.HasOriginalValuesSnapshot &&
                        property.GetOriginalValueIndex() != -1)
                    {
                        var originalValue = entry.GetOriginalValue(property);
                        if (!Equals(originalValue, currentValue))
                        {
                            builder.Append(" Originally ");
                            AppendValue(originalValue);
                        }
                    }
                }
            }
            else
            {
                foreach (var alternateKey in entry.EntityType.GetKeys().Where(k => !k.IsPrimaryKey()))
                {
                    builder
                    .Append(" AK ")
                    .Append(entry.BuildCurrentValuesString(alternateKey.Properties));
                }

                foreach (var foreignKey in entry.EntityType.GetForeignKeys())
                {
                    builder
                    .Append(" FK ")
                    .Append(entry.BuildCurrentValuesString(foreignKey.Properties));
                }
            }

            if ((options & ChangeTrackerDebugStringOptions.IncludeNavigations) != 0)
            {
                foreach (var navigation in entry.EntityType.GetNavigations()
                         .Concat <INavigationBase>(entry.EntityType.GetSkipNavigations()))
                {
                    builder.AppendLine().Append(indentString);

                    var currentValue = entry.GetCurrentValue(navigation);
                    var targetType   = navigation.TargetEntityType;

                    builder
                    .Append("  ")
                    .Append(navigation.Name)
                    .Append(": ");

                    if (currentValue == null)
                    {
                        builder.Append("<null>");
                    }
                    else if (navigation.IsCollection)
                    {
                        builder.Append('[');

                        const int maxRelatedToShow = 32;
                        var       relatedEntities  = ((IEnumerable)currentValue).Cast <object>().Take(maxRelatedToShow + 1).ToList();

                        for (var i = 0; i < relatedEntities.Count; i++)
                        {
                            if (i != 0)
                            {
                                builder.Append(", ");
                            }

                            if (i < 32)
                            {
                                AppendRelatedKey(targetType, relatedEntities[i]);
                            }
                            else
                            {
                                builder.Append("...");
                            }
                        }

                        builder.Append(']');
                    }
                    else
                    {
                        AppendRelatedKey(targetType, currentValue);
                    }
                }
            }

            return(builder.ToString());

            void AppendValue(object?value)
            {
                if (value == null)
                {
                    builder.Append("<null>");
                }
                else if (value.GetType().IsNumeric())
                {
                    builder.Append(value);
                }
                else if (value is byte[] bytes)
                {
                    builder.AppendBytes(bytes);
                }
                else
                {
                    var stringValue = value.ToString();
                    if (stringValue?.Length > 63)
                    {
                        stringValue = stringValue.Substring(0, 60) + "...";
                    }

                    builder
                    .Append('\'')
                    .Append(stringValue)
                    .Append('\'');
                }
            }

            void AppendRelatedKey(IEntityType targetType, object value)
            {
                var otherEntry = entry.StateManager.TryGetEntry(value, targetType, throwOnTypeMismatch: false);

                builder.Append(
                    otherEntry == null
                        ? "<not found>"
                        : otherEntry.BuildCurrentValuesString(targetType.FindPrimaryKey() !.Properties));
            }
        }