Category: roslyn

RSGC- MetadataFromObject – part 9

 

 

name Metadata from object
nuget

https://www.nuget.org/packages/AOPMethodsCommon/
https://www.nuget.org/packages/AOPMethodsGenerator/

link http://msprogrammer.serviciipeweb.ro/category/roslyn/
author Andrei Ignat

This will generate code to retrieve the values of properties directly, not by reflection
 

The code that you start with is


    [AutoMethods(template = TemplateMethod.CustomTemplateFile, CustomTemplateFileName = "GenerateFromPOCO.txt")]

    public partial class Person

    {

        public string FirstName { get; set; }

        public string LastName { get; set; }

    }


The code that you will use is



    var p = new Person();                                      

    p.FirstName = "Andrei";

    p.LastName = "Ignat";

    var last = p.ValueProperty(Person_EnumProps.LastName);

    var first = p.ValueProperty("FirstName");

    

    Console.WriteLine(last + " "+first);

 

The code that is generated is


    public enum Person_EnumProps{                                                                  

        None

        ,FirstName // Public 

        ,LastName // Public 

    }

    partial class Person{

        public object ValueProperty(Person_EnumProps val){

            if(val == Person_EnumProps.FirstName) {

                return this.FirstName;

            }

            if(val == Person_EnumProps.LastName) {

                return this.LastName;

            }

        throw new ArgumentException("cannot find "+ val);

        }

        public object ValueProperty(string val){

            if(string.Compare("FirstName",val,StringComparison.CurrentCultureIgnoreCase)==0) {

                return this.FirstName;

            }

            if(string.Compare("LastName",val,StringComparison.CurrentCultureIgnoreCase)==0) {

                return this.LastName;

            }

        throw new ArgumentException("cannot find "+ val);

        }

    }

Example Code: https://github.com/ignatandrei/RSCG_Examples/tree/main/MetadataFromObject

RSGC-Builder Design Pattern – part 8

 

 

name data-builder-generator
nuget

https://www.nuget.org/packages/DasMulli.DataBuilderGenerator/

link https://github.com/dasMulli/data-builder-generator
author Martin Andreas Ulrich

Implements the Builder Design pattern for any class. Useful , at least, for test projects
 

The code that you start with is


    [GenerateDataBuilder]                               

    public class Person

    {

         public string FirstName { get; set; }

         public string? MiddleNames { get; set; }

         public string LastName { get; set; }

         

    }


The code that you will use is



    var pOld = new Person();                                                                              

    pOld.FirstName = "Andrei";

    pOld.LastName = "Ignat";

    pOld.MiddleNames = "G";

    var build = new PersonBuilder(pOld).WithoutMiddleNames().WithFirstName("Florin");

    var pNew = build.Build();

    Console.WriteLine(pNew.FirstName);

 

The code that is generated is


    public partial class PersonBuilder                                         

         {

              private string? _firstName;

              private string? _middleNames;

              private string? _lastName;

              public PersonBuilder()

              {

              }

    

              public PersonBuilder(PersonBuilder otherBuilder)

              {

                   _firstName = otherBuilder._firstName;

                   _middleNames = otherBuilder._middleNames;

                   _lastName = otherBuilder._lastName;

              }

    

              public PersonBuilder(Person existingInstance)

              {

                   _firstName = existingInstance.FirstName;

                   _middleNames = existingInstance.MiddleNames;

                   _lastName = existingInstance.LastName;

              }

    

              public PersonBuilder WithFirstName(string firstName)

              {

                   var mutatedBuilder = new PersonBuilder(this);

                   mutatedBuilder._firstName = firstName;

                   return mutatedBuilder;

              }

    

              public PersonBuilder WithMiddleNames(string? middleNames)

              {

                   var mutatedBuilder = new PersonBuilder(this);

                   mutatedBuilder._middleNames = middleNames;

                   return mutatedBuilder;

              }

    

              public PersonBuilder WithoutMiddleNames()

              {

                   var mutatedBuilder = new PersonBuilder(this);

                   mutatedBuilder._middleNames = null;

                   return mutatedBuilder;

              }

    

              public PersonBuilder WithLastName(string lastName)

              {

                   var mutatedBuilder = new PersonBuilder(this);

                   mutatedBuilder._lastName = lastName;

                   return mutatedBuilder;

              }

    

              public Person Build()

              {

                   var instance = new Person();

                   if (!(_firstName is null))

                        instance.FirstName = _firstName;

                   if (!(_middleNames is null))

                        instance.MiddleNames = _middleNames;

                   if (!(_lastName is null))

                        instance.LastName = _lastName;

                   return instance;

              }

         }

Example Code: https://github.com/ignatandrei/RSCG_Examples/tree/main/DP_Builder

RSGC – Skinny Controllers- part 7

 

 

name Skinny Controllers
nuget

https://www.nuget.org/packages/SkinnyControllersCommon/
https://www.nuget.org/packages/SkinnyControllersGenerator/

link http://msprogrammer.serviciipeweb.ro/category/roslyn/
author Andrei Ignat

This will generate code for WebAPI for each method of a field in the controller
 

The code that you start with is


    public class PersonRepository                          

    {

        public async Task<Person> Get(int id)

        {

            await Task.Delay(1000);

            return new Person()

            {

                ID = id,

                Name = "Andrei " + id

            };

        }

    

        //add more functions here to make the demo

    }


The code that you will use is



    [AutoActions(template = TemplateIndicator.AllPostWithRecord,  FieldsName = new[] { "*" }, ExcludeFields = new[] { "_logger" })] 

    [ApiController]

    [Route("[controller]/[action]")]

    public partial class PersonController : ControllerBase

    {

        private readonly PersonRepository pr;

        private readonly ILogger<PersonController> _logger;

    

        public PersonController(PersonRepository pr, ILogger<PersonController> logger)

        {

            this.pr = pr;

            _logger = logger;

        }

    

    }

 

The code that is generated is


    [HttpPost]                                                                                             

    public System.Threading.Tasks.Task<AOPSkinnyController.Classes.Person> Get( recGet_143266108 data ){

        

            return 

        

        pr.Get(data.id);

    

    }

Example Code: https://github.com/ignatandrei/RSCG_Examples/tree/main/SkinnyControllers

RSGC – DTO Mapper – part 6

 

 

name GeneratedMapper
nuget

https://www.nuget.org/packages/GeneratedMapper/

link https://github.com/ThomasBleijendaal/GeneratedMapper
author Thomas Bleijendaal

AutoMapping from a POCO to a DTO. Lots of customizations
 

The code that you start with is


    public class Department                                    

        {

            public int ID { get; set; }

            public string Name { get; set; }

            

            public List<string> Employees { get; set; }

        }

    

        [IgnoreInTarget("Employees")]

        [MapFrom(typeof(Department))]

        public class DepartmentDTO

        {

            public int ID { get; set; }

            public string Name{get; set;}

    

            [MapWith("Employees",typeof(ResolverLength))]

            public int EmployeesNr { get; set; }

    

        }

        public class ResolverLength

        {

            public int Resolve(List<string> input)

            {

                return ((input?.Count) ?? 0);

            }

        }


The code that you will use is



    static void Main(string[] args)                                

    {

        var dep = new Department();

        dep.Name = "IT";

        dep.ID = 1;

        dep.Employees = new List<string>();

        dep.Employees.Add("Andrei");

        var dto = dep.MapToDepartmentDTO();

        Console.WriteLine(dto.Name+"=>"+ dto.EmployeesNr);

    }

 

The code that is generated is


    namespace DTOMapper                                                                                                                                                                                                        

    {

        public static partial class DepartmentMapToExtensions

        {

            public static DTOMapper.DepartmentDTO MapToDepartmentDTO(this DTOMapper.Department self)

            {

                if (self is null)

                {

                    throw new ArgumentNullException(nameof(self), "DTOMapper.Department -> DTOMapper.DepartmentDTO: Source is null.");

                }

                

                var resolverLength = new DTOMapper.ResolverLength();

                

                var target = new DTOMapper.DepartmentDTO

                {

                    ID = self.ID,

                    Name = (self.Name ?? throw new GeneratedMapper.Exceptions.PropertyNullException("DTOMapper.Department -> DTOMapper.DepartmentDTO: Property Name is null.")),

                    EmployeesNr = resolverLength.Resolve((self.Employees ?? throw new GeneratedMapper.Exceptions.PropertyNullException("DTOMapper.Department -> DTOMapper.DepartmentDTO: Property Employees is null."))),

                };

                

                return target;

            }

        }

    }

    

Example Code: https://github.com/ignatandrei/RSCG_Examples/tree/main/DTOMapper

RSGC-Constructor – Deconstructor – part 5

 

 

name CopyConstructor + Deconstructor
nuget

https://www.nuget.org/packages/AOPMethodsCommon/
https://www.nuget.org/packages/AOPMethodsGenerator/

