CINXE.COM

Aspect Oriented Programming (AOP) in C# via T4 | DotNetCurry

<!DOCTYPE html> <html lang="en"> <head id="ctl00_Head1"><meta charset="utf-8" /><title> Aspect Oriented Programming (AOP) in C# via T4 | DotNetCurry </title><meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1" /><meta name="description" content="C# examples on how we can use the Text Template Transformation Toolkit (T4) to create aspects. " /><meta name="keywords" content="Patterns &amp; Practices" /><meta name="author" content="DotNetCurry.com" /><meta name="CCBot" content="nofollow" /><link rel="shortcut icon" href="../../img/favicon.ico" /><link rel="icon" type="image/ico" href="../../img/favicon.ico" /><link rel="stylesheet" href="../../js/font-awesome/css/font-awesome.min.css" /><link rel="canonical" href="https://www.dotnetcurry.com/patterns-practices/1318/aspect-oriented-programming-aop-t4" /><link href="/content/site?v=dSwaMsW0iplPE-tk0JGVwhcZRf6VKKqSg-lTdPGvZso1" rel="stylesheet"/> <style>.async-hide { opacity: 0 !important} </style> <!-- GA disabled 17062020 --> <!--<script>(function(a,s,y,n,c,h,i,d,e){s.className+=' '+y;h.start=1*new Date; h.end=i=function(){s.className=s.className.replace(RegExp(' ?'+y),'')}; (a[n]=a[n]||[]).hide=h;setTimeout(function(){i();h.end=null},c);h.timeout=c; })(window,document.documentElement,'async-hide','dataLayer',4000, {'GTM-NJXQ5BG':true});</script> <script> (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','https://www.google-analytics.com/analytics.js','ga'); ga('create', 'UA-2417460-4', 'auto'); ga('require', 'GTM-NJXQ5BG'); ga('send', 'pageview'); </script>--> <!-- Facebook Pixel Code --> <script> !function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod? n.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n; n.push=n;n.loaded=!0;n.version='2.0';n.queue=[];t=b.createElement(e);t.async=!0; t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}(window, document,'script','https://connect.facebook.net/en_US/fbevents.js'); fbq('init', '1902706389940870', { //em: 'insert_email_variable' }); fbq('track', 'PageView'); </script> <noscript><img height="1" width="1" style="display:none" src="https://www.facebook.com/tr?id=1902706389940870&ev=PageView&noscript=1" /></noscript> <!-- DO NOT MODIFY --> <!-- End Facebook Pixel Code --> <!-- Twitter universal website tag code May 30 17 --> <script> !function(e,t,n,s,u,a){e.twq||(s=e.twq=function(){s.exe?s.exe.apply(s,arguments):s.queue.push(arguments); },s.version='1.1',s.queue=[],u=t.createElement(n),u.async=!0,u.src='//static.ads-twitter.com/uwt.js', a=t.getElementsByTagName(n)[0],a.parentNode.insertBefore(u,a))}(window,document,'script'); // Insert Twitter Pixel ID and Standard Event data below twq('init','nw0gz'); twq('track','PageView'); </script> <!-- End Twitter universal website tag code --> </head> <body> <form name="aspnetForm" method="post" action="./aspect-oriented-programming-aop-t4?ID=1318" id="aspnetForm"> <div> <input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwULLTE0MDk2ODQwMTdkZJysVikQVHzjAtZqOeUvZO1Bu1pl" /> </div> <div> <input type="hidden" name="__VIEWSTATEGENERATOR" id="__VIEWSTATEGENERATOR" value="A9C58E29" /> </div> <div class="wrapper"> <!-- header --> <header class="header header-megamenu"> <nav class="navbar navbar-default" role="navigation"> <div class="container"> <!--<div class="search-bar"> <input type="search" onkeyup="executeQuery();" placeholder="Type search text here..." name="search" id="search-keyword"> <div class="search-close" id="x"><i class="fa fa-times"></i></div> </div>--> <!-- Brand and toggle get grouped for better mobile display --> <div class="navbar-header"> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <!--<a class="navbar-brand" href="./index.html"> <img src="img/dnc-logo-tee-trans.png" srcset="img/dnc-logo-small.jpg 1980w, img/dnc-logo-x-small.jpg 320w" sizes ="100vw" class="img-responsive" alt="DNC Magazine" /> </a>--> <a class="navbar-brand" href="https://www.dotnetcurry.com"> <img src="/img/dnc-logo-small.jpg" class="img-responsive" alt="DNC Magazine" /> </a> </div> <!--<div class="search-trigger pull-right"></div>--> <div class="navbar-social pull-right visible-xs-block, hidden-xs"> <a href="https://www.facebook.com/dotnetcurry"><img src="/img/icon/fb.png" class="img-responsive" alt="DotNetCurry's Official Facebook Account" /></a> <a href="https://www.twitter.com/dotnetcurry"><img src="/img/icon/twit.png" class="img-responsive" alt="DotNetCurry's Official Twitter Account" /></a> <a href="https://github.com/dotnetcurry"><img src="/img/icon/git.png" class="img-responsive" alt="DotNetCurry's Official Github Account" /></a> </div> <!-- Collect the nav links, forms, and other content for toggling --> <div class="collapse navbar-collapse navbar-ex1-collapse"> <ul class="nav navbar-nav"> <li class="dropdown megamenu"> <a href="#" class="dropdown-toggle" data-toggle="dropdown">Tutorials <span class="fa fa-angle-down"></span></a> <ul class="dropdown-menu"> <li> <div class="row"> <div class="col-md-3"> <h5>ASP.NET</h5> <a href="https://www.dotnetcurry.com/tutorials/aspnet">ASP.NET</a> <a href="https://www.dotnetcurry.com/tutorials/aspnet-mvc">ASP.NET MVC</a> <a href="https://www.dotnetcurry.com/tutorials/aspnet-core">ASP.NET Core</a> <a href="https://www.dotnetcurry.com/tutorials/sharepoint">SharePoint</a> <a href="https://www.dotnetcurry.com/tutorials/aspnet-ajax" class="last">ASP.NET AJAX</a> <h5>Patterns and Practices</h5> <a href="https://www.dotnetcurry.com/tutorials/software-gardening">Software Gardening</a> <a href="https://www.dotnetcurry.com/tutorials/patterns-practices">Design Patterns</a> </div> <div class="col-md-3"> <h5>.NET</h5> <a href="https://www.dotnetcurry.com/tutorials/csharp">C#</a> <a href="https://www.dotnetcurry.com/tutorials/visualstudio">Visual Studio</a> <a href="https://www.dotnetcurry.com/tutorials/vsts-tfs">VSTS (Azure DevOps) & TFS</a> <a href="https://www.dotnetcurry.com/tutorials/linq">LINQ</a> <a href="https://www.dotnetcurry.com/tutorials/entityframework">Entity Framework</a> <a href="https://www.dotnetcurry.com/tutorials/dotnetframework">.NET Framework</a> <a href="https://www.dotnetcurry.com/tutorials/dotnet-standard-core">.NET Standard & .NET Core</a> <a href="https://www.dotnetcurry.com/tutorials/wpf">WPF</a> <a href="https://www.dotnetcurry.com/tutorials/wcf">WCF</a> <a href="https://www.dotnetcurry.com/tutorials/winforms">WinForms</a> </div> <div class="col-md-3"> <h5>Cloud and Mobile</h5> <a href="https://www.dotnetcurry.com/tutorials/windows-azure">Microsoft Azure</a> <a href="https://www.dotnetcurry.com/tutorials/devops">DevOps</a> <a href="https://www.dotnetcurry.com/tutorials/xamarin">Xamarin</a> <a href="https://www.dotnetcurry.com/tutorials/powershell">Powershell</a> <a href="https://www.dotnetcurry.com/tutorials/machine-learning-ai">Machine Learning & AI</a> <a href="https://www.dotnetcurry.com/tutorials/windows-store">UWP & Windows Store</a> <a href="https://www.dotnetcurry.com/tutorials/windowsphone" class="last">Windows Phone</a> <h5>Useful</h5> <a href="https://www.dotnetcurry.com/tutorials/dotnetinterview">.NET Interview Q&A</a> <a href="https://www.dotnetcurry.com/tutorials/product-articles-review">Product Reviews</a> <a href="https://www.dotnetcurry.com/tutorials/general-programming-topics">General Topics</a> </div> <div class="col-md-3"> <h5>JavaScript</h5> <a href="https://www.dotnetcurry.com/tutorials/jquery-aspnet">jQuery</a> <a href="https://www.dotnetcurry.com/tutorials/angularjs">Angular</a> <a href="https://www.dotnetcurry.com/tutorials/typescript">TypeScript</a> <a href="https://www.dotnetcurry.com/tutorials/nodejs">Node.js</a> <a href="https://www.dotnetcurry.com/tutorials/reactjs">React.js</a> <a href="https://www.dotnetcurry.com/tutorials/backbonejs">Backbone.js</a> <a href="https://www.dotnetcurry.com/tutorials/html5-javascript">HTML5 & JavaScript</a> <a href="https://www.dotnetcurry.com/tutorials/bootstrap-css">Bootstrap & CSS</a> </div> <div class="col-md-3"> <h5>Publications</h5> <a href="http://www.jquerycookbook.com/">Books</a> <a href="https://www.dotnetcurry.com/magazine/" class="last">Magazines</a> <h5>Older Technologies</h5> <a href="https://www.dotnetcurry.com/tutorials/silverlight">Silverlight</a> <a href="https://www.dotnetcurry.com/tutorials/expression-web">Expression Web</a> <a href="https://www.dotnetcurry.com/tutorials/expression-blend">Expression Blend</a> <a href="https://www.dotnetcurry.com/tutorials/windows-vista">Windows Vista</a> <a href="https://www.dotnetcurry.com/tutorials/microsoft-word">Word 2007</a> <a href="https://www.dotnetcurry.com/tutorials/microsoft-outlook">Outlook 2007</a> <a href="#"></a> </div> </div> </li> </ul> </li> <li class="dropdown megamenu"> <a href="#" class="dropdown-toggle" data-toggle="dropdown">Featured <span class="fa fa-angle-down"></span></a> <ul class="dropdown-menu"> <li> <div class="row"> <div class="col-md-3 col-xs-12"> <div class="header-post"> <a href="https://www.dotnetcurry.com/angular/1385/angular-4-cheat-sheet"> <div class="hp-thumb"> <img data-src="/img/header/1385.jpg" class="img-responsive lazyload" alt="" /> </div> </a> <date>Aug 30, 2017</date> <h4><a href="https://www.dotnetcurry.com/angular/1385/angular-4-cheat-sheet">Angular 4 Development Cheat Sheet</a></h4> <p class="hidden-xs">A quick reference guide to get you going with Angular development. It uses Angular v4 with TypeScript.</p> </div> </div> <div class="col-md-3 col-xs-12"> <div class="header-post"> <a href="https://www.dotnetcurry.com/patterns-practices/1364/error-handling-dotnet-projects"> <div class="hp-thumb"> <!-- <div class="hp-overlay"> <img src="img/header/gallery.png" alt=""/> <span>12 Photos</span> </div> --> <img data-src="/img/header/1364.jpg" class="img-responsive lazyload" alt="" /> </div> </a> <date>May 12, 2017</date> <h4><a href="https://www.dotnetcurry.com/patterns-practices/1364/error-handling-dotnet-projects">Error Handling in Large .NET Projects - Best Practices</a></h4> <p class="hidden-xs">Learn some effective error handling strategies that you can use in your .NET projects.</p> </div> </div> <div class="col-md-3 col-xs-12"> <div class="header-post"> <a href="https://www.dotnetcurry.com/patterns-practices/1375/behavior-driven-development-bdd"> <div class="hp-thumb"> <img data-src="/img/header/1375.jpg" class="img-responsive lazyload" alt="" /> </div> </a> <date>July 3, 2017</date> <h4><a href="https://www.dotnetcurry.com/patterns-practices/1375/behavior-driven-development-bdd">Behavior Driven Development (BDD) – an in-depth look</a></h4> <p class="hidden-xs">Learn how Behavior Driven Development (BDD) works with a real-world example of how to use it.</p> </div> </div> <div class="col-md-3 col-xs-12"> <div class="header-post"> <a href="https://www.dotnetcurry.com/patterns-practices/1305/aspect-oriented-programming-aop-csharp-using-solid"> <div class="hp-thumb"> <img data-src="/img/header/1305.jpg" class="img-responsive lazyload" alt="" /> </div> </a> <date>Sep 25, 2016</date> <h4><a href="https://www.dotnetcurry.com/patterns-practices/1305/aspect-oriented-programming-aop-csharp-using-solid">Aspect Oriented Programming (AOP) in C# with SOLID</a></h4> <p class="hidden-xs">Aspect Oriented Programming (AOP) in C# using SOLID principles, with challenges and solutions.</p> </div> </div> <div class="col-md-3 col-xs-12"> <div class="header-post"> <a href="https://www.dotnetcurry.com/javascript/1359/javascript-frameworks-aspnet-mvc-developer"> <div class="hp-thumb"> <img data-src="/img/header/1359.jpg" class="img-responsive lazyload" alt="" /> </div> </a> <date>Sep 25, 2016</date> <h4><a href="https://www.dotnetcurry.com/javascript/1359/javascript-frameworks-aspnet-mvc-developer">JavaScript Frameworks for ASP.NET MVC Developers</a></h4> <p class="hidden-xs">Learn about a few JavaScript frameworks, and which one will be a good fit in your ASP.NET MVC apps</p> </div> </div> </div> </li> </ul> </li> <li class="dropdown megamenu"> <a href="https://www.dotnetcurry.com/magazine/">.NET Magazines</a> </li> <li class="dropdown dropdown-v1"> <a href="#" class="dropdown-toggle" data-toggle="dropdown">About<span class="fa fa-angle-down"></span></a> <ul class="dropdown-menu"> <li><a href="https://www.dotnetcurry.com/About.aspx">About Us</a></li> <li><a href="https://www.dotnetcurry.com/WriteForUs.aspx">Write For Us</a></li> <li><a href="https://www.dotnetcurry.com/Contact.aspx">Contact Us</a></li> </ul> </li> <!--<li class="dropdown megamenu"> <a href="https://www.dotnetcurry.net/s/dnc-products">Developer Tools</a> </li>--> </ul> </div> <!-- /.navbar-collapse --> </div> </nav> </header> <!--header--> <!-- container --> <div class="container"> <div class="clearfix divborder"> <div class="col-md-8 column"> <div class="col-md-12 articlebox row"> <div class="articletitle"> <h1 class="articlehead"> <span id="ctl00_MainContent_lblTitle">Aspect Oriented Programming (AOP) in C# via T4</span> </h1> <b>Posted by: </b> <a id="ctl00_MainContent_lnkAddedBy" href="../../Author.aspx?AuthorName=Yacoub Massad">Yacoub Massad</a> , on 11/12/2016, in <b> Category </b> <a id="ctl00_MainContent_lnkCategory" href="../../BrowseArticles.aspx?CatID=72">Patterns & Practices</a> <br /> </div> <div class="articlestats"> <div class="stats1"> <b>Views: </b> 30847 <br/> </div> </div> <div class="articleabstract"> <b>Abstract: </b> C# examples on how we can use the Text Template Transformation Toolkit (T4) to create aspects. </div> <div class="socialshare"> <a class="resp-sharing-button__link" id="fbsharelinktop" target="_blank" aria-label=""> <div class="resp-sharing-button resp-sharing-button--facebook resp-sharing-button--small"><div aria-hidden="true" class="resp-sharing-button__icon resp-sharing-button__icon--solid"> <svg version="1.1" x="0px" y="0px" width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve"> <g> <path d="M18.768,7.465H14.5V5.56c0-0.896,0.594-1.105,1.012-1.105s2.988,0,2.988,0V0.513L14.171,0.5C10.244,0.5,9.5,3.438,9.5,5.32 v2.145h-3v4h3c0,5.212,0,12,0,12h5c0,0,0-6.85,0-12h3.851L18.768,7.465z"/> </g> </svg> <span></span> </div> </div> </a> <a class="resp-sharing-button__link" id="twtsharelinktop" target="_blank" aria-label=""> <div class="resp-sharing-button resp-sharing-button--twitter resp-sharing-button--small"><div aria-hidden="true" class="resp-sharing-button__icon resp-sharing-button__icon--solid"> <svg version="1.1" x="0px" y="0px" width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve"> <g> <path d="M23.444,4.834c-0.814,0.363-1.5,0.375-2.228,0.016c0.938-0.562,0.981-0.957,1.32-2.019c-0.878,0.521-1.851,0.9-2.886,1.104 C18.823,3.053,17.642,2.5,16.335,2.5c-2.51,0-4.544,2.036-4.544,4.544c0,0.356,0.04,0.703,0.117,1.036 C8.132,7.891,4.783,6.082,2.542,3.332C2.151,4.003,1.927,4.784,1.927,5.617c0,1.577,0.803,2.967,2.021,3.782 C3.203,9.375,2.503,9.171,1.891,8.831C1.89,8.85,1.89,8.868,1.89,8.888c0,2.202,1.566,4.038,3.646,4.456 c-0.666,0.181-1.368,0.209-2.053,0.079c0.579,1.804,2.257,3.118,4.245,3.155C5.783,18.102,3.372,18.737,1,18.459 C3.012,19.748,5.399,20.5,7.966,20.5c8.358,0,12.928-6.924,12.928-12.929c0-0.198-0.003-0.393-0.012-0.588 C21.769,6.343,22.835,5.746,23.444,4.834z"/> </g> </svg> <span></span> </div> </div> </a> <a class="resp-sharing-button__link" id="linkdinsharelinktop" target="_blank" aria-label=""> <div class="resp-sharing-button resp-sharing-button--linkedin resp-sharing-button--small"><div aria-hidden="true" class="resp-sharing-button__icon resp-sharing-button__icon--solid"> <svg xmlns="https://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M6.5 21.5h-5v-13h5v13zM4 6.5C2.5 6.5 1.5 5.3 1.5 4s1-2.4 2.5-2.4c1.6 0 2.5 1 2.6 2.5 0 1.4-1 2.5-2.6 2.5zm11.5 6c-1 0-2 1-2 2v7h-5v-13h5V10s1.6-1.5 4-1.5c3 0 5 2.2 5 6.3v6.7h-5v-7c0-1-1-2-2-2z"/></svg> <span></span> </div> </div> </a> <!--<a class="resp-sharing-button__link" id="googsharelinktop" target="_blank" aria-label=""> <div class="resp-sharing-button resp-sharing-button--google resp-sharing-button--small"><div aria-hidden="true" class="resp-sharing-button__icon resp-sharing-button__icon--solid"> <svg version="1.1" x="0px" y="0px" width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve"> <g> <path d="M11.366,12.928c-0.729-0.516-1.393-1.273-1.404-1.505c0-0.425,0.038-0.627,0.988-1.368 c1.229-0.962,1.906-2.228,1.906-3.564c0-1.212-0.37-2.289-1.001-3.044h0.488c0.102,0,0.2-0.033,0.282-0.091l1.364-0.989 c0.169-0.121,0.24-0.338,0.176-0.536C14.102,1.635,13.918,1.5,13.709,1.5H7.608c-0.667,0-1.345,0.118-2.011,0.347 c-2.225,0.766-3.778,2.66-3.778,4.605c0,2.755,2.134,4.845,4.987,4.91c-0.056,0.22-0.084,0.434-0.084,0.645 c0,0.425,0.108,0.827,0.33,1.216c-0.026,0-0.051,0-0.079,0c-2.72,0-5.175,1.334-6.107,3.32C0.623,17.06,0.5,17.582,0.5,18.098 c0,0.501,0.129,0.984,0.382,1.438c0.585,1.046,1.843,1.861,3.544,2.289c0.877,0.223,1.82,0.335,2.8,0.335 c0.88,0,1.718-0.114,2.494-0.338c2.419-0.702,3.981-2.482,3.981-4.538C13.701,15.312,13.068,14.132,11.366,12.928z M3.66,17.443 c0-1.435,1.823-2.693,3.899-2.693h0.057c0.451,0.005,0.892,0.072,1.309,0.2c0.142,0.098,0.28,0.192,0.412,0.282 c0.962,0.656,1.597,1.088,1.774,1.783c0.041,0.175,0.063,0.35,0.063,0.519c0,1.787-1.333,2.693-3.961,2.693 C5.221,20.225,3.66,19.002,3.66,17.443z M5.551,3.89c0.324-0.371,0.75-0.566,1.227-0.566l0.055,0 c1.349,0.041,2.639,1.543,2.876,3.349c0.133,1.013-0.092,1.964-0.601,2.544C8.782,9.589,8.363,9.783,7.866,9.783H7.865H7.844 c-1.321-0.04-2.639-1.6-2.875-3.405C4.836,5.37,5.049,4.462,5.551,3.89z"/> <polygon points="23.5,9.5 20.5,9.5 20.5,6.5 18.5,6.5 18.5,9.5 15.5,9.5 15.5,11.5 18.5,11.5 18.5,14.5 20.5,14.5 20.5,11.5 23.5,11.5 "/> </g> </svg> </div> </div> </a>--> </div> </div> <div id="articleBody" class="col-md-12 row"> <p>Aspect Oriented Programming (AOP) allows us to remove code duplication and code tangling that we get when we address cross-cutting concerns (e.g. logging). This allows us to focus on the real problem that we are trying to solve, as supporting functions and logic for cross-cutting concerns are isolated. In the article <a href="http://bit.ly/dnc-aop-solid">Aspect Oriented Programming in C# with SOLID</a>, I discussed some options for doing AOP in SOLID code bases. For more details on what AOP is, and the problems we face when we do AOP in SOLID code bases, please refer to that article.</p> <p><em>This article is published from the <strong>DNC Magazine for Developers and Architects. </strong></em><a href="https://www.dotnetcurry.net/s/dnc-mag-27th-single" rel="nofollow" target="_blank"><strong>Download this magazine from here</strong></a><em> [PDF] or </em><a href="https://www.dotnetcurry.com/magazine/"><em>Subscribe to this magazine for FREE</em></a><em> and download all previous and current editions.</em></p> <p>One of the options for doing AOP is via Text Template Transformation Toolkit a.k.a. <b>T4</b>. This article will discuss T4 in more details, provide examples for creating a retry aspect as well as a logging aspect via T4, and discuss the benefits of such an approach.</p> <p>The example code is available on GitHub at <a href="http://bit.ly/dnc-m27-aopviat4">http://bit.ly/dnc-m27-aopviat4</a>.</p> <h2><strong>Text Template Transformation Toolkit (T4)</strong></h2> <p>The Text Template Transformation Toolkit (T4) is a framework in Visual Studio that allows developers to generate text, based on templates. Developers write templates that can contain static text, and also code blocks that allow for the generation of dynamic text. Consider the following T4 example:</p> <p><img src="https://www.dotnetcurry.com/images/codingpatterns/aop-t4/t4-example.png" alt="T4 Example" /></p> <p>I have highlighted the static text in the template. The other part of the template is code written in C# that controls the dynamic generation of text. This code has to be inside a control block. In this example, we use two types of control blocks.</p> <p>The first type is the <i>standard control block</i>. The code in this block is surrounded by the &lt;# and #&gt; brackets, and it allows us to write code statements.</p> <p>The second type is the <i>expression control block</i>. The code in this block is surrounded by the &lt;#= and #&gt; brackets, and it allows us to specify a single expression.</p> <p>This particular template will generate an XML document that looks like this:</p> <pre class="brush: xml;"> &lt;People&gt; &lt;Person Age=&quot;20&quot;/&gt; &lt;Person Age=&quot;21&quot;/&gt; &lt;Person Age=&quot;22&quot;/&gt; &lt;Person Age=&quot;23&quot;/&gt; &lt;Person Age=&quot;24&quot;/&gt; &lt;Person Age=&quot;25&quot;/&gt; &lt;Person Age=&quot;26&quot;/&gt; &lt;Person Age=&quot;27&quot;/&gt; &lt;Person Age=&quot;28&quot;/&gt; &lt;Person Age=&quot;29&quot;/&gt; &lt;/People&gt; </pre> <p>Although the &lt;person&gt; tag is static in the template, it is surrounded by a standard control block that loops from 20 to 29. This will generate the static text multiple times. Notice how we have the &lt;#= i #&gt; expression control block to vary the value of the <i>Age</i> attribute in each iteration.</p> <p>There is a third type of control block too (which we aren&rsquo;t using in this example) called the <i>class feature control block</i>. This code block has the &lt;#+ and #&gt; brackets and allows us to write methods, fields, properties and even new interfaces and classes inside it.</p> <p>T4 supports two kinds of templates; runtime templates and design time templates.</p> <p><i>Runtime templates</i> allow the generation of text during the execution of our applications. For example, one could use a runtime template to generate some report based on data from the database.</p> <p><i>Design time templates</i> allows the generation of text during design time. For example, one could use a design-time template to generate some C# code for a logging decorator for some class at design time.</p> <p>Since in this article we are interested to use T4 to create aspects at design time, we are going to use design time templates.</p> <p>This article is about AOP via T4, and not an introduction to T4. Further information about T4 can be obtained from this article on MSDN: <a href="https://msdn.microsoft.com/en-us/library/bb126445.aspx">Code Generation and T4 Text Templates</a> (bit.ly/dnc-t4-msdn). I will be explaining more about relevant T4 features in later sections.</p> <h3><strong>C# examples on GitHub</strong></h3> <p>I have created C# examples on GitHub to demonstrate the ideas in this article. The solution contains 5 projects:</p> <table cellspacing="0" cellpadding="2" width="600" border="1"> <tbody> <tr> <td valign="top" width="136">Project</td> <td valign="top" width="464">Description</td> </tr> <tr> <td valign="top" width="136">T4Aspects</td> <td valign="top" width="464">This project contains <a href="https://github.com/ymassad/AOPViaT4Examples/blob/master/T4Aspects/RetryAspect.tt">RetryAspect.tt</a>, <a href="https://github.com/ymassad/AOPViaT4Examples/blob/master/T4Aspects/LoggingAspect.tt">LoggingAspect.tt</a>, and other .tt files. These two files represent the Retry, and the Logging aspects respectively. These aspects are generic and do not apply to specific types. Many applications can use these aspects to apply them to their own types.</td> </tr> <tr> <td valign="top" width="136">Library</td> <td valign="top" width="464">Contains the <a href="https://github.com/ymassad/AOPViaT4Examples/blob/master/Library/IDocumentSource.cs">IDocumentSource</a> interface and a sample implementation of this interface, the <a href="https://github.com/ymassad/AOPViaT4Examples/blob/master/Library/DocumentSource.cs">DocumentSource</a> class. This represents a library where one would put the application code.</td> </tr> <tr> <td valign="top" width="136">App</td> <td valign="top" width="464"> <p>This is the project that contains the <a href="http://blog.ploeh.dk/2011/07/28/CompositionRoot/">Composition Root</a> of the application. This is where the different classes are wired together. This is also where we apply the aspects to objects. This project also contains the <a href="https://github.com/ymassad/AOPViaT4Examples/blob/master/App/RetryAspectLocal.tt">RetryAspectLocal.tt</a> and <a href="https://github.com/ymassad/AOPViaT4Examples/blob/master/App/LoggingAspectLocal.tt">LoggingAspectLocal.tt</a> files. These files are not the real aspects, instead they consume the aspect files found in the T4Aspects project to generate the decorators relevant to the application. Under these files, you can find <a href="https://github.com/ymassad/AOPViaT4Examples/blob/master/App/RetryAspectLocal.cs">RetryAspectLocal.cs</a> and <a href="https://github.com/ymassad/AOPViaT4Examples/blob/master/App/LoggingAspectLocal.cs">LoggingAspectLocal.cs</a> respectively. These files contain the decorators generated by the T4 engine.</p> <p>Make sure that this project is set as the startup project when you want to run the application.</p> </td> </tr> <tr> <td valign="top" width="136">LoggingAOP</td> <td valign="top" width="464">This project contains some attributes that can be used to decorate methods and parameters so that the logging aspect knows which data needs to be logged.</td> </tr> <tr> <td valign="top" width="136">Logging</td> <td valign="top" width="464">This project contains the <a href="https://github.com/ymassad/AOPViaT4Examples/blob/master/Logging/ILogger.cs">ILogger</a> interface and the basic <a href="https://github.com/ymassad/AOPViaT4Examples/blob/master/Logging/ConsoleLogger.cs">ConsoleLogger</a> implementation.</td> </tr> </tbody> </table> <h3><strong>A simple example: a retry aspect</strong></h3> <p>Sometimes, we want a certain operation to retry in case of failure. We can create an aspect that causes method invocations to retry if an exception is thrown. One way to do this is to create a T4 template that generates decorators for our interfaces that retries the execution of the decorated methods.</p> <p>Let&rsquo;s consider the following interface that represents a document source from which we can obtain documents of some format:</p> <pre class="brush: csharp;"> public interface IDocumentSource { Document[] GetDocuments(string format); }</pre> <p>We can manually write a decorator for this interface to support retrying the GetDocuments() method like this:</p> <pre class="brush: csharp;"> public class DocumentSourceRetryDecorator : IDocumentSource { private readonly IDocumentSource decorated; private readonly int numberOfRetries; private readonly TimeSpan waitTimeBetweenRetries; //... public Document[] GetDocuments(string format) { int retries = 0; while(true) { try { return decorated.GetDocuments(format); } catch { retries++; if(retries == numberOfRetries) throw; Thread.Sleep(waitTimeBetweenRetries); } } } }</pre> <p>However, we don&rsquo;t want to repeat the same thing for all interfaces in our code base. Instead, we create a T4 template that can generate a similar decorator for any number of interfaces that we like.</p> <p>Let&rsquo;s start by considering what would change in this code for different interfaces:</p> <p>1. The class name needs to change. More specifically, &ldquo;DocumentSource&rdquo; at the beginning of the class name needs to change.</p> <p>2. The implemented interface name (after the column) needs to change.</p> <p>3. The type of the &ldquo;decorated&rdquo; field needs to change.</p> <p>4. The method signature (return type, method name, and parameters) needs to change.</p> <p>5. The method call inside the try block needs to change to reflect the method signature. If the target method has no return value (its return type is void), then the &ldquo;return&rdquo; keyword before the method call should be removed, and instead, a &ldquo;return;&rdquo; statement should be put after the method call.</p> <p>6. If the other interface has multiple methods, we need to generate a method in the decorator for each of the methods in the interface.</p> <p>We have basically identified the pieces that need to be generated dynamically. The rest of the code is static and doesn&rsquo;t change from interface to interface.</p> <p>Let&rsquo;s discuss the retry aspect example. Please take a look at the <a href="https://github.com/ymassad/AOPViaT4Examples/blob/master/T4Aspects/RetryAspect.tt">RetryAspect.tt</a> file. At the top of the file, we have some T4 directives. T4 directives provide instructions to the T4 engine. For example,</p> <p>&middot; the <b>assembly</b> directive in the first line informs the engine to load the EnvDTE assembly because the code inside the template is going to consume types inside that assembly.</p> <p>&middot; the <b>import</b> directive works like the <a href="https://msdn.microsoft.com/en-us/library/sf0df423.aspx">using directive</a> in C# in that it allows us to reference certain types in some namespace without typing the full name of the type.</p> <p>&middot; the <b>include</b> directive allows us to include the content of another .tt file inside the current template file. This allows us to create helper methods or classes and use them from multiple templates.</p> <p>Please note that these .tt files that contain helper methods/classes are usually not meant to be used directly by the T4 engine to generate text/code. Therefore, it is best to let Visual Studio know never to execute the T4 engine on these files. To do so, we can click on the .tt file, and then in the properties window clear the value of the Custom Tool property. For example, the RetryAspect.tt file is not meant to be executed directly by the T4 engine. As you will see later in this article, a template file called RetryAspectLocal.tt that includes RetryAspect.tt is the template that the T4 engine will execute.</p> <p>For more information on T4 directives, you might want to check the <a href="https://msdn.microsoft.com/en-us/library/bb126421.aspx">T4 Text Template Directives</a> (<a href="http://bit.ly/dnc-t4dir-msdn">bit.ly/dnc-t4dir-msdn</a>) reference from MSDN.</p> <p><strong>The Visual Studio Automation Object Model</strong></p> <p>The first two assemblies referenced from our template are EnvDTE and EnvDTE80. These assemblies contain types that allow us to interact with Visual Studio. We will use this in our aspect to search for some interfaces in the current solution to create the decorators for. We will be able to enumerate methods inside any interface, get the signature of these methods, and read any attributes on the methods if we want.</p> <p>This looks a lot like <a href="https://msdn.microsoft.com/en-us/library/mt656691.aspx">Reflection</a>. However, the difference is that Reflection works only with code that is compiled into assemblies, while the Visual Studio Automation Object Model can work with code in the open solution in Visual Studio, even if it is not yet compiled.</p> <p><strong>The GenerateRetryDecoratorForInterface method</strong></p> <p>Let us continue exploring the <a href="https://github.com/ymassad/AOPViaT4Examples/blob/master/T4Aspects/RetryAspect.tt">RetryAspect.tt</a> file. After the T4 directives, we have a class feature control block that contains a C# method called GenerateRetryDecoratorForInterface. This method takes an object of type CodeInterface2 (which is a type from the EnvDTE80 assembly) and generates a retry decorator for the interface represented by this object. We use this object to obtain information about this interface like its full name (e.g. Library.IDocumentSource), and the namespace where it resides. We also generate the name of the decorator that we want to create. The FormatInterfaceName method (found in the same file) removes the &ldquo;I&rdquo; from the interface name so that it becomes &ldquo;prettier&rdquo; when we use it to generate the name of the decorator.</p> <p>Next, we have some static text that represents the retry decorator that will be generated. Notice how we use expression code blocks (the ones surrounded by &lt;#= and #&gt;) to insert some dynamic content (e.g. the namespace, the name of the decorator, and the implemented interface) between the static text.</p> <p>We then have a class feature control block that calls the GenerateMethodsForInterface method. This method is going to generate the methods of the decorator. We use the <a href="https://msdn.microsoft.com/en-us/library/microsoft.visualstudio.texttemplating.texttransformation.pushindent.aspx">PushIndent</a> and <a href="https://msdn.microsoft.com/en-us/library/microsoft.visualstudio.texttemplating.texttransformation.popindent.aspx">PopIndent</a> T4 methods to control the indentation of the generated code. We pass eight spaces to the PushIndent method so that any code generated inside the GenerateMethodsForInterface method is pushed eight spaces to the right.</p> <p>Before we move on to the GenerateMethodsForInterface method, note that after the decorator class is generated, we generate another static class whose name starts with ExtentionMethodsForRetryAspectFor and ends with the formatted interface name. This static class contains a single extension method that allows us to decorate objects in a more readable way. Take a look at the <a href="https://github.com/ymassad/AOPViaT4Examples/blob/master/App/Program.cs">Composition Root of the application</a> to see how the generated ApplyRetryAspect method is used.</p> <p><strong>The GenerateMethodsForInterface method</strong></p> <p>This method will loop through the methods of the interface, and for each one, it will generate a method inside the decorator. As we did before, we first obtain some information about each method from inside the control block. For example, we determine whether the method has a &ldquo;void&rdquo; return type, and we store this information in the <i>isVoid</i> variable. Then we have the code of the decorator method as static text that includes some control blocks to dynamically generate code relevant to the current method. For example, note how the method call inside the try block is generated differently based on the <i>isVoid</i> variable.</p> <p><strong>The RetryAspectLocal.tt file</strong></p> <p><a href="https://github.com/ymassad/AOPViaT4Examples/blob/master/App/RetryAspectLocal.tt">This file</a> lives inside the App project. It searches for some interfaces (currently the IDocumentSource interface only) using the InterfaceFinder.FindInterfacesInSolution helper method (found in the <a href="https://github.com/ymassad/AOPViaT4Examples/blob/master/T4Aspects/Utilities/InterfaceFinder.tt">InterfaceFinder.tt</a> file in the T4Aspects project). It then loops through the interfaces found and invokes the GenerateRetryDecoratorForInterface method from the RetryAspect.tt file to generate a retry decorator for each interface.</p> <p>To see the power of what we have done so far, let&rsquo;s create a new interface inside the Library project, and then have T4 generate a retry decorator for the new interface.</p> <p>1. Create the following interface inside the Library project (feel free to create any other interface if you want):</p> <pre class="brush: csharp;"> public interface IDocumentProcessor { void ProcessDocument(Document document); }</pre> <p>2. In the RetryAspectLocal.tt file, include &ldquo;IDocumentProcessor&rdquo; in the list of interfaces to search for like this:</p> <pre class="brush: csharp;"> var interfaces = InterfaceFinder.FindInterfacesInSolution(dte, new[] {&quot;IDocumentSource&quot;, &quot;IDocumentProcessor&quot;});</pre> <p>1. Right-click the RetryAspectLocal.tt file in the Solution Explorer window, and click Run Custom Tool. This will run the T4 engine and generate the decorators.</p> <p>2. Go to the RetryAspectLocal.cs file to see the generated DocumentProcessorRetryDecorator class and the ExtentionMethodsForRetryAspectForDocumentProcessor class.</p> <p><strong>A challenge for the reader</strong></p> <p>For asynchronous methods that return <a href="https://msdn.microsoft.com/en-us/library/system.threading.tasks.task(v=vs.110).aspx">Task</a> or <a href="https://msdn.microsoft.com/en-us/library/dd321424(v=vs.110).aspx">Task &lt;TResult&gt; </a>, we need to <a href="https://msdn.microsoft.com/en-us/library/hh156528.aspx">await</a> the tasks returned from the decorated methods. Also, it makes sense to asynchronously wait (before retrying to execute the method again) via the <a href="https://msdn.microsoft.com/en-us/library/hh139096(v=vs.110).aspx">Task.Delay</a> method instead of using Thread.Sleep.</p> <p><b>What changes need to be made to support such asynchronous methods?</b></p> <h3><strong>A more advanced example: a logging aspect</strong></h3> <p>The example solution also contains a logging aspect. The purpose of this aspect is to separate the logging logic of any class into its own decorator. To see how the end result would look like, take a look at the DocumentSourceLoggingDecorator class in the generated <a href="https://github.com/ymassad/AOPViaT4Examples/blob/master/App/LoggingAspectLocal.cs">LoggingAspectLocal.cs</a> file.</p> <p>As with the retry aspect, the logging aspect lives inside the T4Aspects project in the <a href="https://github.com/ymassad/AOPViaT4Examples/blob/master/T4Aspects/LoggingAspect.tt">LoggingAspect.tt</a> file. However, there are some dependencies for this aspect. Take a look at the GenerateLoggingDecoratorForClass method inside the LoggingAspect.tt file. It takes in a CodeClass2 object representing the class that we are going to generate the decorator for, a CodeInterface object representing the interface that will be implemented by the decorator object, a IPreInvocationLoggingDataCodeGenerator object, and a IPostInvocationLoggingDataCodeGenerator object.</p> <p>The last two parameters for this method represent seams that allow us to customize the logging aspect. The first one of these two parameters represent an object that knows how to generate logging data code prior to executing the decorated methods.</p> <p>To explain this, let&rsquo;s first take a look at the IPreInvocationLoggingDataCodeGenerator interface in the <a href="https://github.com/ymassad/AOPViaT4Examples/blob/master/T4Aspects/LoggingAspect.Core.tt">LoggingAspect.Core.tt file</a>. This interface has a method called Extract that takes in a CodeFunction object that represents a method, and returns an array of LoggingDataCode objects that contain the <b>code (as strings)</b> that knows how to access the name and value of the data to be logged.</p> <p>For an example implementation of this interface, let&rsquo;s look at the <a href="https://github.com/ymassad/AOPViaT4Examples/blob/master/T4Aspects/LoggingDataCodeGeneratorForArgumentsBasedOnTheLogAttribute.tt">LoggingDataCodeGeneratorForArgumentsBasedOnTheLogAttribute</a> class. This class extracts logging data code based on the Log attribute that developers can use to decorate method parameters in their classes. Take a look at the GetDocuments method in the <a href="https://github.com/ymassad/AOPViaT4Examples/blob/master/Library/DocumentSource.cs">DocumentSource</a> class:</p> <pre class="brush: csharp;"> public Document[] GetDocuments([Log(&quot;Document Format&quot;)] string format) { return Enumerable .Range(0, 10) .Select(x =&gt; new Document(&quot;document&quot; + x, &quot;content&quot; + x)) .ToArray(); }</pre> <p>The <i>format</i> parameter is decorated with the <a href="https://github.com/ymassad/AOPViaT4Examples/blob/master/LoggingAOP/LogAttribute.cs">Log</a> attribute. The LoggingDataCodeGeneratorForArgumentsBasedOnTheLogAttribute class would in this case generate a LoggingDataCode object that contains the following code (as strings):</p> <p>NameCode: &quot;Document Format&quot;</p> <p>ValueCode: format</p> <p>When a decorator is generated (in <a href="https://github.com/ymassad/AOPViaT4Examples/blob/master/App/LoggingAspectLocal.cs">LoggingAspectLocal.cs</a>), the following code will be included in the generated class as a result of this:</p> <pre class="brush: csharp;"> new LoggingData{ Name = &quot;Document Format&quot;, Value = format},</pre> <p>So the content of NameCode is actually C# code that will be put after &ldquo;Name = &rdquo;, and the content of ValueCode is C# code that would be put after &ldquo;Value = &ldquo;.</p> <p>The other LoggingDataCode generator classes work in a similar way. Here is a list of these classes:</p> <p><a href="https://github.com/ymassad/AOPViaT4Examples/blob/master/T4Aspects/LoggingDataCodeGeneratorForArgumentsBasedOnTheLogAttribute.tt">LoggingDataCodeGeneratorForArgumentsBasedOnTheLogAttribute</a>, <a href="https://github.com/ymassad/AOPViaT4Examples/blob/master/T4Aspects/LoggingDataCodeGeneratorForArgumentsBasedOnTheLogCountAttribute.tt">LoggingDataCodeGeneratorForArgumentsBasedOnTheLogCountAttribute</a>, <a href="https://github.com/ymassad/AOPViaT4Examples/blob/master/T4Aspects/LoggingDataCodeGeneratorForReturnValueBasedOnTheLogAttribute.tt">LoggingDataCodeGeneratorForReturnValueBasedOnTheLogAttribute</a>, <a href="https://github.com/ymassad/AOPViaT4Examples/blob/master/T4Aspects/LoggingDataCodeGeneratorForReturnValueBasedOnTheLogCountAttribute.tt">LoggingDataCodeGeneratorForReturnValueBasedOnTheLogCountAttribute</a>, <a href="https://github.com/ymassad/AOPViaT4Examples/blob/master/T4Aspects/MethodDescriptionLoggingDataCodeGenerator.tt">MethodDescriptionLoggingDataCodeGenerator</a>.</p> <p>The structure of the rest of the code in LoggingAspect.tt is somewhat similar to that of RetryAspect.tt. We basically loop through each of the methods and we have a mix of static code and control blocks to generate a try/catch block that invokes the decorated method. If there is an error, we invoke the LogError method on the <a href="https://github.com/ymassad/AOPViaT4Examples/blob/master/Logging/ILogger.cs">ILogger</a> dependency (in the decorator class). If on the other hand, the call is successful, we invoke LogSuccess.</p> <p>Notice how we use the LoggingDataCode generator dependencies to extract the preInvocationLoggingDataCodeList list that contains LoggingDataCode items for pre-invocation data like arguments, and the postInvocationLoggingDataCodeList list that contains LoggingDataCode items for the return value.</p> <p>As we did with the retry aspect, feel free to:</p> <p>&middot; create new interfaces and classes in the Library project</p> <p>&middot; decorate the class methods with <a href="https://github.com/ymassad/AOPViaT4Examples/blob/master/LoggingAOP/LogAttribute.cs">Log</a>, <a href="https://github.com/ymassad/AOPViaT4Examples/blob/master/LoggingAOP/LogCountAttribute.cs">LogCount</a>, and <a href="https://github.com/ymassad/AOPViaT4Examples/blob/master/LoggingAOP/MethodDescriptionAttribute.cs">MethodDescription</a> attributes</p> <p>&middot; inform our aspect system about them inside LoggingAspectLocal.tt</p> <p>&middot; see the logging decorator generated for them in LoggingAspectLocal.cs</p> <p>&middot; then use the decorators inside the Composition Root to see the logged messages printed to the console.</p> <h3><strong>T4 versus Reflection</strong></h3> <p>In the previous article, we used Reflection at runtime to extract logging data. I can see two advantages to the T4 approach in this article:</p> <p><strong>Performance</strong></p> <p>Consider the logging aspect for example. T4 will generate code at design time that accesses the source of the logging data (e.g. method parameters) directly. Therefore, performance is enhanced.</p> <p>In the <a href="https://www.dotnetcurry.com/patterns-practices/1305/aspect-oriented-programming-aop-csharp-using-solid">previous article&rsquo;s example</a>, LoggingData extractors were executed every time a method is invoked to extract logging data via Reflection. With T4, most of this complexity is moved to design time, and therefore no extractors/generators need to execute at runtime.</p> <p><strong>Debugging Experience</strong></p> <p>With T4, we generate decorators at design time. So at runtime, when we debug, we will debug code that is specific to a certain interface/class, and therefore is simpler. This is true because generic code is always more complex than concrete/specific code.</p> <p>Consider for example the logic that extracts logging data in the previous article&rsquo;s example. Such code does not exist in the T4 generated decorators so we don&rsquo;t have to debug it at runtime.</p> <p>Please note however, that similar code (e.g. the LoggingDataCode generators) can be debugged at <b>design time</b>. For example, you can right-click on the RetryAspectLocal.tt file in the Solution Explorer window and click Debug T4 Template. This allows us to debug the T4 template itself.</p> <h3><strong>Conclusion:</strong></h3> <p>T4 allows us to create templates that contain static code and code blocks that can generate code dynamically. This article provided examples on how to use T4 to create aspects: a retry aspect and a logging aspect. Using T4 to generate decorators at design time has some advantages over using Reflection at runtime. These advantages are better performance, and better runtime debugging experience.</p> <p><em>This article has been editorially reviewed by </em><a href="https://www.dotnetcurry.com/author/suprotim-agarwal"><em>Suprotim Agarwal.</em></a></p> <div id="csharpbook" class="col-xs-12 col-sm-12 col-md-12 col-lg-12 articletext"> <!--<a href="http://www.dotnetcurry.org/r/dnc-csharpbk-web-imgbtm" target="_blank" style="color:#cd282f"><img alt="Absolutely Awesome Book on C# and .NET" src="/csharpbook/images/csharpbook-hori.jpg" /></a>--> <div class="col-md-3 col-sm-3 col-xs-12"> <a href="https://www.dotnetcurry.com/csharpbook" target="_blank"><img alt="Absolutely Awesome Book on C# and .NET" src="/csharpbook/images/csharp-book-ad.jpg" /></a> </div> <div class="col-md-9 col-sm-9 col-xs-12"> <p>C# and .NET have been around for a very long time, but their constant growth means there’s always more to learn.</p> <p>We at DotNetCurry are very excited to announce <strong><a href="https://www.dotnetcurry.com/csharpbook" target="_blank">The Absolutely Awesome Book on C# and .NET</a></strong>. This is a 500 pages concise technical eBook available in PDF, ePub (iPad), and Mobi (Kindle). </p><p>Organized around concepts, this Book aims to provide a concise, yet solid foundation in C# and .NET, covering <b>C# 6.0, C# 7.0 and .NET Core, with chapters on the latest .NET Core 3.0, .NET Standard and C# 8.0 (final release) too</b>. Use these concepts to deepen your existing knowledge of C# and .NET, to have a solid grasp of the latest in C# and .NET OR to crack your next .NET Interview.</p> <p><strong><a href="https://www.dotnetcurry.com/csharpbook" target="_blank" style="color:#cd282f">Click here to Explore the Table of Contents or Download Sample Chapters!<a /></strong></p> </div> </div> <div class="footerheading margin-top-20">What Others Are Reading!</div> <div class="col-xs-12 col-sm-12 col-md-12 similarauthor"> <div class="row"> <div class="col-md-12 similararticles"> <a href="http://www.dotnetcurry.com/ShowArticle.aspx?ID=1575" style="color:#4A75AD;font-weight:normal;">Tackling Legacy Code</a> <br /><br /> <a href="http://www.dotnetcurry.com/ShowArticle.aspx?ID=1557" style="color:#4A75AD;font-weight:normal;">Coding Practices: The most important ones – Part 3</a> <br /><br /> <a href="http://www.dotnetcurry.com/ShowArticle.aspx?ID=1555" style="color:#4A75AD;font-weight:normal;">Architecting .NET Desktop and Mobile applications</a> <br /><br /> <a href="http://www.dotnetcurry.com/ShowArticle.aspx?ID=1553" style="color:#4A75AD;font-weight:normal;">Application Architecture – Getting started</a> <br /><br /> <a href="http://www.dotnetcurry.com/ShowArticle.aspx?ID=1543" style="color:#4A75AD;font-weight:normal;">Coding practices: The most important ones – Part 2</a> <br /><br /> <a href="http://www.dotnetcurry.com/ShowArticle.aspx?ID=1534" style="color:#4A75AD;font-weight:normal;">Coding Practices: The most important ones – Part 1</a> <br /><br /> <span id="ctl00_MainContent_SimilarPosts1_lblCategoNam"></span> </div> </div> </div> <!-- DNC_BottomText --> <div class="visible-xs visible-sm visible-md visible-lg"> <div id='dnc-bot-txtad' class="articletext"> </div> </div> <div class="socialbox"> <b>Was this article worth reading? Share it with fellow developers too. Thanks!</b> <br/> <!-- Sharingbutton Facebook --> <a class="resp-sharing-button__link" id="fbsharelink" target="_blank" aria-label="Share on Facebook"> <div class="resp-sharing-button resp-sharing-button--facebook resp-sharing-button--large"><div aria-hidden="true" class="resp-sharing-button__icon resp-sharing-button__icon--solid"> <svg version="1.1" x="0px" y="0px" width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve"> <g> <path d="M18.768,7.465H14.5V5.56c0-0.896,0.594-1.105,1.012-1.105s2.988,0,2.988,0V0.513L14.171,0.5C10.244,0.5,9.5,3.438,9.5,5.32 v2.145h-3v4h3c0,5.212,0,12,0,12h5c0,0,0-6.85,0-12h3.851L18.768,7.465z"/> </g> </svg> </div>Share on Facebook</div> </a> <!-- Sharingbutton Twitter --> <a class="resp-sharing-button__link" id="twtsharelink" target="_blank" aria-label="Share on Twitter"> <div class="resp-sharing-button resp-sharing-button--twitter resp-sharing-button--large"><div aria-hidden="true" class="resp-sharing-button__icon resp-sharing-button__icon--solid"> <svg version="1.1" x="0px" y="0px" width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve"> <g> <path d="M23.444,4.834c-0.814,0.363-1.5,0.375-2.228,0.016c0.938-0.562,0.981-0.957,1.32-2.019c-0.878,0.521-1.851,0.9-2.886,1.104 C18.823,3.053,17.642,2.5,16.335,2.5c-2.51,0-4.544,2.036-4.544,4.544c0,0.356,0.04,0.703,0.117,1.036 C8.132,7.891,4.783,6.082,2.542,3.332C2.151,4.003,1.927,4.784,1.927,5.617c0,1.577,0.803,2.967,2.021,3.782 C3.203,9.375,2.503,9.171,1.891,8.831C1.89,8.85,1.89,8.868,1.89,8.888c0,2.202,1.566,4.038,3.646,4.456 c-0.666,0.181-1.368,0.209-2.053,0.079c0.579,1.804,2.257,3.118,4.245,3.155C5.783,18.102,3.372,18.737,1,18.459 C3.012,19.748,5.399,20.5,7.966,20.5c8.358,0,12.928-6.924,12.928-12.929c0-0.198-0.003-0.393-0.012-0.588 C21.769,6.343,22.835,5.746,23.444,4.834z"/> </g> </svg> </div>Share on Twitter</div> </a> <!-- Sharingbutton LinkedIn --> <a class="resp-sharing-button__link" id="linkdinsharelink" target="_blank" aria-label="Share on LinkedIn"> <div class="resp-sharing-button resp-sharing-button--linkedin resp-sharing-button--large"><div aria-hidden="true" class="resp-sharing-button__icon resp-sharing-button__icon--solid"> <svg xmlns="https://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M6.5 21.5h-5v-13h5v13zM4 6.5C2.5 6.5 1.5 5.3 1.5 4s1-2.4 2.5-2.4c1.6 0 2.5 1 2.6 2.5 0 1.4-1 2.5-2.6 2.5zm11.5 6c-1 0-2 1-2 2v7h-5v-13h5V10s1.6-1.5 4-1.5c3 0 5 2.2 5 6.3v6.7h-5v-7c0-1-1-2-2-2z"/></svg> </div>Share on LinkedIn</div> </a> <!-- Sharingbutton Google+ --> <a class="resp-sharing-button__link" id="googsharelink" target="_blank" aria-label="Share on Google+"> <div class="resp-sharing-button resp-sharing-button--google resp-sharing-button--large"><div aria-hidden="true" class="resp-sharing-button__icon resp-sharing-button__icon--solid"> <svg version="1.1" x="0px" y="0px" width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve"> <g> <path d="M11.366,12.928c-0.729-0.516-1.393-1.273-1.404-1.505c0-0.425,0.038-0.627,0.988-1.368 c1.229-0.962,1.906-2.228,1.906-3.564c0-1.212-0.37-2.289-1.001-3.044h0.488c0.102,0,0.2-0.033,0.282-0.091l1.364-0.989 c0.169-0.121,0.24-0.338,0.176-0.536C14.102,1.635,13.918,1.5,13.709,1.5H7.608c-0.667,0-1.345,0.118-2.011,0.347 c-2.225,0.766-3.778,2.66-3.778,4.605c0,2.755,2.134,4.845,4.987,4.91c-0.056,0.22-0.084,0.434-0.084,0.645 c0,0.425,0.108,0.827,0.33,1.216c-0.026,0-0.051,0-0.079,0c-2.72,0-5.175,1.334-6.107,3.32C0.623,17.06,0.5,17.582,0.5,18.098 c0,0.501,0.129,0.984,0.382,1.438c0.585,1.046,1.843,1.861,3.544,2.289c0.877,0.223,1.82,0.335,2.8,0.335 c0.88,0,1.718-0.114,2.494-0.338c2.419-0.702,3.981-2.482,3.981-4.538C13.701,15.312,13.068,14.132,11.366,12.928z M3.66,17.443 c0-1.435,1.823-2.693,3.899-2.693h0.057c0.451,0.005,0.892,0.072,1.309,0.2c0.142,0.098,0.28,0.192,0.412,0.282 c0.962,0.656,1.597,1.088,1.774,1.783c0.041,0.175,0.063,0.35,0.063,0.519c0,1.787-1.333,2.693-3.961,2.693 C5.221,20.225,3.66,19.002,3.66,17.443z M5.551,3.89c0.324-0.371,0.75-0.566,1.227-0.566l0.055,0 c1.349,0.041,2.639,1.543,2.876,3.349c0.133,1.013-0.092,1.964-0.601,2.544C8.782,9.589,8.363,9.783,7.866,9.783H7.865H7.844 c-1.321-0.04-2.639-1.6-2.875-3.405C4.836,5.37,5.049,4.462,5.551,3.89z"/> <polygon points="23.5,9.5 20.5,9.5 20.5,6.5 18.5,6.5 18.5,9.5 15.5,9.5 15.5,11.5 18.5,11.5 18.5,14.5 20.5,14.5 20.5,11.5 23.5,11.5 "/> </g> </svg> </div>Share on Google+</div> </a> <!--<ul class="socialshare"> <li><div class="gplusarticle"><b>Please Share this article if you think it was worth reading. Thanks!</b></div></li> <li><div class="fb-share-button" data-layout="button"> </div> </li> <li><script type="IN/Share"></script></li> </ul>--> </div> <br /> <div class="col-xs-12 col-sm-12 col-md-12 similarauthor"> <div class="row footerheading">Author</div> <div class="row"> <div class="col-md-12"> <div class="col-md-12 author-wrap img-rounded"> <div class="author-image thumbnail"> <img id="ctl00_MainContent_authorInfo_imgAuthor" onerror="this.onload = null; this.src=&#39;/images/authors/NoImage.jpg&#39;;" src="../../images/authors/Yacoub%20Massad.jpg" style="border-width:0px;" /> </div> <div class="author-desc caption"> Yacoub Massad is a software architect and works mainly on Microsoft technologies. Currently, he works at NextgenID where he uses C#, .NET, and other technologies to create identity solutions. He is interested in learning and writing about software design principles that aim at creating maintainable software. You can view his blog posts at <a href='http://criticalsoftwareblog.com/'>criticalsoftwareblog.com</a>. He is also the creator of DIVEX (<a href='https://divex.dev/'>https://divex.dev</a>), a dependency injection tool that allows you to compose objects and functions in C# in a way that makes your code more maintainable. Recently he started a <a href='https://www.youtube.com/channel/UCKUnsjTO9KRlNzJoLv3KmDQ/'>YouTube channel</a> about Roslyn, the .NET compiler. You can follow him on twitter @<a href='https://twitter.com/yacoubmassad'>yacoubmassad</a>. </div> </div> </div> </div> </div> </div> <!--<div class="footerheading">Further Reading - Articles You May Like!</div>--> <div class="marginspace"> <div id='ban-pos-9-90'></div> <div id='ban-pos-10-90'></div> <div id='ban-pos-11-90'></div> </div> <br /><br /> <div class="col-xs-12 col-sm-12 col-md-12"> <!--<a href="http://www.copyscape.com/" target="blank" rel="nofollow">--><img src="/images/copyscape.gif" alt="Page copy protected against web site content infringement by Copyscape" title="Do not copy content from the page. Plagiarism will be detected by Copyscape." height="16" border="0"/><!--</a>--> </div> <br /><br /> <br /> <hr /> <div id="ctl00_MainContent_panComments" class="col-md-12 spacer"> <div class="footerheading">Feedback - Leave us some adulation, criticism and everything in between!</div> <ul class="tabHeader"> <li><a class="show-comments" href="#t1">Click here to post your Comments</a> </li> </ul> <div class="tabContent"> <div id="t1" class="col-md-12 column"> <div id="disqus_thread"></div> <!--<script type="text/javascript"> var disqus_shortname = 'dotnetcurry'; /* * * DON'T EDIT BELOW THIS LINE * * */ (function () { var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; dsq.src = 'http://' + disqus_shortname + '.disqus.com/embed.js'; (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); })(); </script> <noscript>Please enable JavaScript to view the <a href="http://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript> <a href="http://disqus.com" class="dsq-brlink">comments powered by <span class="logo-disqus">Disqus</span></a>--> </div> <div id="t2"> </div> <!-- t2 --> </div> <!-- tabcontent --> </div> <!-- Place this render call where appropriate <script type="text/javascript"> (function () { var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true; po.src = 'https://apis.google.com/js/plusone.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s); })(); </script> <script src="//platform.linkedin.com/in.js" type="text/javascript"> lang: en_US</script> <script> !function (d, s, id) { var js, fjs = d.getElementsByTagName(s)[0]; if (!d.getElementById(id)) { js = d.createElement(s); js.id = id; js.src = "//platform.twitter.com/widgets.js"; fjs.parentNode.insertBefore(js, fjs); } } (document, "script", "twitter-wjs");</script>--> <!-- Finally, to actually run the highlighter, you need to include this JS on your page --> </div> <div id="rightcol" class="col-md-4"> <!-- <div class="row"> <div class="spacerforsearch"> <div class="input-group"> <input name="ctl00$searchbox" id="ctl00_searchbox" type="text" class="form-control" placeholder="Search DotNetCurry.com for..." onFocus="this.className = &#39;form-control&#39;" style="border-color:#94C7EF;border-width:1px;border-style:solid;width:100%" /> <span class="input-group-btn"> <input type="submit" name="ctl00$SearchButton" value="Go!" id="ctl00_SearchButton" class="btn btn-default" type="button" /> </span> </div> </div> </div> --> <div class="margin-vertical-20" /> <div class="side-widget"> <div class="featured-tools"> <span>Featured Tools</span> <div id="creatives"> <div id='ban-pos-1' style='display: none'> </div> <div id='ban-pos-2'> <div class="padding-vertical-10"></div> </div> <div id='ban-pos-3'> </div> <div class="padding-vertical-10"></div> <div id='ban-pos-4-250'> </div> <div id='ban-pos-5-250'> </div> <div id='ban-pos-6-250'> </div> </div> </div> </div> <div class="sec-title"> <h4>Categories</h4> </div> <div class="side-widget"> <ul id="toggle-view"> <li> <h3>.NET Web</h3> <span class="fa fa-angle-down"></span> <div class="toggle-panel"> <div> <a href="https://www.dotnetcurry.com/tutorials/aspnet">ASP.NET</a> <a href="https://www.dotnetcurry.com/tutorials/aspnet-mvc">ASP.NET MVC</a> </div> <div> <a href="https://www.dotnetcurry.com/tutorials/aspnet-core">ASP.NET Core</a> <a href="https://www.dotnetcurry.com/tutorials/aspnet-ajax">ASP.NET AJAX</a> </div> <div> <a href="https://www.dotnetcurry.com/tutorials/sharepoint">SharePoint</a> <a href="https://www.dotnetcurry.com/tutorials/wcf">WCF</a> </div> </div> </li> <li> <h3>.NET Framework, Visual Studio and C#</h3> <span class="fa fa-angle-down"></span> <div class="toggle-panel"> <div> <a href="https://www.dotnetcurry.com/tutorials/csharp">C#</a> <a href="https://www.dotnetcurry.com/tutorials/linq">LINQ</a> </div> <div> <a href="https://www.dotnetcurry.com/tutorials/visualstudio">Visual Studio</a> <a href="https://www.dotnetcurry.com/tutorials/vsts-tfs">VSTS & TFS</a> </div> <div> <a href="https://www.dotnetcurry.com/tutorials/entityframework">Entity Framework</a> <a href="https://www.dotnetcurry.com/tutorials/dotnetframework">.NET Framework</a> </div> <div> <a href="https://www.dotnetcurry.com/tutorials/dotnet-standard-core">.NET Standard & .NET Core</a> </div> </div> </li> <li> <h3>Patterns & Practices</h3> <span class="fa fa-angle-down"></span> <div class="toggle-panel"> <div> <a href="https://www.dotnetcurry.com/tutorials/patterns-practices">Design Patterns</a> <a href="https://www.dotnetcurry.com/tutorials/software-gardening">Software Gardening</a> </div> </div> </li> <li> <h3>Cloud and Mobile</h3> <span class="fa fa-angle-down"></span> <div class="toggle-panel"> <div> <a href="https://www.dotnetcurry.com/tutorials/windows-azure">Microsoft Azure</a> <a href="https://www.dotnetcurry.com/tutorials/xamarin">Xamarin</a> </div> <div> <a href="https://www.dotnetcurry.com/tutorials/powershell">Powershell</a> <a href="https://www.dotnetcurry.com/tutorials/machine-learning-ai">Machine Learning & AI</a> </div> <div> <a href="https://www.dotnetcurry.com/tutorials/windows-store">UWP & Windows Store</a> <a href="https://www.dotnetcurry.com/tutorials/windowsphone">Windows Phone</a> </div> </div> </li> <li> <h3>JavaScript</h3> <span class="fa fa-angle-down"></span> <div class="toggle-panel"> <div> <a href="https://www.dotnetcurry.com/tutorials/typescript">TypeScript</a> <a href="https://www.dotnetcurry.com/tutorials/angularjs">Angular</a> </div> <div> <a href="https://www.dotnetcurry.com/tutorials/jquery-aspnet">jQuery</a> <a href="https://www.dotnetcurry.com/tutorials/nodejs">Node.js</a> </div> <div> <a href="https://www.dotnetcurry.com/tutorials/reactjs">React.js</a> <a href="https://www.dotnetcurry.com/tutorials/backbonejs">Backbone.js</a> </div> <div> <a href="https://www.dotnetcurry.com/tutorials/html5-javascript">HTML5 & JavaScript</a> <a href="https://www.dotnetcurry.com/tutorials/bootstrap-css">Bootstrap & CSS</a> </div> </div> </li> <li> <h3>.NET Desktop</h3> <span class="fa fa-angle-down"></span> <div class="toggle-panel"> <div> <a href="https://www.dotnetcurry.com/tutorials/wpf">WPF</a> <a href="https://www.dotnetcurry.com/tutorials/winforms">WinForms</a> </div> </div> </li> <li> <h3>Interview Questions & Product Reviews</h3> <span class="fa fa-angle-down"></span> <div class="toggle-panel"> <div> <a href="https://www.dotnetcurry.com/tutorials/dotnetinterview">.NET Interview Q&A</a> <a href="https://www.dotnetcurry.com/tutorials/product-articles-review">Product Reviews</a> </div> <div> <a href="https://www.dotnetcurry.com/tutorials/general-programming-topics">General Topics</a> </div> </div> </li> </ul> </div> <div class="sec-title"> <h3><b>JOIN OUR COMMUNITY</b></h3> </div> <div class="side-widget"> <div class="side-social"> <a href="https://www.facebook.com/dotnetcurry"><i class="fa fa-facebook"></i> 50K+ <span>fans</span></a> <a href="https://www.twitter.com/dotnetcurry"><i class="fa fa-twitter"></i> 8K+ <span>followers</span></a> <a href="https://www.dotnetcurry.com/magazine/"><i class="fa fa-envelope"></i> 128K+ <span>subscribers</span></a> </div> </div> <div class="sec-title"> <h3><b>POPULAR ARTICLES</b></h3> </div> <div id="latestart" class="side-widget"> <div> <br /> <br /> <br /> </div> </div> <div id='ban-pos-7-600'> </div> <div id='ban-pos-8-600'> </div> <div class="sec-title-plain"> <h4>Tags</h4> </div> <div class="side-widget"> <div class="tags"> <a href="https://www.dotnetcurry.com/tutorials/aspnet-mvc">ASP.NET MVC</a> <a href="https://www.dotnetcurry.com/tutorials/aspnet-core">ASP.NET Core</a> <a href="https://www.dotnetcurry.com/tutorials/aspnet">ASP.NET</a> <a href="https://www.dotnetcurry.com/tutorials/sharepoint">SharePoint</a> <a href="https://www.dotnetcurry.com/tutorials/patterns-practices">Design Patterns</a> <a href="https://www.dotnetcurry.com/tutorials/csharp">C#</a> <a href="https://www.dotnetcurry.com/tutorials/linq">LINQ</a> <a href="https://www.dotnetcurry.com/tutorials/wpf">WPF</a> <a href="https://www.dotnetcurry.com/tutorials/wcf">WCF</a> <a href="https://www.dotnetcurry.com/tutorials/visualstudio">Visual Studio</a> <a href="https://www.dotnetcurry.com/tutorials/vsts-tfs">VSTS & TFS</a> <a href="https://www.dotnetcurry.com/tutorials/windows-azure">Azure</a> <a href="https://www.dotnetcurry.com/tutorials/entityframework">Entity Framework</a> <a href="https://www.dotnetcurry.com/tutorials/angularjs">Angular.js</a> <a href="https://www.dotnetcurry.com/tutorials/reactjs">React.js</a> <a href="https://www.dotnetcurry.com/tutorials/jquery-aspnet">jQuery</a> <a href="https://www.dotnetcurry.com/tutorials/html5-javascript">JavaScript</a> <a href="https://www.dotnetcurry.com/tutorials/html5-javascript">HTML5</a> <a href="https://www.dotnetcurry.com/tutorials/dotnet-standard-core">.NET Core</a> <a href="https://www.dotnetcurry.com/tutorials/dotnetframework">.NET Framework</a> </div> </div> <div class="sec-title"> <h3><b>JQUERY COOKBOOK</b></h3> </div> <div class="side-widget"> <a href="http://www.jquerycookbook.com"><img src="https://www.dotnetcurry.com/images/books/300x300-jqckbk.png" alt="jQuery CookBook" /></a> </div> </div> </div> </div> <!-- container --> </div> <!-- footer --> <footer class="margin-top-30"> <div class="container"> <div class="footer-head"> <div class="row center-content"> <div class="col-md-2 col-sm-3"> <a href="https://www.dotnetcurry.com"> <img data-src="/img/dnc-logo-tee-trans.png" class="img-responsive lazyload" alt="" /> </a> </div> <div class="col-md-6 col-sm-4"> <p></p> </div> <!--<div class="col-md-4 col-sm-5"> <form class="footer-search"> <input type="search" placeholder="Search"> <button type="submit"><i class="fa fa-search"></i></button> </form> </div>--> </div> </div> <div class="footer-content"> <div class="row"> <div class="col-sm-2"> <h5 class="text-white">Server-Side</h5> <ul class="footer-links"> <li><a href="https://www.dotnetcurry.com/tutorials/aspnet">ASP.NET</a></li> <li><a href="https://www.dotnetcurry.com/tutorials/aspnet-core">ASP.NET Core</a></li> <li><a href="https://www.dotnetcurry.com/tutorials/aspnet-mvc">ASP.NET MVC</a></li> <li><a href="https://www.dotnetcurry.com/tutorials/wcf">WCF</a></li> <li><a href="https://www.dotnetcurry.com/tutorials/sharepoint">SharePoint</a></li> </ul> </div> <div class="col-sm-2"> <h5 class="text-white">Client-side</h5> <ul class="footer-links"> <li><a href="https://www.dotnetcurry.com/tutorials/angularjs">Angular.js</a></li> <li><a href="https://www.dotnetcurry.com/tutorials/reactjs">React.js</a></li> <li><a href="https://www.dotnetcurry.com/tutorials/jquery-aspnet">jQuery</a></li> <li><a href="https://www.dotnetcurry.com/tutorials/backbonejs">Backbone.js</a></li> <li><a href="https://www.dotnetcurry.com/tutorials/html5-javascript">HTML5</a></li> <li><a href="https://www.dotnetcurry.com/tutorials/bootstrap-css">CSS</a></li> </ul> </div> <div class="col-sm-2"> <h5 class="text-white">.NET</h5> <ul class="footer-links"> <li><a href="https://www.dotnetcurry.com/tutorials/csharp">C#</a></li> <li><a href="https://www.dotnetcurry.com/tutorials/visualstudio">Visual Studio</a></li> <li><a href="https://www.dotnetcurry.com/tutorials/vsts-tfs">VSTS & TFS</a></li> <li><a href="https://www.dotnetcurry.com/tutorials/linq">LINQ</a></li> <li><a href="https://www.dotnetcurry.com/tutorials/entityframework">Entity Framework</a></li> <li><a href="https://www.dotnetcurry.com/tutorials/dotnetframework">.NET Framework</a></li> <li><a href="https://www.dotnetcurry.com/tutorials/dotnet-standard-core">.NET Standard & .NET Core</a></li> <li><a href="https://www.dotnetcurry.com/tutorials/wpf">WPF</a></li> <li><a href="https://www.dotnetcurry.com/tutorials/winforms">WinForms</a></li> </ul> </div> <div class="col-sm-2"> <h5 class="text-white">Cloud and Mobile</h5> <ul class="footer-links"> <li><a href="https://www.dotnetcurry.com/tutorials/windows-azure">Microsoft Azure</a></li> <li><a href="https://www.dotnetcurry.com/tutorials/devops">DevOps</a></li> <li><a href="https://www.dotnetcurry.com/tutorials/xamarin">Xamarin</a></li> <li><a href="https://www.dotnetcurry.com/tutorials/powershell">Powershell</a></li> <li><a href="https://www.dotnetcurry.com/tutorials/machine-learning-ai">Machine Learning & AI</a></li> <li><a href="https://www.dotnetcurry.com/tutorials/windows-store">UWP & Windows Store</a></li> <li><a href="https://www.dotnetcurry.com/tutorials/windowsphone">Windows Phone</a></li> </ul> </div> <div class="col-sm-2"> <h5 class="text-white">Skill Up</h5> <ul class="footer-links"> <li><a href="https://www.dotnetcurry.com/tutorials/patterns-practices">Design Patterns</a></li> <li><a href="https://www.dotnetcurry.com/tutorials/software-gardening">Software Gardening</a></li> <li><a href="https://www.dotnetcurry.com/tutorials/dotnetinterview">.NET Interview Q&A</a></li> <li><a href="https://www.dotnetcurry.com/magazine/" class="last">Magazines</a></li> <li><a href="http://www.jquerycookbook.com/">Books</a></li> <li><a href="https://www.dotnetcurry.com/tutorials/product-articles-review">Product Reviews</a></li> </ul> </div> <div class="col-sm-2"> <h5 class="text-white">Follow Us</h5> <ul class="footer-social"> <li><a href="https://www.facebook.com/dotnetcurry">Facebook</a></li> <li><a href="https://www.twitter.com/dotnetcurry">Twitter</a></li> <li><a href="https://github.com/dotnetcurry">Github</a></li> </ul> </div> </div> </div> <div class="footer-bottom"> <div class="row"> <div class="col-sm-6"> <p>&copy; 2007-2023 DotNetCurry.com (A subsidiary of A2Z Knowledge Visuals Pvt. Ltd). All rights reserved.</p> </div> <div class="col-sm-6 text-right"> <ul class="list-inline"> <li><a href="https://www.dotnetcurry.com/Contact.aspx">Contact Us</a></li> <li><a href="https://www.dotnetcurry.com/WriteForUs.aspx">Write For Us</a></li> <li><a href="https://www.dotnetcurry.com/PrivacyPolicy.aspx">Privacy</a></li> <li><a href="https://www.dotnetcurry.com/terms-conditions">Terms</a></li> </ul> </div> </div> </div> </div> </footer> <!-- footer --> </form> <script src="//ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <script src="//js.maxmind.com/js/apis/geoip2/v2.1/geoip2.js"></script> <script src="/bundles/scriptsmin?v=ztXW8lGCJchjv7iQbaTOZjTpSGfOrLg1lKllrWansXI1"></script> <script src="/bundles/framework?v=yOkBizHgoWwNt4OFd4FauUxu9fucEJ_a4EJrci8uIGA1"></script> <script type="text/javascript"> SyntaxHighlighter.defaults['auto-links'] = false; SyntaxHighlighter.defaults['gutter'] = false; SyntaxHighlighter.defaults['toolbar'] = false; SyntaxHighlighter.all() </script> <script> $(function () { $('.tabHeader').children().first().addClass('current'); $('.tabContent').children().first().addClass('current'); $('.tabHeader li').click(function (e) { e.preventDefault(); $(this).siblings('.current').removeClass('current'); $(this).addClass('current'); $('.tabContent').children('.current').removeClass('current'); $('.tabContent').children().eq($(this).index()).addClass('current'); }); }); </script> <script src="https://ajax.googleapis.com/ajax/libs/webfont/1.5.18/webfont.js"></script> <script> WebFont.load({ google: { families: ['Hind Vadodara:300,400,500,600,700'] } }); </script> </body> </html>

Pages: 1 2 3 4 5 6 7 8 9 10