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生成代码
C# 使用Roslyn编译项目 .csproj
bat脚本:生成项目自动加壳发布
C# Roslyn编译csproj项目 使用Roslyn编译整个C#项目
利用代码生成工具Database2Sharp生成ABP VNext框架项目代码
FastReport完全破解--asp.net使用签名问题
微信授权连接URL生成
ABP VNext框架基础知识介绍(1)--框架基础继承关系
C#实现INotifyPropertyChanged的优雅封装方案(含简洁写法与自动生成
.NET 微服务——CI/CD(1):Jenkins+Gitee自动构建
.NET中大型项目开发必备(1)--UUID全球通用唯一识别码
生成随机数
分享一个自研开发的QA自动化审计工具-Sonar检查
.NET C#教程初级篇 1-1 基本数据类型及其存储方式
Asp.NetCore3.1开源项目升级为.Net6.0
C#的进化——C#发展史、C#1.0-10.0语法系统性梳理、C#与JAVA的对比
Visual Studio(VS)发布自动使用Net Reactor给生成的DLL加壳
验证码生成

热门标签
.NET Core .NET Reactor ag-grid AI发布 api安全 ASP.NET Core C#DLL加密 C#播放声音 C#代码混淆 C#代码加密 ChromeDriver Codex DateTime DBeaver devexpress devTool DLL混淆 edge.js EF EFCore Electron element-ui el-form el-table excel FastReport FileStream FolderBrowerDialog FolderSelectDialog form提交 git gridcontrol gridview input javascript json字符串 JS转换对象JSON jwt JWT授权 linq log Math MCP mitmproxy MVC MySQL Navicat netstat nginx node_modules NSwag Nuget Nuget镜像 number PowerShell pyinstaller python pythoncom python爬虫 python抓包 pywin32 redis Requests-html RestSharp Selenium sql SQL Server Swagger to-cms Visual Studio VSCode vue VueRouter vue路由 VUE页面通讯 Webpack Windows Windows服务 winform wmi xlrd yaml YESCMS YESWEB开发框架 白象 表单提交 播放声音 打开URL 代码混淆 弹窗提醒 端口占用 对象转换 分布式 公共字典 机器码 进程排查 静态资源 开发指南 路由参数 密钥 配置教程 配置文件 权限 人工智能 任务 任务调度 日期间隔 日志 日志记录 省市区 授权验证 数据库 四舍五入 文案 文件读取 文件夹选择 文件目录选择 问题排查 行政区域数据 页面通讯 中间件 CSharp 事务锁 工单系统 并发控制 重复提交 CMS Markdig Markdown markdown-it marked 技术选型 VS Code 开发工具 源代码管理 版本控制 Docker PostgreSQL 时区 部署排查 CMS架构 EF Core 主题系统 二次开发 插件系统 容器 运维命令 镜像清理 Linux NAS 远程挂载 飞牛 fnOS S/4HANA SAP GUI SAP HANA SAP R/3 SAP入门 SAP版本 ERP SAP SAP MM 库存管理 物料管理 采购管理 入门教程 SAP S/4HANA SPRO 企业结构 采购组织 MM01 物料主数据 物料类型 BP分组 业务伙伴 供应商主数据 ME41 RFQ 库存物料 采购流程 ME51 消耗性物料 科目分配 采购申请 AC03 ML81N 外部服务 服务主数据 Business Partner SAP培训 ME51N MM模块 Lean Services MM-SRV 外部服务采购 PIR 供应来源 采购主数据 采购信息记录 ME31K 框架协议 计划协议 采购合同 ME01 供应来源确定 货源清单 MEQ1 供应源确定 配额安排 配额评分 MD04 MD21 MRP 计划文件 需求计划 批量程序 MD01N MD02 MRP Live MD05 MM 物料计划 优化采购 供应源 采购订单
联系我们
联系电话:15090125178(微信同号)
电子邮箱:garson_zhang@163.com
站长微信二维码
微信二维码