link http://msprogrammer.serviciipeweb.ro/category/roslyn/
author Andrei Ignat

This will generate code for a POCO to generate copy constructor and deconstructor
 

The code that you start with is


    [AutoMethods(template = TemplateMethod.CustomTemplateFile, CustomTemplateFileName = "CopyConstructorDestructor.txt")]

    partial class Person

    {

       public string FirstName { get; set; }

       public string LastName { get; set; }

    }


The code that you will use is



    var pOldPerson = new Person();

    pOldPerson.FirstName = "Andrei";

    pOldPerson.LastName = "Ignat";

    var newPerson = new Person(pOldPerson);

    Console.WriteLine(newPerson.FirstName);

    var (_, last) = newPerson;

    Console.WriteLine(last);

 

The code that is generated is


    public Person (){                                                          

       OnConstructor();

    }

    public Person(IPerson other):base(){ 

         BeforeCopyConstructor(other);

         CopyPropertiesFrom(other);

         AfterCopyConstructor(other);

              

    }

    public void CopyPropertiesFrom(IPerson other){

    

         this.FirstName = other.FirstName;            

         this.LastName = other.LastName;            

    }    

    

    

    

     public void Deconstruct( out string FirstName, out string LastName)

     {

         FirstName = this.FirstName;            

         LastName = this.LastName;            

     }

Example Code: https://github.com/ignatandrei/RSCG_Examples/tree/main/CopyConstructor

RSGC-JSON to Class- part 4

 

 

name JsonByExampleGenerator
nuget

https://www.nuget.org/packages/JsonByExampleGenerator/

link https://github.com/hermanussen/JsonByExampleGenerator/
author Robin Hermanussen

This will generate C# classes from json files.
 

