65.9K
CodeProject 正在变化。 阅读更多。
Home

Visual Studio 可视化工具:第 3 部分 - 集合可视化工具

starIconstarIconstarIconstarIconstarIcon

5.00/5 (5投票s)

2013年6月10日

CPOL

2分钟阅读

viewsIcon

20477

该项目为 .NET 集合创建 Visual Studio 可视化工具

介绍 

该项目为 .NET 列表集合创建 Visual Studio 可视化工具:List<T>

项目网站:http://vsdatawatchers.codeplex.com

背景

第 1 部分在此处提供:https://codeproject.org.cn/Articles/578777/Visual-Studio-Visualizer-Part-1

如果您不熟悉 Visual Studio 可视化工具,我建议阅读第 1 部分。

第 2 部分在此处提供:https://codeproject.org.cn/Articles/584739/Visual-Studio-Visualizer-Part-2-Entity-Framework

使用代码

关于 List<> 的反射

为了将列表中的值读取到可视化工具中,使用了简单的 DTO

[Serializable]
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 属性(如字符串、整数等)的容器

[Serializable]
public class EntityProperties
{
    public string PropertyName { get; set; }
    public object Value { get; set; }
    public string PropertyType { get; set; }
}

反射方法遍历列表中的所有条目,并填充一个 List<Entity>,然后将其传递给可视化工具

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 来读取深度,此值可以由用户在可视化工具中持久化。将此值存储在属性项目中不起作用,返回的值始终为 0,并且使用 XmlSerializer 读取文件也返回 0,因此 XDocument 是一个解决方法。

使用可视化工具

第 1 部分 中,详细解释了如何使用可视化工具。

可视化工具以树状视图显示信息,所有复杂属性都作为子节点。呈现的信息是属性的名称、其值和 Type

可以过滤信息,这在包含大量项目的列表中至关重要。可以通过名称、值或类型进行过滤

可以更改深度,即显示的复杂属性(子项)。出于性能原因,存在此限制,测试是使用实体框架进行的,如果不使用深度限制,可视化工具可以一次检索所有数据库,一个属性一个属性!!! 要更改使用的深度,请更改深度文本框中的值并重新启动可视化工具。

对于更详细的操作,可以将数据导出到 XML 和 Excel

导出到 Excel 文件保留了树状层次结构

此第一个版本 v20130601 不允许更改值,将在下一个版本中实现。

历史

  • 2013-06-03:文章创建。
© . All rights reserved.