AutoActions for Skinny controllers–small customizations

Logging Roslyn Code Generator:

For each program that you develop , it is important ( if not vital ) to see logging. In the Roslyn  Analyzer / Code Generators, the diagnostics are logged with DiagnosticDescriptor and Diagnostic – think that you should see in the output console when compiling and this will become clear.

More, the enum DiagnosticSeverity Enum (Microsoft.CodeAnalysis) | Microsoft Docs  can be seen as default for Warn and Info – but not for Info. Info can be seen only with

dotnet build -v diag

The code that I used is:

static Diagnostic DoDiagnostic(DiagnosticSeverity ds,string message)
         {
             //info  could be seen only with
             // dotnet build -v diag
             var dd = new DiagnosticDescriptor(“SkinnyControllersGenerator”, $”StartExecution”, $”{message}”, “SkinnyControllers”, ds, true);
             var d = Diagnostic.Create(dd, Location.Create(“skinnycontrollers.cs”, new TextSpan(1, 2), new LinePositionSpan()));
             return d;
         }

And I called like this

string name = $”{ThisAssembly.Project.AssemblyName} {ThisAssembly.Info.Version}”;
context.ReportDiagnostic(DoDiagnostic(DiagnosticSeverity.Info,name));

( Yes , I used a Roslyn Code Generator – GitHub – kzu/ThisAssembly: Exposes project and assembly level information as constants in the ThisAssembly class using source generators powered by Roslyn. – inside another code generator – and no reference used on deploy – how cool is that ? )

Also, I use this to display some warnings when something is wrong , but I do not generate code. For example:

var ms = m as IMethodSymbol;
if (ms is null)
{
     context.ReportDiagnostic(DoDiagnostic(DiagnosticSeverity.Warning, $”{m.Name} is not a IMethodSymbol”));
     continue;

}

Intellisense for Code Generators:

This is something easy. First, I did not have intellisense. After reading , I discovered that I should put above the class declaration :

[GeneratedCode(“”{ThisAssembly.Info.Product}””, “”{ThisAssembly.Info.Version}””)]
[CompilerGenerated]

( Yes, again code generator  – thanks,  KZU ! – to the rescue).

In this manner , if something wrong, I can see the version of the SkinnyControllersGenerator right away .

AutoActions for Skinny controllers–deploying at NuGet

First time I was thinking that is enough to do

dotnet pack

to make a Nuget package to deploy on NUGET. It was not. I realized that the generator was not starting at all! And it was not in the project Dependencies=>Analyzers .

Time to read more from https://github.com/dotnet/roslyn/blob/master/docs/features/source-generators.cookbook.md , I realized that I was wrong – for analyzers /source generators we need more. So I put

<!– Package the generator in the analyzer directory of the nuget package –>
<None Include=”$(OutputPath)\$(AssemblyName).dll” Pack=”true” PackagePath=”analyzers/dotnet/cs” Visible=”false” />

to include in the nuget package at analyzers.

After this, the next error was

CSC : warning CS8032: An instance of analyzer SkinnyControllersGenerator.AutoActionsGenerator cannot be created from C:\Users\Surface1\.nuget\packages\skinnycontrollersgenerator\2020.11.24.2135\analyzers\dotnet\cs\SkinnyControllersGenerator.dll : Exception has been thrown by the target of an invocation..

Most probably, this comes from the fact that the SkinnyControllersGenerator has a dependency from another Dll/Nuget, SkinnyControllersCommon . Time to read more from https://github.com/dotnet/roslyn/blob/master/docs/features/source-generators.cookbook.md . I figured that the example with Newtonsoft.JSON is to complicated for me, so I took the decision to just include the file AutoActionsAttribute as link.

And now it works! – see https://www.nuget.org/packages/SkinnyControllersGenerator/

AutoActions for Skinny controllers–first implementation

Now with the implementation.

First, I find all the fields declarations that had  the  Autonotify 

if (syntaxNode is FieldDeclarationSyntax fieldDeclarationSyntax
                         && fieldDeclarationSyntax.AttributeLists.Count > 0)
             {
                 foreach(var al in fieldDeclarationSyntax.AttributeLists)
                 {
                     var att = al.Attributes;
                     foreach(var at in att)
                     {
                         var x = at.Name as IdentifierNameSyntax;
                         if(autoActions.Contains(x.Identifier.Text))
                         {
                             CandidateFields.Add(fieldDeclarationSyntax);
                             return;
                         }
                     }
                 }
                
             }

Second , I must find all the methods ( without the constructor) for generating data:

if (!(context.SyntaxReceiver is SyntaxReceiverFields receiver))
                 return;
             var compilation = context.Compilation;
             var fieldSymbols = new List<IFieldSymbol>();
             foreach (var field in receiver.CandidateFields)
             {
                 SemanticModel model = compilation.GetSemanticModel(field.SyntaxTree);
                 foreach (var variable in field.Declaration.Variables)
                 {
                     var fieldSymbol = model.GetDeclaredSymbol(variable) as IFieldSymbol;
                     var attr = fieldSymbol.GetAttributes();
                     if (attr.Any(ad => ad.AttributeClass.Name == autoActions))
                     {
                         fieldSymbols.Add(fieldSymbol);
                     }
                 }

                foreach (var group in fieldSymbols.GroupBy(f => f.ContainingType))
                 {
                     string classSource = ProcessClass(group.Key, group.ToArray(),   context);
                     if (string.IsNullOrWhiteSpace(classSource))
                         continue;

                    context.AddSource($”{group.Key.Name}_autogenerate.cs”, SourceText.From(classSource, Encoding.UTF8));
                 }
             }

