/// <summary> /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// </summary> public static string ToDebugString([NotNull] this IStateManager stateManager, StateManagerDebugStringOptions options) { var builder = new StringBuilder(); foreach (var entry in stateManager.Entries.OrderBy(e => e, EntityEntryComparer.Instance)) { builder.AppendLine(entry.ToDebugString(options)); } return(builder.ToString()); }
/// <summary> /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// </summary> public static string ToDebugString( [NotNull] this IStateManager stateManager, StateManagerDebugStringOptions options, [NotNull] string indent = "") { var builder = new StringBuilder(); 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 = stateManager.TryGetEntry(value, targetType, throwOnTypeMismatch: false); builder.Append( otherEntry == null ? "<not found>" : otherEntry.BuildCurrentValuesString(targetType.FindPrimaryKey().Properties)); } foreach (var item in stateManager.Entries .Select( e => new { KeyString = e.BuildCurrentValuesString(e.EntityType.FindPrimaryKey().Properties), Entry = e }) .OrderBy(e => e.Entry.EntityType.DisplayName()) .ThenBy(e => e.KeyString)) { var entry = item.Entry; var entityType = entry.EntityType; builder .Append(entityType.DisplayName()) .Append(' ') .Append(item.KeyString) .Append(' ') .Append(entry.EntityState.ToString()); if ((options & StateManagerDebugStringOptions.IncludeProperties) != 0) { builder.AppendLine(); foreach (var property in entityType.GetProperties()) { 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); } } builder.AppendLine(); } } else { foreach (var alternateKey in entityType.GetKeys().Where(k => !k.IsPrimaryKey())) { builder .Append(" AK ") .Append(entry.BuildCurrentValuesString(alternateKey.Properties)); } foreach (var foreignKey in entityType.GetForeignKeys()) { builder .Append(" FK ") .Append(entry.BuildCurrentValuesString(foreignKey.Properties)); } builder.AppendLine(); } if ((options & StateManagerDebugStringOptions.IncludeNavigations) != 0) { foreach (var navigation in entityType.GetNavigations()) { var currentValue = entry.GetCurrentValue(navigation); var targetType = navigation.GetTargetType(); builder .Append(" ") .Append(navigation.Name) .Append(": "); if (currentValue == null) { builder.Append("<null>"); } else if (navigation.IsCollection()) { builder.Append('['); var relatedEntities = ((IEnumerable)currentValue).Cast <object>().Take(33).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); } builder.AppendLine(); } } } return(builder.ToString()); }