Roslyn+T4+EnvDTE项目完全自动化(1) ——类自动生成界面


前言

(视频演示 代码 提取码:mjl6)

Roslyn是什么?

XmlDocument,XDocument可以解析xml,同样 Roslyn 可解析项目中C#代码。c#常用插件ReSharper,只能重构一些很规范的代码(生成IEqualityComparer,IComparer接口...),用Roslyn可以自动化业务代码,可解决项目所有琐碎重复性操作,代码完全自动化 

自己写 代码助手

 一,类->视图->增删改查 全自动

需求:一个数据库所有表的增删改查

实现:Roslyn+T4实现类自动生成增删改查界面

  • 生成最终效果

  •  每个属性对应不同的控件:

  •  自动生成单个对象编辑预览

  

Roslyn+T4实现 

  1. EnvDTE获取当前打开项目
  2. Roslyn api CodeAnalysis 分析当前项目
  3. 获取代码Symbol
  4. T4代码自动化 
  • T4主要代码
<#@ include file="Include\base.t4" #>
<#@ include file="Include\CodeAnalysis.t4" #>
<#
    param.Task = DoAsync();
#>
<#+
    Dictionary<string, ClassEntry> dic;
    int depthLevel = 2;                         //嵌套类展开深度
    
    async Task<string> DoAsync()
    {
        var Modules = "Modules";
        var ns = "Test.Database";               //获取名称空间 Test.Database 所有类
        var DbContextName = "sakilaEntities";   //测试数据库
        var skipClass = new[]
        {
            DbContextName,
        }.ToHashSet();                          //排除类

        var modulesProjectItem = new ProjectItemEntry(@"Test\View\Modules");        //获取项目view的路径
        var viewModelProjectItem = new ProjectItemEntry(@"Test\ViewModel\Modules"); //获取项目ViewModel的路径
        /*
        modulesProjectItem.Delete();
        viewModelProjectItem.Delete();
        return "";
        */

        var analysisCore = await param.TryAnalysisSolutionAsync(cancellationToken); //Roslyn 分析当前工程
        var solution = analysisCore.Workspace.CurrentSolution;                      //Roslyn 的解决方案

        await TypeSymbols.InitializeTypeSymbolsAsync(solution, cancellationToken);

        //获取名称空间 Test.Database 所有类
        var list = await solution.GetAllSymbolsAsync((project, symbol) =>
        {
            var displayString = symbol.ContainingNamespace.ToDisplayString();
            if (displayString == ns && !skipClass.Contains(symbol.Name))
            {
                return true;
            }
            return false;
        });
        var items = list.Select(p =>
        {
            var entry = new ClassEntry(p);
            entry.DoProperty();
            return entry;
        }).ToList();
#>
  •  T4生成文本
<#+
//xaml
{
        var notMapHashSet = new HashSet<string>();  //每个属性类型对应的控件,如果没有映射,写日志,再运行
        double index = 0;
        dic = items.ToDictionary(p=> p.Name);
        foreach (var c in items)
        {
            index++;

            var xamlOutput = StartNewFile(modulesProjectItem.GetRelativePath(c.MainViewPair.XamlFileName), true); //打开一个新文件
            param.Log($@"{index / items.Count:P1}:{c.MainViewPair.ClassName}");
//xaml
#>
<UserControl
    x:Class="<#=modulesProjectItem.Namespace#>.<#=c.MainViewPair.ClassName#>"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
    xmlns:custcombobox="clr-namespace:Test.View.CustComboBox"
    xmlns:local="clr-namespace:<#=modulesProjectItem.Namespace#>"
    xmlns:vm="clr-namespace:<#=viewModelProjectItem.Namespace#>"
    d:DataContext="{d:DesignInstance IsDesignTimeCreatable=True,
                                     Type={x:Type vm:<#=c.MainViewPair.ViewModelClassName#>}}"
    d:DesignHeight="450"
    d:DesignWidth="800"
    mc:Ignorable="d"
    >
..........................................
  • 类型->控件映射
<#+ if(type.IsType(SpecialType.System_String)){#>
                <DataGridTextColumn
                    Width="*"
                    Binding="{Binding Result.<#=property.Name #>, UpdateSourceTrigger=PropertyChanged}"
                    Header="<#=property.NameZh #>"
                    >
                    <DataGridTextColumn.ElementStyle>
                        <Style TargetType="TextBlock" BasedOn="{StaticResource {x:Type TextBlock}}">
                            <Setter Property="ToolTip" Value="<#=property.Name#>" />
                        </Style>
                    </DataGridTextColumn.ElementStyle>
                </DataGridTextColumn>
<#+ } else if(type.IsType(SpecialType.System_Int32)){#>
                <DataGridTemplateColumn Width="*" Header="<#=property.NameZh #>">
                    <DataGridTemplateColumn.CellEditingTemplate>
                        <DataTemplate>
                            <xctk:IntegerUpDown ToolTip="<#=property.Name#>" Value="{Binding Result.<#=property.Name #>, UpdateSourceTrigger=PropertyChanged}">
                            </xctk:IntegerUpDown>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellEditingTemplate>
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock ToolTip="<#=property.Name#>" Text="{Binding Result.<#=property.Name #>, Mode=OneWay}" />
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
<#+ } else if(type.IsType(SpecialType.System_Byte)){#>
                <DataGridTemplateColumn Width="*" Header="<#=property.NameZh #>">
                    <DataGridTemplateColumn.CellEditingTemplate>
                        <DataTemplate>
                            <xctk:ByteUpDown ToolTip="<#=property.Name#>" Value="{Binding Result.<#=property.Name #>, UpdateSourceTrigger=PropertyChanged}">
                            </xctk:ByteUpDown>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellEditingTemplate>
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock ToolTip="<#=property.Name#>" Text="{Binding Result.<#=property.Name #>, Mode=OneWay}" />
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
<#+ } else if(type.IsType(SpecialType.System_Decimal)){#>
........
  • t4涉及类
<#+
public class XamlCsPair
{
    public readonly string ClassName = string.Empty;            //类名
    public readonly string XamlFileName = string.Empty;         //View绝对路径
    public readonly string CsFileName = string.Empty;           //ViewModel绝对路径

    public readonly string ViewModelClassName = string.Empty;   //View类名
    public readonly string ViewModelFileName = string.Empty;    //ViewModel 文件名
    public XamlCsPair(string className, string vewModelClassName)
    {
        ClassName = className;
        className = className.GetValidFileName("_");            //类名到文件名,移除非法字符

        XamlFileName = $"{className}.xaml";
        CsFileName = $"{XamlFileName}.cs";
        ViewModelClassName = vewModelClassName;
        ViewModelFileName = $"{ViewModelClassName}.cs";
    }
}
public class ClassEntry
{
    public ProjectSymbolEntry Entry { get; }
    public INamedTypeSymbol Symbol { get; }             //类Symbol
    public readonly string Name = string.Empty;         //类的名称
    public readonly string NameZh = string.Empty;       //类的中文名称
    public readonly ITypeSymbol Type;                   //类的类型Symbol
    public readonly string TypeName = string.Empty;     //类的类型名称

    public readonly string ClassName = string.Empty;    //目标类名
    
    public readonly XamlCsPair MainViewPair;
    public readonly XamlCsPair EditViewPair;
    
    public List<PropertyEntry> Properties { get; set; } = new List<PropertyEntry>();//类的属性集合
    public ClassEntry(ProjectSymbolEntry entry)
    {
        Entry = entry;
        var symbol = entry.Symbol;
        Symbol = symbol;
        Name = symbol.Name;
        NameZh = Name.ToZh(cacheMode: CacheMode.OneWay); //百度api英文翻译中文
        Type = symbol.GetTypeSymbol();
        TypeName = Type.GetDisplayShortName();

        ClassName = Name.ToValidIdentifier(removeChars:"_".ToCharArray());
        MainViewPair = new XamlCsPair($"{ClassName}View", $"{ClassName}ViewModel");
        EditViewPair = new XamlCsPair($"{ClassName}ViewEditorWindow", $"{ClassName}ViewEditorViewModel");
    }

    public void DoProperty()
    {
        Properties.Clear();
        foreach (var member in Symbol.GetMembers().OfType<IPropertySymbol>())
        {
            Properties.Add(new PropertyEntry(this, member));
        }
    }
}
public class PropertyEntry
{
    public ClassEntry Class { get; }
    public IPropertySymbol Symbol { get; }          //属性的Symbol
    public readonly string Name = string.Empty;
    public readonly string NameZh = string.Empty;   //属性中文名称
    public readonly ITypeSymbol Type;               //属性类型
    public readonly string TypeName = string.Empty; //属性类型名称

    public PropertyEntry(ClassEntry entry, IPropertySymbol symbol)
    {
        Class = entry;
        Symbol = symbol;
        Name = symbol.Name;
        NameZh = Name.ToZh(cacheMode: CacheMode.OneWay);//百度api英文翻译中文
        Type = symbol.GetTypeSymbol();                  //属性Roslyn类型
        TypeName = Type.GetDisplayShortName();          //属性Roslyn名称
    }
}
public class ProjectItemEntry
{
    public ProjectItem ProjectItem { get; } //dte对应的一个项目文件
    public string Namespace { get; }        //ProjectItem的命名空间
    public string FileName { get; }         //项目文件绝对路径

    public ProjectItemEntry(string projectRelativePath)
    {
        ProjectItem = dte.GetProjectItemByRelativePath(projectRelativePath).TryCreateDir();
        Namespace = (string)ProjectItem.Properties.Item("DefaultNamespace").Value;
        FileName = ProjectItem.GetFileName();

    }
    public void Delete()
    {
        ProjectItem.DeleteChildren();
        FileName.DeleteSubFiles();
    }
    public string GetRelativePath(string relativePath)
    {
        return Path.Combine(FileName, relativePath);
    }
}
#>

  • 自动生成项目文件

文章来源:https://www.cnblogs.com/metoget/p/15611564.html

版权声明:本文为YES开发框架网发布内容,转载请附上原文出处连接
管理员
上一篇:面试官:如果存取IP地址,用什么数据类型比较好 (C#版本)
下一篇:二进制与二进制运算
评论列表

发表评论

评论内容
昵称:
关联文章

Roslyn+T4+EnvDTE项目完全自动化(1) ——自动生成界面
Roslyn+T4+EnvDTE项目完全自动化(3) ——生成c++代码
.NET Core 运行时T4模板使用,T4生成代码
bat脚本:生成项目自动加壳发布
C# 使用Roslyn编译项目 .csproj
C# Roslyn编译csproj项目 使用Roslyn编译整个C#项目
利用代码生成工具Database2Sharp生成ABP VNext框架项目代码
FastReport完全破解--asp.net使用签名问题
微信授权连接URL生成
ABP VNext框架基础知识介绍(1)--框架基础继承关系
.NET 微服务——CI/CD(1):Jenkins+Gitee自动构建
.NET中大型项目开发必备(1)--UUID全球通用唯一识别码
生成随机数
分享一个自研开发的QA自动化审计工具-Sonar检查
.NET C#教程初级篇 1-1 基本数据类型及其存储方式
.NET中大型项目开发必备(4)--数据库的读写分离
.net core自动构建版本号
Visual Studio(VS)发布自动使用Net Reactor给生成的DLL加壳
Asp.NetCore3.1开源项目升级为.Net6.0
C#的进化——C#发展史、C#1.0-10.0语法系统性梳理、C#与JAVA的对比

联系我们
联系电话:15090125178(微信同号)
电子邮箱:garson_zhang@163.com
站长微信二维码
微信二维码