Roslyn+T4+EnvDTE项目完全自动化 (一)

  • A+
所属分类:.NET技术
摘要

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


前言

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

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

原理

Roslyn是啥?

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

自己写 代码助手

Roslyn+T4+EnvDTE项目完全自动化 (一)

 

 

 

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

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

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

Roslyn+T4+EnvDTE项目完全自动化 (一)

 

 

 生成最终效果

Roslyn+T4+EnvDTE项目完全自动化 (一)

 

 

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

Roslyn+T4+EnvDTE项目完全自动化 (一)

 

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

 

 Roslyn+T4+EnvDTE项目完全自动化 (一)

 

 

 

Roslyn+T4实现 

  1. EnvDTE获取当前打开项目
  2. Roslyn api CodeAnalysis 分析当前项目
  3. 获取代码Symbol
  4. T4代码自动化 

T4主要代码

<#@ include file="Includebase.t4" #> <#@ include file="IncludeCodeAnalysis.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(@"TestViewModules");        //获取项目view的路径         var viewModelProjectItem = new ProjectItemEntry(@"TestViewModelModules"); //获取项目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项目完全自动化 (一)Roslyn+T4+EnvDTE项目完全自动化 (一)