前言
Roslyn是什么?
XmlDocument,XDocument可以解析xml,同樣 Roslyn 可解析項目中C#代碼。c#常用插件ReSharper,只能重構一些很規范的代碼(生成IEqualityComparer,IComparer接口...),用Roslyn可以自動化業務代碼,可解決項目所有瑣碎重復性操作,代碼完全自動化
自己寫 代碼助手
一,類->視圖->增刪改查 全自動
需求:一個數據庫所有表的增刪改查
- 生成最終效果
- 每個屬性對應不同的控件:
- 自動生成單個對象編輯預覽
Roslyn+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); } } #>
- 自動生成項目文件