The code that you start with is


    {

    "FirstName": "Andrei",

    "LastName": "Ignat",

    "Blog": "http://msprogrammer.serviciipeweb.ro/"


The code that you will use is



    var p1 = new Person();

    p1.Blog = "http://msprogrammer.serviciipeweb.ro/";

    var config = new ConfigurationBuilder()

      .AddJsonFile("persons.json")

      .Build();

    

    var p = config.Get<Person>();

    var p2 = Person.FromConfig(config);

 

The code that is generated is


    [DataContract(Name = "Person", Namespace = "JsonToClass.Json.Persons")]

    public partial class Person

    {

    [DataMember(Name = "FirstName", EmitDefaultValue = false, Order = 0)]

    public string FirstName { get; set; }

    [DataMember(Name = "LastName", EmitDefaultValue = false, Order = 1)]

    public string LastName { get; set; }

    [DataMember(Name = "Blog", EmitDefaultValue = false, Order = 2)]

    public string Blog { get; set; }

    

    public static Person FromConfig([System.Diagnostics.CodeAnalysis.NotNull] IConfiguration config)

    {

    return config.Get<Person>();

    }

    }

Example Code: https://github.com/ignatandrei/RSCG_Examples/tree/main/JsonToClass

RSGC-Enum-part 3

 

 

name Enum
nuget

https://www.nuget.org/packages/AOPMethodsCommon/
https://www.nuget.org/packages/AOPMethodsGenerator/

link http://msprogrammer.serviciipeweb.ro/category/roslyn/
author Andrei Ignat

This will generate code to fast parsing a int or a string to an enum
 

The code that you start with is


    [AutoEnum(template = EnumMethod.GenerateExtensionCode)]

    public enum MathematicalOperation

    {

    None=0,

    Add=1,

    Multiplication=2

    }


The code that you will use is



    var fromInt = enumMathematicalOperation.ParseExactMathematicalOperation(1);

    var fromString = enumMathematicalOperation.ParseExactMathematicalOperation("add");

    Console.WriteLine(fromInt + "-"+fromString);

 

The code that is generated is


      [GeneratedCode("AOPMethods", "")] 

      [CompilerGenerated]

      public  static partial class enumMathematicalOperation{ 

       /*

        public static int idMathematicalOperation(){

        System.Diagnostics.Debugger.Break();

        return 1;

        }

        */

        public static RSCG_Enum.MathematicalOperation ParseExactMathematicalOperation(this long value, RSCG_Enum.MathematicalOperation? defaultValue = null){

                if(0 == value)

                    return RSCG_Enum.MathematicalOperation.None;

                        if(1 == value)

                    return RSCG_Enum.MathematicalOperation.Add;

                        if(2 == value)

                    return RSCG_Enum.MathematicalOperation.Multiplication;

            

            if(defaultValue != null)

                return defaultValue.Value;

    

            throw new ArgumentException("cannot find " + value +" for RSCG_Enum.MathematicalOperation  ");

        }

       

        public static RSCG_Enum.MathematicalOperation ParseExactMathematicalOperation(this string value, RSCG_Enum.MathematicalOperation? defaultValue = null){

            //trying to see if it is a value inside

            //if(!string.IsNullOrWhiteSpace)

            if(long.TryParse(value, out long valueParsed)){

                return ParseExactMathematicalOperation(valueParsed);

            }

    

                if(0==string.Compare("None" , value, StringComparison.InvariantCultureIgnoreCase))

                    return RSCG_Enum.MathematicalOperation.None;

                        if(0==string.Compare("Add" , value, StringComparison.InvariantCultureIgnoreCase))

                    return RSCG_Enum.MathematicalOperation.Add;

                        if(0==string.Compare("Multiplication" , value, StringComparison.InvariantCultureIgnoreCase))

                    return RSCG_Enum.MathematicalOperation.Multiplication;

            

    

            if(defaultValue != null)

                return defaultValue.Value

            throw new ArgumentException("cannot find " + value +" for RSCG_Enum.MathematicalOperation  ");

        }

        /*

        

        */

        

      }

Example Code: https://github.com/ignatandrei/RSCG_Examples/tree/main/Enum

RSCG- AppVersion–part 2

 

 

name ThisAssembly
nuget https://www.nuget.org/packages/ThisAssembly
link https://www.clarius.org/ThisAssembly/
author Daniel Cazzulino

The ThisAssembly.Info allows you access to the Assembly Information as constants, instead of going to reflection each time. I found useful to see the assembly version right away in any project that I have.

 

The code that you start with is in .csproj

<PropertyGroup>
<Version>2021.2.15.800</Version>
</PropertyGroup>

The code that you will use is

var strVersion = ThisAssembly.Info.Version;
Console.WriteLine(strVersion);

 

The code that is generated is

/// <summary>
/// Provides access to the current assembly information as pure constants, 
///  without requiring reflection.
/// </summary>
partial class ThisAssembly
{
    /// <summary>
    /// Gets the AssemblyInfo attributes.
    /// </summary>
    [GeneratedCode("ThisAssembly.AssemblyInfo", "1.0.0")]
    [CompilerGenerated]
    public static partial class Info
    {
        public const string Company = @"RSCG_Version";

        public const string Configuration = @"Debug";

        public const string FileVersion = @"2021.2.15.800";

        public const string InformationalVersion = @"2021.2.15.800";

        public const string Product = @"RSCG_Version";

        public const string Title = @"RSCG_Version";

        public const string Version = @"2021.2.15.800";

    }
}

Example Code: https://github.com/ignatandrei/RSCG_Examples/tree/main/ApplicationVersion

RSCG–part 1

Roslyn Source Code Generators are a easy way to generate automatically code that can be injected at compile time. This code can be generated on some template file or based on existing code or both . It works by intercepting at compile time the result of compilation of the existing source code and adding to this compilation other files. It cannot modify the code, just add to it.

You can see a deep tutorial about how to do it at https://khalidabuhakmeh.com/dotnet-5-source-generators-jump-start . Also, you can read more at https://devblogs.microsoft.com/dotnet/introducing-c-source-generators/ and at https://github.com/dotnet/roslyn/blob/master/docs/features/source-generators.cookbook.md .

There are a fair amount of them  – and I intend to detail their use .  That means

1. I will show how they can be useful for you

2. I will show examples with source code .

3. Make a video with this.

AOPMethods–dogfooding

I was trying to apply AOPMethods to – surprise! –  AOPMethods project itself. And I have discovered a new reason: I do not want to make the methods public. I just want to put try/catch around them to know what is wrong.

The fast – and not so good – idea was to transform

MethodPrefix =”pub”

into a

Dictionary<MethodsPrefix: string,  VIsibilty: string>

in order to pass something like that

{ “pub”, “public” } , {“prv”, “private”}

The second idea was better : what if I allow multiple instances and generate accordingly  ?

So I came up with this definition

[AutoMethods(template = TemplateMethod.MethodWithPartial, MethodPrefix =”pub”)]
[AutoMethods(template = TemplateMethod.CustomTemplateFile,CustomTemplateFileName =”privateTryCatch.txt”,  MethodSuffix = “bup”)]
partial class Person

And the code  processes both attributes – and generates the code. One slightly problem: Cannot have GeneratedCode and CompilerGenerated on both generated files. But – it works!

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.