You can find the source code at https://github.com/ignatandrei/AOP_With_Roslyn/releases/tag/2020.11.23 .

AutoActions for Skinny controllers–idea

How you generate actions in controllers ? Usually, you create a Business Logic class and then you expose via a controller in a WebAPI . And, if you are very careful, then you use the Skinny controllers concept  ( read more about it at 3 ways to keep your asp.net mvc controllers thin (jonhilton.net) ).

So this implies usually repeating code in order to call the functions / methods from the business logic class.  I can give an example:

public class RepositoryWF
     {
         private static readonly string[] Summaries = new[]
{
             “Freezing”, “Bracing”, “Chilly”, “Cool”, “Mild”, “Warm”, “Balmy”, “Hot”, “Sweltering”, “Scorching”
         };
         public WeatherForecast[] DataToDo(int i)
         {
             var rng = new Random();
             return Enumerable.Range(1, i).Select(index => new WeatherForecast
             {
                 Date = DateTime.Now.AddDays(index),
                 TemperatureC = rng.Next(-20, 55),
                 Summary = Summaries[rng.Next(Summaries.Length)]
             })
             .ToArray();

        }
     // more methods / actions

and the controller

[ApiController]
     [Route(“[controller]/[action]”)]
     public partial class WeatherForecastController : ControllerBase
     {

        private readonly ILogger<WeatherForecastController> _logger;

       
         private readonly RepositoryWF repository;
        
         public WeatherForecastController(ILogger<WeatherForecastController> logger, RepositoryWF repository)
         {
             _logger = logger;
             this.repository = repository;
            
         }

        [HttpGet()]
         public WeatherForecast[] DataToDo(int i)
         {
             return repository.DataToDo(i);
         }

    }

What if , instead of the writing code , we will auto – generate the actions ?

For this I was thinking that was fun to use Source Generators – https://devblogs.microsoft.com/dotnet/new-c-source-generator-samples/

The code should read like this:

[ApiController]
     [Route(“[controller]/[action]”)]
     public partial class WeatherForecastController : ControllerBase
     {

        private readonly ILogger<WeatherForecastController> _logger;

        [AutoActions]
         private readonly RepositoryWF repository;
        
         public WeatherForecastController(ILogger<WeatherForecastController> logger, RepositoryWF repository)
         {
             _logger = logger;
             this.repository = repository;
            
         }

    

    }

So the only thing that you should do is annotate your class with

        [AutoActions]
         private readonly RepositoryWF repository;

and the actions will be generated for you!

[ADCES] Presentation about .NET 5

My presentation were about EFCore, RoslynGenerators, Breaking Changes and ClickOnce.

Code and presentation at https://ignatandrei.github.io/Presentations/NET5prez.html .

Next presentation will be

De la Multinationala la Startup & Filbo – Technical Stack

Tuesday, Jan 12, 2021, 7:30 PM

Online event
,

19 Members Attending

Prezentare 1 : De la multinationala la startup si freelancer – sau cum sa iti parasesti cariera bine platita pentru alta Speaker: Daniel Tila, https://automationpill.com/ Descriere: Aventurile unui programator in cautarea businessului Prezentare 2: Filbo – Technical Stack Speaker: Adrian Nasui, https://www.linkedin.com/in/adrian-nasui-b887a5a9/…

Check out this Meetup →

[ADCES].NET 5 What’s new and awesome

Daniel Costea , Andrei Ignat si Dan Patrascu-Baba si o sa faca demo practice despre

1. C# – What’s new

2. ASP.NET Core – What’s new

3. EF Core – What’s new

4. Auto-Update de aplicatii Asp.NET Core si WPF prin ClickOnce

5. Roslyn Generators pentru code

6. Breaking changes

7 What’s new in Blazor on .NET 5?

Speaker: Dan Patrascu-Baba, http://danpatrascu.com/

Description: .NET 5 is probably one of the most important milestones in the history of .NET. Everybody is excited about the new release and we have plenty of reasons to be as a bunch o new features and improvements have been announced. In this talk we’ll dive deeper in the new Blazor features released with .NET 5, both for Blazor Server and Blazor WebAssembly. We’ll tackle concepts like CSS and JS isolation, lazy loading, secure local storage interactions and much more.

Va astept la https://www.meetup.com/Bucharest-A-D-C-E-S-Meetup/events/273633735/ !
( Acest eveniment este in colaborare cu .NET Romania )

Andrei Ignat weekly software news(mostly .NET)

* indicates required

Please select all the ways you would like to hear from me:

You can unsubscribe at any time by clicking the link in the footer of our emails. For information about our privacy practices, please visit our website.

We use Mailchimp as our marketing platform. By clicking below to subscribe, you acknowledge that your information will be transferred to Mailchimp for processing. Learn more about Mailchimp's privacy practices here.