├── .gitattributes ├── .gitignore ├── AOPViaT4Examples.sln ├── App ├── App.config ├── App.csproj ├── LoggingAspectLocal.cs ├── LoggingAspectLocal.tt ├── Program.cs ├── Properties │ └── AssemblyInfo.cs ├── RetryAspectLocal.cs └── RetryAspectLocal.tt ├── Library ├── Document.cs ├── DocumentSource.cs ├── IDocumentSource.cs ├── Library.csproj └── Properties │ └── AssemblyInfo.cs ├── Logging ├── ConsoleLogger.cs ├── ILogger.cs ├── Logging.csproj ├── LoggingData.cs └── Properties │ └── AssemblyInfo.cs ├── LoggingAOP ├── LogAttribute.cs ├── LogCountAttribute.cs ├── LoggingAOP.csproj ├── MethodDescriptionAttribute.cs └── Properties │ └── AssemblyInfo.cs ├── README.md └── T4Aspects ├── CompositePostInvocationLoggingDataCodeGenerator.tt ├── CompositePreInvocationLoggingDataCodeGenerator.tt ├── LoggingAspect.Core.tt ├── LoggingAspect.tt ├── LoggingDataCodeGeneratorForArgumentsBasedOnTheLogAttribute.tt ├── LoggingDataCodeGeneratorForArgumentsBasedOnTheLogCountAttribute.tt ├── LoggingDataCodeGeneratorForReturnValueBasedOnTheLogAttribute.tt ├── LoggingDataCodeGeneratorForReturnValueBasedOnTheLogCountAttribute.tt ├── MethodDescriptionLoggingDataCodeGenerator.tt ├── Properties └── AssemblyInfo.cs ├── RetryAspect.tt ├── T4Aspects.csproj └── Utilities ├── AttributeValueExtractor.tt ├── ClassFinder.tt ├── InterfaceFinder.tt ├── MethodMatcher.tt └── MethodParametersGenerator.tt /.gitattributes: -------------------------------------------------------------------------------- 1 | *.cs eol=crlf 2 | *.tt eol=crlf 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | *.VC.VC.opendb 85 | 86 | # Visual Studio profiler 87 | *.psess 88 | *.vsp 89 | *.vspx 90 | *.sap 91 | 92 | # TFS 2012 Local Workspace 93 | $tf/ 94 | 95 | # Guidance Automation Toolkit 96 | *.gpState 97 | 98 | # ReSharper is a .NET coding add-in 99 | _ReSharper*/ 100 | *.[Rr]e[Ss]harper 101 | *.DotSettings.user 102 | 103 | # JustCode is a .NET coding add-in 104 | .JustCode 105 | 106 | # TeamCity is a build add-in 107 | _TeamCity* 108 | 109 | # DotCover is a Code Coverage Tool 110 | *.dotCover 111 | 112 | # NCrunch 113 | _NCrunch_* 114 | .*crunch*.local.xml 115 | nCrunchTemp_* 116 | 117 | # MightyMoose 118 | *.mm.* 119 | AutoTest.Net/ 120 | 121 | # Web workbench (sass) 122 | .sass-cache/ 123 | 124 | # Installshield output folder 125 | [Ee]xpress/ 126 | 127 | # DocProject is a documentation generator add-in 128 | DocProject/buildhelp/ 129 | DocProject/Help/*.HxT 130 | DocProject/Help/*.HxC 131 | DocProject/Help/*.hhc 132 | DocProject/Help/*.hhk 133 | DocProject/Help/*.hhp 134 | DocProject/Help/Html2 135 | DocProject/Help/html 136 | 137 | # Click-Once directory 138 | publish/ 139 | 140 | # Publish Web Output 141 | *.[Pp]ublish.xml 142 | *.azurePubxml 143 | # TODO: Comment the next line if you want to checkin your web deploy settings 144 | # but database connection strings (with potential passwords) will be unencrypted 145 | *.pubxml 146 | *.publishproj 147 | 148 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 149 | # checkin your Azure Web App publish settings, but sensitive information contained 150 | # in these scripts will be unencrypted 151 | PublishScripts/ 152 | 153 | # NuGet Packages 154 | *.nupkg 155 | # The packages folder can be ignored because of Package Restore 156 | **/packages/* 157 | # except build/, which is used as an MSBuild target. 158 | !**/packages/build/ 159 | # Uncomment if necessary however generally it will be regenerated when needed 160 | #!**/packages/repositories.config 161 | # NuGet v3's project.json files produces more ignoreable files 162 | *.nuget.props 163 | *.nuget.targets 164 | 165 | # Microsoft Azure Build Output 166 | csx/ 167 | *.build.csdef 168 | 169 | # Microsoft Azure Emulator 170 | ecf/ 171 | rcf/ 172 | 173 | # Windows Store app package directories and files 174 | AppPackages/ 175 | BundleArtifacts/ 176 | Package.StoreAssociation.xml 177 | _pkginfo.txt 178 | 179 | # Visual Studio cache files 180 | # files ending in .cache can be ignored 181 | *.[Cc]ache 182 | # but keep track of directories ending in .cache 183 | !*.[Cc]ache/ 184 | 185 | # Others 186 | ClientBin/ 187 | ~$* 188 | *~ 189 | *.dbmdl 190 | *.dbproj.schemaview 191 | *.pfx 192 | *.publishsettings 193 | node_modules/ 194 | orleans.codegen.cs 195 | 196 | # Since there are multiple workflows, uncomment next line to ignore bower_components 197 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 198 | #bower_components/ 199 | 200 | # RIA/Silverlight projects 201 | Generated_Code/ 202 | 203 | # Backup & report files from converting an old project file 204 | # to a newer Visual Studio version. Backup files are not needed, 205 | # because we have git ;-) 206 | _UpgradeReport_Files/ 207 | Backup*/ 208 | UpgradeLog*.XML 209 | UpgradeLog*.htm 210 | 211 | # SQL Server files 212 | *.mdf 213 | *.ldf 214 | 215 | # Business Intelligence projects 216 | *.rdl.data 217 | *.bim.layout 218 | *.bim_*.settings 219 | 220 | # Microsoft Fakes 221 | FakesAssemblies/ 222 | 223 | # GhostDoc plugin setting file 224 | *.GhostDoc.xml 225 | 226 | # Node.js Tools for Visual Studio 227 | .ntvs_analysis.dat 228 | 229 | # Visual Studio 6 build log 230 | *.plg 231 | 232 | # Visual Studio 6 workspace options file 233 | *.opt 234 | 235 | # Visual Studio LightSwitch build output 236 | **/*.HTMLClient/GeneratedArtifacts 237 | **/*.DesktopClient/GeneratedArtifacts 238 | **/*.DesktopClient/ModelManifest.xml 239 | **/*.Server/GeneratedArtifacts 240 | **/*.Server/ModelManifest.xml 241 | _Pvt_Extensions 242 | 243 | # Paket dependency manager 244 | .paket/paket.exe 245 | paket-files/ 246 | 247 | # FAKE - F# Make 248 | .fake/ 249 | 250 | # JetBrains Rider 251 | .idea/ 252 | *.sln.iml 253 | -------------------------------------------------------------------------------- /AOPViaT4Examples.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Library", "Library\Library.csproj", "{4336620E-9849-40D4-9E90-FD449E8CE83C}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LoggingAOP", "LoggingAOP\LoggingAOP.csproj", "{25F930E2-3F2D-4B06-9545-9E03EB59B11C}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "T4Aspects", "T4Aspects\T4Aspects.csproj", "{24148B11-2079-440A-A7D2-1DBDC0848855}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Logging", "Logging\Logging.csproj", "{B4FD511B-81D5-4B28-9D4F-7C3B5CFFEC5D}" 13 | EndProject 14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "App", "App\App.csproj", "{87F51EEA-92F9-4A86-B004-C6EA5CBDE48C}" 15 | EndProject 16 | Global 17 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 18 | Debug|Any CPU = Debug|Any CPU 19 | Release|Any CPU = Release|Any CPU 20 | EndGlobalSection 21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 22 | {4336620E-9849-40D4-9E90-FD449E8CE83C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {4336620E-9849-40D4-9E90-FD449E8CE83C}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {4336620E-9849-40D4-9E90-FD449E8CE83C}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {4336620E-9849-40D4-9E90-FD449E8CE83C}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {25F930E2-3F2D-4B06-9545-9E03EB59B11C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {25F930E2-3F2D-4B06-9545-9E03EB59B11C}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {25F930E2-3F2D-4B06-9545-9E03EB59B11C}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {25F930E2-3F2D-4B06-9545-9E03EB59B11C}.Release|Any CPU.Build.0 = Release|Any CPU 30 | {24148B11-2079-440A-A7D2-1DBDC0848855}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {24148B11-2079-440A-A7D2-1DBDC0848855}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {24148B11-2079-440A-A7D2-1DBDC0848855}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {24148B11-2079-440A-A7D2-1DBDC0848855}.Release|Any CPU.Build.0 = Release|Any CPU 34 | {B4FD511B-81D5-4B28-9D4F-7C3B5CFFEC5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {B4FD511B-81D5-4B28-9D4F-7C3B5CFFEC5D}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {B4FD511B-81D5-4B28-9D4F-7C3B5CFFEC5D}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {B4FD511B-81D5-4B28-9D4F-7C3B5CFFEC5D}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {87F51EEA-92F9-4A86-B004-C6EA5CBDE48C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {87F51EEA-92F9-4A86-B004-C6EA5CBDE48C}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {87F51EEA-92F9-4A86-B004-C6EA5CBDE48C}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {87F51EEA-92F9-4A86-B004-C6EA5CBDE48C}.Release|Any CPU.Build.0 = Release|Any CPU 42 | EndGlobalSection 43 | GlobalSection(SolutionProperties) = preSolution 44 | HideSolutionNode = FALSE 45 | EndGlobalSection 46 | EndGlobal 47 | -------------------------------------------------------------------------------- /App/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /App/App.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {87F51EEA-92F9-4A86-B004-C6EA5CBDE48C} 8 | Exe 9 | Properties 10 | App 11 | App 12 | v4.5.2 13 | 512 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | True 48 | True 49 | LoggingAspectLocal.tt 50 | 51 | 52 | 53 | 54 | True 55 | True 56 | RetryAspectLocal.tt 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | TextTemplatingFileGenerator 65 | LoggingAspectLocal.cs 66 | 67 | 68 | TextTemplatingFileGenerator 69 | RetryAspectLocal.cs 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | {4336620e-9849-40d4-9e90-fd449e8ce83c} 78 | Library 79 | 80 | 81 | {b4fd511b-81d5-4b28-9d4f-7c3b5cffec5d} 82 | Logging 83 | 84 | 85 | 86 | 93 | -------------------------------------------------------------------------------- /App/LoggingAspectLocal.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace Library 3 | { 4 | using System; 5 | using Logging; 6 | using System.Linq; 7 | 8 | public class DocumentSourceLoggingDecorator : Library.IDocumentSource 9 | { 10 | private readonly Library.DocumentSource decorated; 11 | private readonly ILogger logger; 12 | private readonly LoggingData[] constantLoggingData; 13 | 14 | public DocumentSourceLoggingDecorator(Library.DocumentSource decorated, ILogger logger, params LoggingData[] constantLoggingData) 15 | { 16 | this.decorated = decorated; 17 | this.logger = logger; 18 | this.constantLoggingData = constantLoggingData; 19 | } 20 | 21 | public Library.Document[] GetDocuments(System.String format) 22 | { 23 | var preInvocationLoggingData = 24 | new LoggingData[] 25 | { 26 | new LoggingData{ Name = "Method", Value = "Get Documents"}, 27 | new LoggingData{ Name = "Document Format", Value = format}, 28 | } 29 | .Concat(constantLoggingData) 30 | .ToArray(); 31 | 32 | Library.Document[] returnValue; 33 | 34 | try 35 | { 36 | returnValue = decorated.GetDocuments(format); 37 | } 38 | catch(Exception ex) 39 | { 40 | logger.LogError(preInvocationLoggingData, ex); 41 | throw; 42 | } 43 | 44 | var returnValueLoggingData = new LoggingData[] 45 | { 46 | new LoggingData{ Name = "Return Value", Value = returnValue.Length.ToString()}, 47 | }; 48 | 49 | logger.LogSuccess(preInvocationLoggingData.Concat(returnValueLoggingData).ToArray()); 50 | 51 | return returnValue; 52 | 53 | } 54 | 55 | } 56 | 57 | public static class DocumentSourceExtensionMethodsForLoggingAspect 58 | { 59 | public static Library.IDocumentSource AsLoggable(this Library.DocumentSource instance, ILogger logger, params LoggingData[] constantLoggingData) 60 | { 61 | return new DocumentSourceLoggingDecorator(instance, logger, constantLoggingData); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /App/LoggingAspectLocal.tt: -------------------------------------------------------------------------------- 1 | <#@ template debug="false" hostspecific="true" language="C#" #> 2 | <#@ assembly name="EnvDTE" #> 3 | <#@ import namespace="EnvDTE" #> 4 | <#@ include file="$(SolutionDir)T4Aspects\LoggingAspect.tt" #><##> 5 | <#@ include file="$(SolutionDir)T4Aspects\LoggingDataCodeGeneratorForArgumentsBasedOnTheLogAttribute.tt" #><##> 6 | <#@ include file="$(SolutionDir)T4Aspects\LoggingDataCodeGeneratorForArgumentsBasedOnTheLogCountAttribute.tt" #><##> 7 | <#@ include file="$(SolutionDir)T4Aspects\LoggingDataCodeGeneratorForReturnValueBasedOnTheLogAttribute.tt" #><##> 8 | <#@ include file="$(SolutionDir)T4Aspects\LoggingDataCodeGeneratorForReturnValueBasedOnTheLogCountAttribute.tt" #><##> 9 | <#@ include file="$(SolutionDir)T4Aspects\CompositePostInvocationLoggingDataCodeGenerator.tt" #><##> 10 | <#@ include file="$(SolutionDir)T4Aspects\CompositePreInvocationLoggingDataCodeGenerator.tt" #><##> 11 | <#@ include file="$(SolutionDir)T4Aspects\MethodDescriptionLoggingDataCodeGenerator.tt" #><##> 12 | <#@include once="true" file="$(SolutionDir)T4Aspects\LoggingAspect.Core.tt"#><##> 13 | <#@include once="true" file="$(SolutionDir)T4Aspects\Utilities\ClassFinder.tt"#><##> 14 | <#@ output extension="cs"#><##> 15 | <# 16 | var hostServiceProvider = (IServiceProvider) Host; 17 | var dte = (DTE) hostServiceProvider.GetService(typeof(DTE)); 18 | 19 | var classes = ClassFinder.FindClassesInSolution(dte, new[] {"DocumentSource"}); 20 | 21 | foreach (var @class in classes) 22 | { 23 | GenerateLoggingDecoratorForClass( 24 | @class, 25 | @class.ImplementedInterfaces.Cast().Single(), 26 | new CompositePreInvocationLoggingDataCodeGenerator( 27 | new MethodDescriptionLoggingDataCodeGenerator(), 28 | new LoggingDataCodeGeneratorForArgumentsBasedOnTheLogAttribute(), 29 | new LoggingDataCodeGeneratorForArgumentsBasedOnTheLogCountAttribute()), 30 | new CompositePostInvocationLoggingDataCodeGenerator( 31 | new LoggingDataCodeGeneratorForReturnValueBasedOnTheLogAttribute(), 32 | new LoggingDataCodeGeneratorForReturnValueBasedOnTheLogCountAttribute())); 33 | } 34 | #> -------------------------------------------------------------------------------- /App/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Library; 7 | using Logging; 8 | 9 | namespace App 10 | { 11 | class Program 12 | { 13 | static void Main(string[] args) 14 | { 15 | var documentSource = 16 | new DocumentSource() 17 | .AsLoggable( 18 | new ConsoleLogger(), 19 | new LoggingData { Name = "Department", Value = "A" }) 20 | .ApplyRetryAspect( 21 | numberOfRetries: 3, 22 | waitTimeBetweenRetries: TimeSpan.FromSeconds(5)); 23 | 24 | var result = documentSource.GetDocuments("docx"); 25 | 26 | Console.ReadLine(); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /App/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("App")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("App")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("87f51eea-92f9-4a86-b004-c6ea5cbde48c")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /App/RetryAspectLocal.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace Library 3 | { 4 | using System; 5 | using System.Threading; 6 | 7 | public class DocumentSourceRetryDecorator : Library.IDocumentSource 8 | { 9 | private readonly Library.IDocumentSource decorated; 10 | private readonly int numberOfRetries; 11 | private readonly TimeSpan waitTimeBetweenRetries; 12 | 13 | public DocumentSourceRetryDecorator( 14 | Library.IDocumentSource decorated, 15 | int numberOfRetries, 16 | TimeSpan waitTimeBetweenRetries) 17 | { 18 | this.decorated = decorated; 19 | this.numberOfRetries = numberOfRetries; 20 | this.waitTimeBetweenRetries = waitTimeBetweenRetries; 21 | } 22 | 23 | public Library.Document[] GetDocuments(System.String format) 24 | { 25 | int retries = 0; 26 | 27 | while(true) 28 | { 29 | try 30 | { 31 | return decorated.GetDocuments(format); 32 | } 33 | catch 34 | { 35 | retries++; 36 | 37 | if(retries == numberOfRetries) 38 | throw; 39 | 40 | Thread.Sleep(waitTimeBetweenRetries); 41 | } 42 | } 43 | } 44 | 45 | } 46 | 47 | public static class ExtensionMethodsForRetryAspectForDocumentSource 48 | { 49 | public static Library.IDocumentSource ApplyRetryAspect(this Library.IDocumentSource instance, int numberOfRetries, TimeSpan waitTimeBetweenRetries) 50 | { 51 | return new DocumentSourceRetryDecorator(instance, numberOfRetries, waitTimeBetweenRetries); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /App/RetryAspectLocal.tt: -------------------------------------------------------------------------------- 1 | <#@ template debug="false" hostspecific="true" language="C#" #> 2 | <#@ assembly name="EnvDTE" #> 3 | <#@ import namespace="EnvDTE" #> 4 | <#@include once="true" file="$(SolutionDir)T4Aspects\RetryAspect.tt"#><##> 5 | <#@include once="true" file="$(SolutionDir)T4Aspects\Utilities\InterfaceFinder.tt"#><##> 6 | <#@ output extension="cs"#> 7 | <# 8 | var hostServiceProvider = (IServiceProvider) Host; 9 | var dte = (DTE) hostServiceProvider.GetService(typeof(DTE)); 10 | 11 | var interfaces = InterfaceFinder.FindInterfacesInSolution(dte, new[] {"IDocumentSource"}); 12 | 13 | foreach (var @interface in interfaces) 14 | { 15 | GenerateRetryDecoratorForInterface(@interface); 16 | } 17 | #> -------------------------------------------------------------------------------- /Library/Document.cs: -------------------------------------------------------------------------------- 1 | namespace Library 2 | { 3 | public class Document 4 | { 5 | public Document(string name, string content) 6 | { 7 | Name = name; 8 | Content = content; 9 | } 10 | 11 | public string Name { get; private set; } 12 | public string Content { get; private set; } 13 | } 14 | } -------------------------------------------------------------------------------- /Library/DocumentSource.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using LoggingAOP; 3 | 4 | namespace Library 5 | { 6 | public class DocumentSource : IDocumentSource 7 | { 8 | [return: LogCount] 9 | [MethodDescription("Get Documents")] 10 | public Document[] GetDocuments([Log("Document Format")] string format) 11 | { 12 | return 13 | Enumerable 14 | .Range(0, 10) 15 | .Select(x => new Document("document" + x, "content" + x)) 16 | .ToArray(); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Library/IDocumentSource.cs: -------------------------------------------------------------------------------- 1 | namespace Library 2 | { 3 | public interface IDocumentSource 4 | { 5 | Document[] GetDocuments(string format); 6 | } 7 | } -------------------------------------------------------------------------------- /Library/Library.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {4336620E-9849-40D4-9E90-FD449E8CE83C} 8 | Library 9 | Properties 10 | Library 11 | Library 12 | v4.5.2 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | 24 | 25 | pdbonly 26 | true 27 | bin\Release\ 28 | TRACE 29 | prompt 30 | 4 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | {25F930E2-3F2D-4B06-9545-9E03EB59B11C} 51 | LoggingAOP 52 | 53 | 54 | 55 | 62 | -------------------------------------------------------------------------------- /Library/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Library")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Library")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("4336620e-9849-40d4-9e90-fd449e8ce83c")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /Logging/ConsoleLogger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Logging 8 | { 9 | public class ConsoleLogger : ILogger 10 | { 11 | public void LogSuccess(LoggingData[] loggingData) 12 | { 13 | Console.WriteLine("Success" + Environment.NewLine + FormatLoggingData(loggingData)); 14 | } 15 | 16 | public void LogError(LoggingData[] loggingData, Exception exception) 17 | { 18 | Console.WriteLine("Error" + Environment.NewLine + FormatLoggingData(loggingData)); 19 | Console.WriteLine(exception.Message); 20 | } 21 | 22 | private string FormatLoggingData(LoggingData[] loggingData) 23 | { 24 | StringBuilder sb = new StringBuilder(); 25 | 26 | foreach (var item in loggingData) 27 | { 28 | sb.AppendLine(item.Name + ": " + item.Value); 29 | } 30 | 31 | return sb.ToString(); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Logging/ILogger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Logging 4 | { 5 | public interface ILogger 6 | { 7 | void LogSuccess(LoggingData[] loggingData); 8 | void LogError(LoggingData[] loggingData, Exception exception); 9 | } 10 | } -------------------------------------------------------------------------------- /Logging/Logging.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {B4FD511B-81D5-4B28-9D4F-7C3B5CFFEC5D} 8 | Library 9 | Properties 10 | Logging 11 | Logging 12 | v4.5.2 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | 24 | 25 | pdbonly 26 | true 27 | bin\Release\ 28 | TRACE 29 | prompt 30 | 4 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 56 | -------------------------------------------------------------------------------- /Logging/LoggingData.cs: -------------------------------------------------------------------------------- 1 | namespace Logging 2 | { 3 | public class LoggingData 4 | { 5 | public string Name { get; set; } 6 | public string Value { get; set; } 7 | } 8 | } -------------------------------------------------------------------------------- /Logging/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Logging")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Logging")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("b4fd511b-81d5-4b28-9d4f-7c3b5cffec5d")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /LoggingAOP/LogAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace LoggingAOP 4 | { 5 | public class LogAttribute : Attribute 6 | { 7 | public LogAttribute(string name) 8 | { 9 | Name = name; 10 | } 11 | 12 | public LogAttribute() 13 | { 14 | } 15 | 16 | public string Name { get; private set; } 17 | } 18 | } -------------------------------------------------------------------------------- /LoggingAOP/LogCountAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace LoggingAOP 4 | { 5 | public class LogCountAttribute : Attribute 6 | { 7 | public LogCountAttribute(string name) 8 | { 9 | Name = name; 10 | } 11 | 12 | public LogCountAttribute() 13 | { 14 | } 15 | 16 | public string Name { get; private set; } 17 | } 18 | } -------------------------------------------------------------------------------- /LoggingAOP/LoggingAOP.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {25F930E2-3F2D-4B06-9545-9E03EB59B11C} 8 | Library 9 | Properties 10 | LoggingAOP 11 | LoggingAOP 12 | v4.5.2 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | 24 | 25 | pdbonly 26 | true 27 | bin\Release\ 28 | TRACE 29 | prompt 30 | 4 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 56 | -------------------------------------------------------------------------------- /LoggingAOP/MethodDescriptionAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace LoggingAOP 4 | { 5 | public class MethodDescriptionAttribute : Attribute 6 | { 7 | public string Description { get; private set; } 8 | 9 | public MethodDescriptionAttribute(string description) 10 | { 11 | Description = description; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /LoggingAOP/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("LoggingAOP")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("LoggingAOP")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("25f930e2-3f2d-4b06-9545-9e03eb59b11c")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AOPViaT4Examples 2 | 3 | The code in this repository is an example to support the Aspect Oriented Programming (AOP) in C# via T4 article. You can read the article here: http://www.dotnetcurry.com/patterns-practices/1318/aspect-oriented-programming-aop-t4 4 | -------------------------------------------------------------------------------- /T4Aspects/CompositePostInvocationLoggingDataCodeGenerator.tt: -------------------------------------------------------------------------------- 1 | <#@ assembly name="EnvDTE" #> 2 | <#@ assembly name="System.Core" #> 3 | <#@ import namespace="EnvDTE" #> 4 | <#@ import namespace="System.Linq" #> 5 | <#@ include once="true" file="LoggingAspect.Core.tt"#><##> 6 | <#+ 7 | public class CompositePostInvocationLoggingDataCodeGenerator : IPostInvocationLoggingDataCodeGenerator 8 | { 9 | private readonly IPostInvocationLoggingDataCodeGenerator[] generators; 10 | 11 | public CompositePostInvocationLoggingDataCodeGenerator(params IPostInvocationLoggingDataCodeGenerator[] generators) 12 | { 13 | this.generators = generators; 14 | } 15 | 16 | public LoggingDataCode[] Extract(CodeFunction method, string returnValueVariableName) 17 | { 18 | return generators.SelectMany(x => x.Extract(method, returnValueVariableName)).ToArray(); 19 | } 20 | } 21 | #> -------------------------------------------------------------------------------- /T4Aspects/CompositePreInvocationLoggingDataCodeGenerator.tt: -------------------------------------------------------------------------------- 1 | <#@ assembly name="EnvDTE" #> 2 | <#@ assembly name="System.Core" #> 3 | <#@ import namespace="EnvDTE" #> 4 | <#@ import namespace="System.Linq" #> 5 | <#@ include once="true" file="LoggingAspect.Core.tt"#><##> 6 | <#+ 7 | public class CompositePreInvocationLoggingDataCodeGenerator : IPreInvocationLoggingDataCodeGenerator 8 | { 9 | private readonly IPreInvocationLoggingDataCodeGenerator[] generators; 10 | 11 | public CompositePreInvocationLoggingDataCodeGenerator(params IPreInvocationLoggingDataCodeGenerator[] generators) 12 | { 13 | this.generators = generators; 14 | } 15 | 16 | public LoggingDataCode[] Extract(CodeFunction method) 17 | { 18 | return generators.SelectMany(x => x.Extract(method)).ToArray(); 19 | } 20 | } 21 | #> -------------------------------------------------------------------------------- /T4Aspects/LoggingAspect.Core.tt: -------------------------------------------------------------------------------- 1 | <#@ assembly name="EnvDTE" #> 2 | <#@ assembly name="System.Core" #> 3 | <#@ import namespace="EnvDTE" #> 4 | <#@ import namespace="System.Linq" #> 5 | <#+ 6 | public class LoggingDataCode 7 | { 8 | public LoggingDataCode(string nameCode, string valueCode) 9 | { 10 | NameCode = nameCode; 11 | ValueCode = valueCode; 12 | } 13 | 14 | public string NameCode { get;private set; } 15 | 16 | public string ValueCode { get; private set; } 17 | } 18 | 19 | public interface IPreInvocationLoggingDataCodeGenerator 20 | { 21 | LoggingDataCode[] Extract(CodeFunction method); 22 | } 23 | 24 | public interface IPostInvocationLoggingDataCodeGenerator 25 | { 26 | LoggingDataCode[] Extract(CodeFunction method, string returnValueVariableName); 27 | } 28 | #> -------------------------------------------------------------------------------- /T4Aspects/LoggingAspect.tt: -------------------------------------------------------------------------------- 1 | <#@ assembly name="EnvDTE" #> 2 | <#@ assembly name="EnvDTE80" #> 3 | <#@ assembly name="System.Core" #> 4 | <#@ import namespace="EnvDTE" #> 5 | <#@ import namespace="EnvDTE80" #> 6 | <#@ import namespace="System.Text" #> 7 | <#@ import namespace="System.Linq" #> 8 | <#@ include once="true" file="LoggingAspect.Core.tt"#><##> 9 | <#@ include once="true" file="Utilities\MethodParametersGenerator.tt"#><##> 10 | <#@ include once="true" file="Utilities\MethodMatcher.tt"#><##> 11 | <#+ 12 | public void GenerateLoggingDecoratorForClass( 13 | CodeClass2 @class, 14 | CodeInterface @interface, 15 | IPreInvocationLoggingDataCodeGenerator preInvocationLoggingDataCodeExtractor, 16 | IPostInvocationLoggingDataCodeGenerator postInvocationLoggingDataCodeExtractor) 17 | { 18 | 19 | string className = @class.Name; 20 | string fullClassName = @class.FullName; 21 | string fullInterfaceName = @interface.FullName; 22 | 23 | string decoratorClassName = className + "LoggingDecorator"; 24 | 25 | var @namespace = @class.Namespace.FullName; 26 | 27 | #> 28 | 29 | namespace <#= @namespace #> 30 | { 31 | using System; 32 | using Logging; 33 | using System.Linq; 34 | 35 | public class <#=decoratorClassName#> : <#=fullInterfaceName#> 36 | { 37 | private readonly <#=fullClassName#> decorated; 38 | private readonly ILogger logger; 39 | private readonly LoggingData[] constantLoggingData; 40 | 41 | public <#=decoratorClassName#>(<#=fullClassName#> decorated, ILogger logger, params LoggingData[] constantLoggingData) 42 | { 43 | this.decorated = decorated; 44 | this.logger = logger; 45 | this.constantLoggingData = constantLoggingData; 46 | } 47 | 48 | <#+ 49 | PushIndent(new string(' ', 8)); 50 | GenerateMethodsForClass(@class, @interface, preInvocationLoggingDataCodeExtractor, postInvocationLoggingDataCodeExtractor); 51 | PopIndent(); 52 | #> 53 | } 54 | 55 | public static class <#=className#>ExtensionMethodsForLoggingAspect 56 | { 57 | public static <#=fullInterfaceName#> AsLoggable(this <#=fullClassName#> instance, ILogger logger, params LoggingData[] constantLoggingData) 58 | { 59 | return new <#=decoratorClassName#>(instance, logger, constantLoggingData); 60 | } 61 | } 62 | } 63 | <#+ 64 | } 65 | 66 | public void GenerateMethodsForClass( 67 | CodeClass2 @class, 68 | CodeInterface @interface, 69 | IPreInvocationLoggingDataCodeGenerator preInvocationLoggingDataCodeExtractor, 70 | IPostInvocationLoggingDataCodeGenerator postInvocationLoggingDataCodeExtractor) 71 | { 72 | 73 | 74 | foreach (CodeFunction interfaceMethod in @interface.Members.OfType()) 75 | { 76 | 77 | string methodReturnType = 78 | interfaceMethod.Type.TypeKind == vsCMTypeRef.vsCMTypeRefVoid 79 | ? "void" 80 | : interfaceMethod.Type.AsFullName; 81 | 82 | string methodName = interfaceMethod.Name; 83 | bool isVoid = interfaceMethod.Type.TypeKind == vsCMTypeRef.vsCMTypeRefVoid; 84 | 85 | var classMethod = MethodMatcher.GetClassMethodFromInterfaceMethod(interfaceMethod, @class); 86 | 87 | 88 | var preInvocationLoggingDataCodeList = preInvocationLoggingDataCodeExtractor.Extract(classMethod); 89 | 90 | var postInvocationLoggingDataCodeList = postInvocationLoggingDataCodeExtractor.Extract(classMethod, "returnValue"); 91 | 92 | #> 93 | public <#=methodReturnType#> <#=methodName#>(<#=MethodParameterGenerator.GenerateMethodParametersWithTypes(classMethod)#>) 94 | { 95 | var preInvocationLoggingData = 96 | new LoggingData[] 97 | { 98 | <#+ 99 | foreach (var loggingDataCode in preInvocationLoggingDataCodeList) 100 | { 101 | PushIndent(new string(' ', 8)); 102 | RenderLoggingDataCode(loggingDataCode); 103 | PopIndent(); 104 | } 105 | #> 106 | } 107 | .Concat(constantLoggingData) 108 | .ToArray(); 109 | 110 | <#+ 111 | if (!isVoid) 112 | { 113 | #> 114 | <#=methodReturnType#> returnValue; 115 | 116 | <#+ 117 | } 118 | #> 119 | try 120 | { 121 | <#+ 122 | if (isVoid) 123 | { 124 | #> 125 | decorated.<#=methodName #>(<#=MethodParameterGenerator.GenerateMethodParametersWithoutTypes(classMethod) #>); 126 | <#+ 127 | } 128 | else 129 | { 130 | #> 131 | returnValue = decorated.<#=methodName #>(<#=MethodParameterGenerator.GenerateMethodParametersWithoutTypes(classMethod) #>); 132 | <#+ 133 | } 134 | #> 135 | } 136 | catch(Exception ex) 137 | { 138 | logger.LogError(preInvocationLoggingData, ex); 139 | throw; 140 | } 141 | <#+ 142 | if (!isVoid) 143 | { 144 | if (postInvocationLoggingDataCodeList.Length > 0) 145 | { 146 | #> 147 | 148 | var returnValueLoggingData = new LoggingData[] 149 | { 150 | <#+ 151 | foreach (var loggingDataCode in postInvocationLoggingDataCodeList) 152 | { 153 | RenderLoggingDataCode(loggingDataCode); 154 | } 155 | #> 156 | }; 157 | 158 | logger.LogSuccess(preInvocationLoggingData.Concat(returnValueLoggingData).ToArray()); 159 | <#+ 160 | } 161 | else 162 | { 163 | #> 164 | 165 | logger.LogSuccess(preInvocationLoggingData); 166 | <#+ 167 | } 168 | #> 169 | 170 | return returnValue; 171 | <#+ 172 | } 173 | else 174 | { 175 | #> 176 | 177 | logger.LogSuccess(preInvocationLoggingData); 178 | <#+ 179 | } 180 | #> 181 | 182 | } 183 | 184 | <#+ 185 | } 186 | } 187 | 188 | public void RenderLoggingDataCode(LoggingDataCode loggingDataCode) 189 | { 190 | #> 191 | new LoggingData{ Name = <#=loggingDataCode.NameCode#>, Value = <#=loggingDataCode.ValueCode#>}, 192 | <#+ 193 | } 194 | #> -------------------------------------------------------------------------------- /T4Aspects/LoggingDataCodeGeneratorForArgumentsBasedOnTheLogAttribute.tt: -------------------------------------------------------------------------------- 1 | <#@ assembly name="EnvDTE" #> 2 | <#@ assembly name="System.Core" #> 3 | <#@ import namespace="System.Collections.Generic" #> 4 | <#@ import namespace="System.Linq" #> 5 | <#@ import namespace="EnvDTE" #> 6 | <#@ include once="true" file="LoggingAspect.Core.tt"#><##> 7 | <#@ include once="true" file="Utilities\AttributeValueExtractor.tt"#><##> 8 | <#+ 9 | public class LoggingDataCodeGeneratorForArgumentsBasedOnTheLogAttribute : IPreInvocationLoggingDataCodeGenerator 10 | { 11 | public LoggingDataCode[] Extract(CodeFunction method) 12 | { 13 | List loggingDataCodeList = new List(); 14 | 15 | foreach (CodeParameter parameter in method.Parameters) 16 | { 17 | var attributes = 18 | parameter.Attributes 19 | .Cast() 20 | .Where(x => x.FullName == "LoggingAOP.LogAttribute") 21 | .ToList(); 22 | 23 | foreach (var attribute in attributes) 24 | { 25 | var valueCode = 26 | parameter.Type.TypeKind == vsCMTypeRef.vsCMTypeRefString 27 | ? parameter.Name 28 | : parameter.Name + ".ToString()"; 29 | 30 | var nameCode = "\"" + (AttributeValueExtractor.GetAttributeSingleStringParameterValueOrNull(attribute) ?? parameter.Name) +"\""; 31 | 32 | loggingDataCodeList 33 | .Add( 34 | new LoggingDataCode( 35 | nameCode, 36 | valueCode)); 37 | } 38 | } 39 | 40 | return loggingDataCodeList.ToArray(); 41 | } 42 | } 43 | #> -------------------------------------------------------------------------------- /T4Aspects/LoggingDataCodeGeneratorForArgumentsBasedOnTheLogCountAttribute.tt: -------------------------------------------------------------------------------- 1 | <#@ assembly name="EnvDTE" #> 2 | <#@ assembly name="System.Core" #> 3 | <#@ import namespace="EnvDTE" #> 4 | <#@ import namespace="System.Collections.Generic" #> 5 | <#@ import namespace="System.Linq" #> 6 | <#@ include once="true" file="LoggingAspect.Core.tt"#><##> 7 | <#@ include once="true" file="Utilities\AttributeValueExtractor.tt"#><##> 8 | <#+ 9 | public class LoggingDataCodeGeneratorForArgumentsBasedOnTheLogCountAttribute : IPreInvocationLoggingDataCodeGenerator 10 | { 11 | public LoggingDataCode[] Extract(CodeFunction method) 12 | { 13 | List loggingDataCodeList = new List(); 14 | 15 | foreach (CodeParameter parameter in method.Parameters) 16 | { 17 | var attributes = 18 | parameter.Attributes 19 | .Cast() 20 | .Where(x => x.FullName == "LoggingAOP.LogCountAttribute") 21 | .ToList(); 22 | 23 | foreach (var attribute in attributes) 24 | { 25 | var valueCode = 26 | parameter.Type.TypeKind.HasFlag(vsCMTypeRef.vsCMTypeRefArray) 27 | ? parameter.Name + ".Length.ToString()" 28 | : parameter.Name + ".Count.ToString()"; 29 | 30 | var nameCode = "\"" + (AttributeValueExtractor.GetAttributeSingleStringParameterValueOrNull(attribute) ?? parameter.Name) +"\""; 31 | 32 | loggingDataCodeList 33 | .Add( 34 | new LoggingDataCode( 35 | nameCode, 36 | valueCode)); 37 | } 38 | } 39 | 40 | return loggingDataCodeList.ToArray(); 41 | } 42 | } 43 | #> -------------------------------------------------------------------------------- /T4Aspects/LoggingDataCodeGeneratorForReturnValueBasedOnTheLogAttribute.tt: -------------------------------------------------------------------------------- 1 | <#@ assembly name="EnvDTE" #> 2 | <#@ assembly name="EnvDTE80" #> 3 | <#@ assembly name="System.Core" #> 4 | <#@ import namespace="EnvDTE" #> 5 | <#@ import namespace="EnvDTE80" #> 6 | <#@ import namespace="System.Collections.Generic" #> 7 | <#@ import namespace="System.Linq" #> 8 | <#@ include once="true" file="LoggingAspect.Core.tt"#><##> 9 | <#@ include once="true" file="Utilities\AttributeValueExtractor.tt"#><##> 10 | <#+ 11 | 12 | public class LoggingDataCodeGeneratorForReturnValueBasedOnTheLogAttribute : IPostInvocationLoggingDataCodeGenerator 13 | { 14 | public LoggingDataCode[] Extract(CodeFunction method, string returnValueVariableName) 15 | { 16 | List loggingDataCodeList = new List(); 17 | 18 | if(method.Type.TypeKind == vsCMTypeRef.vsCMTypeRefVoid ) 19 | return new LoggingDataCode[]{}; 20 | 21 | var attributes = 22 | method.Attributes 23 | .Cast() 24 | .Where(x => x.Target == "return" && x.FullName == "LoggingAOP.LogAttribute") 25 | .ToList(); 26 | 27 | foreach (var attribute in attributes) 28 | { 29 | var valueCode = 30 | method.Type.TypeKind == vsCMTypeRef.vsCMTypeRefString 31 | ? "returnValue" 32 | : "returnValue.ToString()"; 33 | 34 | var nameCode = "\"" + (AttributeValueExtractor.GetAttributeSingleStringParameterValueOrNull(attribute) ?? "Return Value") + "\""; 35 | 36 | loggingDataCodeList 37 | .Add( 38 | new LoggingDataCode( 39 | nameCode, 40 | valueCode)); 41 | } 42 | 43 | return loggingDataCodeList.ToArray(); 44 | } 45 | } 46 | #> -------------------------------------------------------------------------------- /T4Aspects/LoggingDataCodeGeneratorForReturnValueBasedOnTheLogCountAttribute.tt: -------------------------------------------------------------------------------- 1 | <#@ assembly name="EnvDTE" #> 2 | <#@ assembly name="EnvDTE80" #> 3 | <#@ assembly name="System.Core" #> 4 | <#@ import namespace="EnvDTE" #> 5 | <#@ import namespace="EnvDTE80" #> 6 | <#@ import namespace="System.Collections.Generic" #> 7 | <#@ import namespace="System.Linq" #> 8 | <#@ include once="true" file="LoggingAspect.Core.tt"#><##> 9 | <#@ include once="true" file="Utilities\AttributeValueExtractor.tt"#><##> 10 | <#+ 11 | public class LoggingDataCodeGeneratorForReturnValueBasedOnTheLogCountAttribute : IPostInvocationLoggingDataCodeGenerator 12 | { 13 | public LoggingDataCode[] Extract(CodeFunction method, string returnValueVariableName) 14 | { 15 | List loggingDataCodeList = new List(); 16 | 17 | if(method.Type.TypeKind == vsCMTypeRef.vsCMTypeRefVoid) 18 | return new LoggingDataCode[]{}; 19 | 20 | var attributes = 21 | method.Attributes 22 | .Cast() 23 | .Where(x => x.Target == "return" && x.FullName == "LoggingAOP.LogCountAttribute") 24 | .ToList(); 25 | 26 | foreach (var attribute in attributes) 27 | { 28 | var valueCode = 29 | method.Type.TypeKind.HasFlag(vsCMTypeRef.vsCMTypeRefArray) 30 | ? "returnValue.Length.ToString()" 31 | : "returnValue.Count.ToString()"; 32 | 33 | var nameCode = "\"" + (AttributeValueExtractor.GetAttributeSingleStringParameterValueOrNull(attribute) ?? "Return Value") + "\""; 34 | 35 | loggingDataCodeList 36 | .Add( 37 | new LoggingDataCode( 38 | nameCode, 39 | valueCode)); 40 | } 41 | 42 | return loggingDataCodeList.ToArray(); 43 | } 44 | } 45 | #> -------------------------------------------------------------------------------- /T4Aspects/MethodDescriptionLoggingDataCodeGenerator.tt: -------------------------------------------------------------------------------- 1 | <#@ assembly name="EnvDTE" #> 2 | <#@ assembly name="EnvDTE80" #> 3 | <#@ assembly name="System.Core" #> 4 | <#@ import namespace="EnvDTE" #> 5 | <#@ import namespace="EnvDTE80" #> 6 | <#@ import namespace="System.Linq" #> 7 | <#@ include once="true" file="LoggingAspect.Core.tt"#><##> 8 | <#@ include once="true" file="Utilities\AttributeValueExtractor.tt"#><##> 9 | <#+ 10 | public class MethodDescriptionLoggingDataCodeGenerator : IPreInvocationLoggingDataCodeGenerator 11 | { 12 | public LoggingDataCode[] Extract(CodeFunction method) 13 | { 14 | var attribute = 15 | method.Attributes 16 | .Cast() 17 | .FirstOrDefault(x => x.Target != "return" && x.FullName == "LoggingAOP.MethodDescriptionAttribute"); 18 | 19 | var methodDescription = 20 | attribute == null 21 | ? method.Name 22 | : AttributeValueExtractor.GetAttributeSingleStringParameterValue(attribute); 23 | 24 | return new [] 25 | { 26 | new LoggingDataCode( 27 | nameCode: "\"Method\"", 28 | valueCode: "\"" + methodDescription + "\"") 29 | }; 30 | } 31 | } 32 | #> -------------------------------------------------------------------------------- /T4Aspects/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("T4Aspects")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("T4Aspects")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("24148b11-2079-440a-a7d2-1dbdc0848855")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /T4Aspects/RetryAspect.tt: -------------------------------------------------------------------------------- 1 | <#@ assembly name="EnvDTE" #> 2 | <#@ assembly name="EnvDTE80" #> 3 | <#@ assembly name="System.Core" #> 4 | <#@ import namespace="EnvDTE" #> 5 | <#@ import namespace="EnvDTE80" #> 6 | <#@ import namespace="System.Text" #> 7 | <#@ import namespace="System.Linq" #> 8 | <#@ include once="true" file="Utilities\MethodParametersGenerator.tt" #><##> 9 | <#@ include once="true" file="Utilities\MethodMatcher.tt"#><##> 10 | <#+ 11 | public void GenerateRetryDecoratorForInterface( 12 | CodeInterface2 @interface) 13 | { 14 | string fullInterfaceName = @interface.FullName; 15 | 16 | var formattedInterfaceName = FormatInterfaceName(@interface.Name); 17 | 18 | string decoratorClassName = formattedInterfaceName + "RetryDecorator"; 19 | 20 | var @namespace = @interface.Namespace.FullName; 21 | 22 | #> 23 | 24 | namespace <#= @namespace #> 25 | { 26 | using System; 27 | using System.Threading; 28 | 29 | public class <#=decoratorClassName#> : <#=fullInterfaceName#> 30 | { 31 | private readonly <#=fullInterfaceName#> decorated; 32 | private readonly int numberOfRetries; 33 | private readonly TimeSpan waitTimeBetweenRetries; 34 | 35 | public <#=decoratorClassName#>( 36 | <#=fullInterfaceName#> decorated, 37 | int numberOfRetries, 38 | TimeSpan waitTimeBetweenRetries) 39 | { 40 | this.decorated = decorated; 41 | this.numberOfRetries = numberOfRetries; 42 | this.waitTimeBetweenRetries = waitTimeBetweenRetries; 43 | } 44 | 45 | <#+ 46 | PushIndent(new string(' ', 8)); 47 | GenerateMethodsForInterface(@interface); 48 | PopIndent(); 49 | #> 50 | } 51 | 52 | public static class ExtensionMethodsForRetryAspectFor<#= formattedInterfaceName#> 53 | { 54 | public static <#=fullInterfaceName#> ApplyRetryAspect(this <#=fullInterfaceName#> instance, int numberOfRetries, TimeSpan waitTimeBetweenRetries) 55 | { 56 | return new <#=decoratorClassName#>(instance, numberOfRetries, waitTimeBetweenRetries); 57 | } 58 | } 59 | } 60 | <#+ 61 | } 62 | 63 | private string FormatInterfaceName(string name) 64 | { 65 | if (name.Length > 1 && name[0] == 'I' && char.IsUpper(name[1])) 66 | return name.Substring(1); 67 | 68 | return name; 69 | } 70 | 71 | private void GenerateMethodsForInterface( 72 | CodeInterface @interface) 73 | { 74 | foreach (CodeFunction interfaceMethod in @interface.Members.OfType()) 75 | { 76 | bool isVoid = interfaceMethod.Type.TypeKind == vsCMTypeRef.vsCMTypeRefVoid; 77 | 78 | string methodReturnType = 79 | isVoid 80 | ? "void" 81 | : interfaceMethod.Type.AsFullName; 82 | 83 | string methodName = interfaceMethod.Name; 84 | 85 | #> 86 | public <#=methodReturnType#> <#=methodName#>(<#= MethodParameterGenerator.GenerateMethodParametersWithTypes(interfaceMethod)#>) 87 | { 88 | int retries = 0; 89 | 90 | while(true) 91 | { 92 | try 93 | { 94 | <#+ 95 | if (isVoid) 96 | { 97 | #> 98 | decorated.<#=methodName #>(<#=MethodParameterGenerator.GenerateMethodParametersWithoutTypes(interfaceMethod) #>); 99 | return; 100 | <#+ 101 | } 102 | else 103 | { 104 | #> 105 | return decorated.<#=methodName #>(<#=MethodParameterGenerator.GenerateMethodParametersWithoutTypes(interfaceMethod) #>); 106 | <#+ 107 | } 108 | #> 109 | } 110 | catch 111 | { 112 | retries++; 113 | 114 | if(retries == numberOfRetries) 115 | throw; 116 | 117 | Thread.Sleep(waitTimeBetweenRetries); 118 | } 119 | } 120 | } 121 | 122 | <#+ 123 | } 124 | } 125 | #> -------------------------------------------------------------------------------- /T4Aspects/T4Aspects.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {24148B11-2079-440A-A7D2-1DBDC0848855} 8 | Library 9 | Properties 10 | T4Aspects 11 | T4Aspects 12 | v4.5.2 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | 24 | 25 | pdbonly 26 | true 27 | bin\Release\ 28 | TRACE 29 | prompt 30 | 4 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 73 | -------------------------------------------------------------------------------- /T4Aspects/Utilities/AttributeValueExtractor.tt: -------------------------------------------------------------------------------- 1 | <#@ assembly name="EnvDTE" #> 2 | <#@ assembly name="System.Core" #> 3 | <#@ import namespace="EnvDTE" #> 4 | <#+ 5 | public static class AttributeValueExtractor 6 | { 7 | public static string GetAttributeSingleStringParameterValueOrNull(CodeAttribute attribute) 8 | { 9 | if (string.IsNullOrWhiteSpace(attribute.Value)) 10 | return null; 11 | 12 | return attribute.Value.Trim('"'); 13 | } 14 | 15 | public static string GetAttributeSingleStringParameterValue(CodeAttribute attribute) 16 | { 17 | return attribute.Value.Trim('"'); 18 | } 19 | } 20 | #> -------------------------------------------------------------------------------- /T4Aspects/Utilities/ClassFinder.tt: -------------------------------------------------------------------------------- 1 | <#@ assembly name="EnvDTE" #> 2 | <#@ assembly name="EnvDTE80" #> 3 | <#@ assembly name="System.Core" #> 4 | <#@ import namespace="EnvDTE" #> 5 | <#@ import namespace="EnvDTE80" #> 6 | <#@ import namespace="System.Collections.Generic" #> 7 | <#@ import namespace="System.Linq" #> 8 | <#+ 9 | public static class ClassFinder 10 | { 11 | public static CodeClass2[] FindClassesInSolution(DTE dte, string[] names) 12 | { 13 | List list = new List(); 14 | 15 | foreach (Project project in dte.Solution.Projects) 16 | { 17 | CodeClass2[] result = FindClassesInProject(project, names); 18 | 19 | list.AddRange(result); 20 | } 21 | 22 | return list.ToArray(); 23 | } 24 | 25 | private static CodeClass2[] FindClassesInProject(Project project, string[] names) 26 | { 27 | List list = new List(); 28 | 29 | if (project.Kind == ProjectKinds.vsProjectKindSolutionFolder) 30 | { 31 | 32 | foreach (ProjectItem projectItem in project.ProjectItems) 33 | { 34 | if(projectItem.SubProject != null) 35 | list.AddRange(FindClassesInProject(projectItem.SubProject, names)); 36 | } 37 | 38 | return list.ToArray(); 39 | } 40 | 41 | foreach (ProjectItem projectItem in project.ProjectItems) 42 | { 43 | CodeClass2[] result = FindClassInProjectItem(projectItem, names); 44 | 45 | list.AddRange(result); 46 | } 47 | 48 | return list.ToArray(); 49 | } 50 | 51 | private static CodeClass2[] FindClassInProjectItem(ProjectItem projectItem, string[] names) 52 | { 53 | List list = new List(); 54 | 55 | if (projectItem.ProjectItems != null) 56 | { 57 | foreach (ProjectItem subprojectItem in projectItem.ProjectItems) 58 | { 59 | CodeClass2[] subResult = FindClassInProjectItem(subprojectItem, names); 60 | 61 | list.AddRange(subResult); 62 | } 63 | } 64 | 65 | if (projectItem.FileCodeModel == null) 66 | return list.ToArray(); 67 | 68 | CodeClass2[] result = FindClass(projectItem.FileCodeModel.CodeElements, names); 69 | 70 | list.AddRange(result); 71 | 72 | return list.ToArray(); 73 | } 74 | 75 | private static CodeClass2[] FindClass(CodeElements elements, string[] names) 76 | { 77 | List list = new List(); 78 | 79 | foreach (CodeElement element in elements) 80 | { 81 | if (element.Kind == vsCMElement.vsCMElementClass) 82 | { 83 | if (names.Contains(element.Name)) 84 | list.Add((CodeClass2)element); 85 | } 86 | else if (element.Kind == vsCMElement.vsCMElementNamespace) 87 | { 88 | var codeNamespace = (CodeNamespace) element; 89 | 90 | CodeClass2[] result = FindClass(codeNamespace.Members, names); 91 | 92 | list.AddRange(result); 93 | } 94 | } 95 | 96 | return list.ToArray(); 97 | } 98 | } 99 | #> -------------------------------------------------------------------------------- /T4Aspects/Utilities/InterfaceFinder.tt: -------------------------------------------------------------------------------- 1 | <#@ assembly name="EnvDTE" #> 2 | <#@ assembly name="EnvDTE80" #> 3 | <#@ assembly name="System.Core" #> 4 | <#@ import namespace="EnvDTE" #> 5 | <#@ import namespace="EnvDTE80" #> 6 | <#@ import namespace="System.Collections.Generic" #> 7 | <#@ import namespace="System.Linq" #> 8 | <#+ 9 | public static class InterfaceFinder 10 | { 11 | public static CodeInterface2[] FindInterfacesInSolution(DTE dte, string[] names) 12 | { 13 | List list = new List(); 14 | 15 | foreach (Project project in dte.Solution.Projects) 16 | { 17 | CodeInterface2[] result = FindInterfacesInProject(project, names); 18 | 19 | list.AddRange(result); 20 | } 21 | 22 | return list.ToArray(); 23 | } 24 | 25 | private static CodeInterface2[] FindInterfacesInProject(Project project, string[] names) 26 | { 27 | List list = new List(); 28 | 29 | if (project.Kind == ProjectKinds.vsProjectKindSolutionFolder) 30 | { 31 | 32 | foreach (ProjectItem projectItem in project.ProjectItems) 33 | { 34 | if(projectItem.SubProject != null) 35 | list.AddRange(FindInterfacesInProject(projectItem.SubProject, names)); 36 | } 37 | 38 | return list.ToArray(); 39 | } 40 | 41 | foreach (ProjectItem projectItem in project.ProjectItems) 42 | { 43 | CodeInterface2[] result = FindInterfaceInProjectItem(projectItem, names); 44 | 45 | list.AddRange(result); 46 | } 47 | 48 | return list.ToArray(); 49 | } 50 | 51 | private static CodeInterface2[] FindInterfaceInProjectItem(ProjectItem projectItem, string[] names) 52 | { 53 | List list = new List(); 54 | 55 | if (projectItem.ProjectItems != null) 56 | { 57 | foreach (ProjectItem subprojectItem in projectItem.ProjectItems) 58 | { 59 | CodeInterface2[] subResult = FindInterfaceInProjectItem(subprojectItem, names); 60 | 61 | list.AddRange(subResult); 62 | } 63 | } 64 | 65 | if (projectItem.FileCodeModel == null) 66 | return list.ToArray(); 67 | 68 | CodeInterface2[] result = FindInterface(projectItem.FileCodeModel.CodeElements, names); 69 | 70 | list.AddRange(result); 71 | 72 | return list.ToArray(); 73 | } 74 | 75 | private static CodeInterface2[] FindInterface(CodeElements elements, string[] names) 76 | { 77 | List list = new List(); 78 | 79 | foreach (CodeElement element in elements) 80 | { 81 | if (element.Kind == vsCMElement.vsCMElementInterface) 82 | { 83 | if (names.Contains(element.Name)) 84 | list.Add((CodeInterface2)element); 85 | } 86 | else if (element.Kind == vsCMElement.vsCMElementNamespace) 87 | { 88 | var codeNamespace = (CodeNamespace) element; 89 | 90 | CodeInterface2[] result = FindInterface(codeNamespace.Members, names); 91 | 92 | list.AddRange(result); 93 | } 94 | } 95 | 96 | return list.ToArray(); 97 | } 98 | } 99 | #> -------------------------------------------------------------------------------- /T4Aspects/Utilities/MethodMatcher.tt: -------------------------------------------------------------------------------- 1 | <#@ assembly name="EnvDTE" #> 2 | <#@ assembly name="System.Core" #> 3 | <#@ import namespace="System.Linq" #> 4 | <#@ import namespace="EnvDTE" #> 5 | <#+ 6 | public static class MethodMatcher 7 | { 8 | public static CodeFunction GetClassMethodFromInterfaceMethod(CodeFunction interfaceMethod, CodeClass @class) 9 | { 10 | var interfaceMethodSignature = GetMethodSignature(interfaceMethod); 11 | 12 | return @class.Members.OfType().Single(x => GetMethodSignature(x) == interfaceMethodSignature); 13 | } 14 | 15 | private static string GetMethodSignature(CodeFunction classMethod) 16 | { 17 | var value = 18 | vsCMPrototype.vsCMPrototypeParamDefaultValues | 19 | vsCMPrototype.vsCMPrototypeParamNames | 20 | vsCMPrototype.vsCMPrototypeParamTypes | 21 | vsCMPrototype.vsCMPrototypeType; 22 | 23 | return classMethod.Prototype[(int) value]; 24 | } 25 | } 26 | #> -------------------------------------------------------------------------------- /T4Aspects/Utilities/MethodParametersGenerator.tt: -------------------------------------------------------------------------------- 1 | <#@ assembly name="EnvDTE" #> 2 | <#@ assembly name="System.Core" #> 3 | <#@ import namespace="EnvDTE" #> 4 | <#@ import namespace="System.Text" #> 5 | <#@ import namespace="System.Linq" #> 6 | <#+ 7 | public static class MethodParameterGenerator 8 | { 9 | public static string GenerateMethodParametersWithTypes(CodeFunction method) 10 | { 11 | return GenerateMethodParametersWithTypes(method, includeTypes: true); 12 | } 13 | 14 | public static string GenerateMethodParametersWithoutTypes(CodeFunction method) 15 | { 16 | return GenerateMethodParametersWithTypes(method, includeTypes: false); 17 | } 18 | 19 | private static string GenerateMethodParametersWithTypes(CodeFunction method, bool includeTypes) 20 | { 21 | var parameters = method.Parameters; 22 | 23 | StringBuilder sb = new StringBuilder(); 24 | 25 | for(int i = 1 ; i <= parameters.Count ; i++) 26 | { 27 | var param = (CodeParameter) parameters.Item(i); 28 | 29 | if(i != 1) 30 | sb.Append(", "); 31 | 32 | if (includeTypes) 33 | { 34 | sb.Append(param.Type.AsFullName); 35 | sb.Append(" "); 36 | } 37 | 38 | sb.Append(param.Name); 39 | } 40 | 41 | return sb.ToString(); 42 | } 43 | } 44 | #> --------------------------------------------------------------------------------