前言

以前做一个金融软件项目,软件要英文、繁体版本,开始甲方弄了好几个月,手动一条一条替换,发现很容易出错,因为有金融专业术语,字符串在不同语义要特殊处理,第三方工具没法使用。最后我用Roslyn写了一个工具,只需10分钟就能翻译整个软件,100%准确

做完上个项目发现Roslyn还可以深度开发,写了一个工具:代码助手,可解决项目所有琐碎重复性操作,代码完全自动化

原理

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"
>
..........................................
<#+
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);
}
}
#>

  

Roslyn+T4+EnvDTE项目完全自动化 (一)的更多相关文章

  1. Roslyn+T4+EnvDTE项目完全自动化(3) ——生成c++代码

    C++语法复杂,写一个示例通过T4可生成c++代码 需求:数据库,生成c++增,删,改,查代码 数据生成c++类,包含所有字段 自动识别数据的主键Key 查询生成赋值类字段,类型转换 通过类自动生成s ...

  2. 利用PowerShell+Jenkins,实现项目的自动化部署

    当项目越来越庞大,部署环境越来越多以后,就会越来越依赖于自动化.比如本人公司的项目,目前有6个web和4个windows service,同时本地有两套环境:开发自测试环境和QA测试环境.每次版本发布 ...

  3. AppVeyor-CI为GitHub项目做自动化集成(dotnet为主)

    travis-ci对dotnet的项目做自动化集成不太友好,尤其是使用mono的编译和不能使用MSTest进行自动化测试,所以转到appveyor进行. appveyor的配置非常简单,有两种方式: ...

  4. 利用Jenkins实现JavaWeb项目的自动化部署

    修改代码,打包,上传,重启... 大把的时间花费在这些重复无味的工作上.笔者与当前主流的价值观保持一致:我们应该把时间花费在更有意义的事情上.我们可以尝试借助一些工具,让这些重复机械的工作交给计算机去 ...

  5. Roslyn 入门:使用 Roslyn 静态分析现有项目中的代码

    Roslyn 是微软为 C# 设计的一套分析器,它具有很强的扩展性.以至于我们只需要编写很少量的代码便能够分析我们的项目文件. 作为 Roslyn 入门篇文章,你将可以通过本文学习如何开始编写一个 R ...

  6. 使用Git实现Laravel项目的自动化部署

    简介 不知道大家一开始是怎么使用 git 进行开发的,反正我个人是先将代码提交到 github 仓库,然后用 SSH 登录到服务器,然后进行克隆或者版本更新.听起来就很麻烦,当然实际操作中也很麻烦,那 ...

  7. 8 步搭建 Node.js + MongoDB 项目的自动化持续集成

    任何事情超过 90 秒就应该自动化,这是程序员的终极打开方式.Automating shapes smarter future. 这篇文章中,我们通过创建一个 Node.js + MongoDB 项目 ...

  8. 利用jenkins做项目的自动化部署

    最近领导要求上海本地的项目需要使用进jenkins实现自动化部署,以便可以直接将项目移交给运维的同学,减轻开发的工作量.记录下这次爬坑的过程. 一.前言 Jenkins是基于Java开发的一种持续集成 ...

  9. 关键词提取自动摘要相关开源项目,自动化seo

    关键词提取自动摘要相关开源项目 GitHub - hankcs/HanLP: 自然语言处理 中文分词 词性标注 命名实体识别 依存句法分析 关键词提取 自动摘要 短语提取 拼音 简繁转换https:/ ...

随机推荐

  1. ServletContext 学习

    ServletContext web容器在启动的时候,它会为每个web程序都创建一个对应的ServletContext对象,它代表了当前的web应用: 1.共享数据 ​ 在这个Servlet中保存了数 ...

  2. selenium3 利用cookie实现免登陆

    1.首先访问要操作的页面 2.登陆一次,使用Fiddle等工具抓取出cookie 3.按照如下代码,即可成功登陆 from selenium import webdriver url = " ...

  3. 在Excel中,不利用任何第三方工具,生成二维码

    有同事提需求,要批量生成二维码.谈了之后,我觉得可以做个excel文件,把要打印的内容放进去,然后给每行数据生成一个二维码.下一步就要在Excel里面生成二维码.问了一下度娘,貌似都得利用一些第三方工 ...

  4. RabbitMQ设计原理解析

    背景 RabbitMQ现在用的也比较多,但是没有过去那么多啦.现在很多的流行或者常用技术或者思路都是从过去的思路中演变而来的.了解一些过去的技术,对有些人来说可能会产生众里寻他千百度的顿悟,加深对技术 ...

  5. [敏捷软工团队博客]Beta阶段测试报告

    项目 内容 2020春季计算机学院软件工程(罗杰 任健) 博客园班级博客 作业要求 Beta阶段测试报告 我们在这个课程的目标是 在团队合作中锻炼自己 这个作业在哪个具体方面帮助我们实现目标 对Bet ...

  6. GT考试

    比较神仙的$dp+KMP+Matrix$综合题目,比较值得一写 $0x00$:首先我打了一个爆搜 不过对正解并无任何启发...(逗比发言请忽略) $0x01$:基础$dp$ 状态还是比较好设的, 考虑 ...

  7. 21.10.14 test

    题目 WOJ5078 到 WOJ5081 T1 Problem A \(\color{green}{100}\) 由于每轮要选择尽量多的边删除,所以想到无向图的生成树,因为在生成树上再加一条边就会形成 ...

  8. JVM:Java内存区域与内存溢出异常

    Java 虚拟机在执行 Java 程序的过程中会把它所管理的内存划分为若干个不同的数据区域.这些区域都有各自的用途,以及创建和销毁时间,有些区域随着虚拟机进程的启动而存在,有些区域依赖用户线程的启动和 ...

  9. 从零开始 DIY 智能家居 - 基于 ESP32 的智能语音合成播报模块

    目录 前言 硬件选择 代码解析 获取代码 设备控制命令: 设备和协议初始化流程: 配置设备信息 回调函数注册 语音播报与设置流程 总结 前言 这里这么多设备,突然发现我做的好像都是传感器之类的居多好像 ...

  10. 2021CCPC河南省省赛

    大一萌新,第一次打比赛,虽然是线下赛,但送气球的环节还是很赞的! 这里主要是补一下自己的弱项和考试时没有做出来的题目. 1002(链接之后再放,官方还没公开题目...) 先说一下第二题,这个题一看就是 ...