Visual Studio的Visualizer工具是开发者在调试过程中的得力助手,它能够帮助开发者以图形化的方式查看对象的内部结构,从而更直观地理解数据。本文将详细介绍Visual Studio Visualizer的背景、使用方法、代码实现以及历史版本信息。
Visual StudioVisualizer是Visual Studio调试器的一部分,它允许开发者在调试过程中查看对象的属性和值。如果对Visual Studio Visualizer不熟悉,建议先阅读。第二部分则介绍了与Entity Framework结合使用的情况,可以在查看。
在Visual StudioVisualizer中,使用反射来读取列表中的值,并将其传递给Visualizer。这里使用了简单的数据传输对象(DTO):
public class Entity
{
public Guid Pk = Guid.NewGuid();
public string Name { get; set; }
List<EntityProperties> properties;
public List<EntityProperties> Properties
{
get
{
if (properties == null)
properties = new List<EntityProperties>();
return properties;
}
set { properties = value; }
}
List<Entity> nestedEntities;
public List<Entity> NestedEntities
{
get
{
if (nestedEntities == null)
nestedEntities = new List<Entity>();
return nestedEntities;
}
set { nestedEntities = value; }
}
}
此类包含一个名称、一个属性列表以及一个称为NestedEntities的复杂属性列表。EntityProperties是一个容器,用于存储简单的.NET属性,如字符串、整数等:
public class EntityProperties
{
public string PropertyName { get; set; }
public object Value { get; set; }
public string PropertyType { get; set; }
}
反射方法遍历列表中的所有实体,并填充一个Entity列表,然后将其传递给Visualizer:
private void WriteObject(object o, Entity currentEntity)
{
if (o is IEnumerable)
{
foreach (object element in (IEnumerable)o)
{
if (element is IEnumerable && !(element is string))
{
if (level < depth)
{
level++;
WriteObject(element, currentEntity);
level--;
}
}
else
WriteObject(element, currentEntity);
}
}
else
{
// reads the object members
MemberInfo[] members = o.GetType().GetMembers(
BindingFlags.Public | BindingFlags.Instance);
Entity newEntity = new Entity();
newEntity.Name = o.ToString();
// we are only interested in properties and fields
foreach (MemberInfo m in members.Where(p => p is FieldInfo || p is PropertyInfo))
{
FieldInfo f = m as FieldInfo;
PropertyInfo p = m as PropertyInfo;
if (f != null || p != null)
{
Type t = f != null ? f.FieldType : p.PropertyType;
// reads the properties values, name value and type
EntityProperties property = new EntityProperties();
property.PropertyName = m.Name;
property.PropertyType = t.FullName;
if (t.IsValueType || t == typeof(string))
{
try
{
// reads the value
property.Value = f != null ? f.GetValue(o) : p.GetValue(o, null);
newEntity.Properties.Add(property);
}
catch { }
}
}
}
// add to new entity or as a nested entity
if (currentEntity == null)
entities.Add(newEntity);
else
currentEntity.NestedEntities.Add(newEntity);
// where we read the complex properties until the defined depth
if (level < depth)
{
foreach (MemberInfo m in members.Where(p => p is FieldInfo || p is PropertyInfo))
{
FieldInfo f = m as FieldInfo;
PropertyInfo p = m as PropertyInfo;
if (f != null || p != null)
{
Type t = f != null ? f.FieldType : p.PropertyType;
if (!(t.IsValueType || t == typeof(string)))
{
try
{
object value = f != null ? f.GetValue(o) : p.GetValue(o, null);
if (value != null)
{
level++;
WriteObject(value, newEntity);
level--;
}
}
catch { }
}
}
}
}
}
}
对于列表中的每个条目,都使用递归,因为每个复杂的相关实体都需要被可视化。这个版本仅支持List<>的可视化,但考虑到未来版本的扩展,创建了一个扩展方法:
public static List<Entity> DumpMe(this Object o, int depth = 2)
{
string filename = System.IO.Path.Combine(Environment.GetFolderPath(
Environment.SpecialFolder.ApplicationData),
"CodeVisualizer",
"CodeVisualizerOptions.xml");
if (File.Exists(filename))
{
XDocument doc = XDocument.Load(filename);
XElement xe = doc.Descendants("Depth").FirstOrDefault();
if (xe != null)
int.TryParse(xe.Value, out depth);
}
return Dump.ReadObject(o, depth).entities;
}
Dump.ReadObject方法简单地调用了上面描述的方法。这里使用了XDocument来读取深度值,这个值可以由用户在Visualizer中持久化。将这个值存储在属性项目中不起作用,因为返回的值总是0,使用XmlSerializer读取文件也返回0,所以XDocument是一个变通方法。
在本系列的中,对如何使用Visualizer进行了所有解释。Visualizer以树状视图显示信息,所有复杂属性作为子节点。显示的信息包括属性的名称、值和类型。
可以按名称、值或类型过滤信息,这对于包含大量项目的列表至关重要。过滤可以通过名称、值或类型进行:
还可以更改显示的深度,即显示的复杂属性(子节点)。这个限制是出于性能原因,测试是在不使用深度限制的情况下使用实体框架进行的,Visualizer可能会逐个属性地检索整个数据库!要更改使用的深度,请在深度文本框中更改值并重新启动Visualizer。
对于更详细的操作,可以将数据导出到XML和Excel: