Category: roslyn

RSCG – RSCG_WebAPIExports

RSCG – RSCG_WebAPIExports
 
 

name RSCG_WebAPIExports
nuget https://www.nuget.org/packages/RSCG_WebAPIExports/
link https://github.com/ignatandrei/RSCG_WebAPIExports/
author Andrei Ignat

Generating Excel from WebAPI json array

 

This is how you can use RSCG_WebAPIExports .

The code that you start with is


<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net7.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.10" />
    <PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
	<PackageReference Include="RSCG_WebAPIExports" Version="2023.8.16.1255" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
	<PackageReference Include="ArrayToExcel" Version="2.2.2" />

  </ItemGroup>
	<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>
	
</Project>


The code that you will use is


using RSCG_WebAPIExportsDemo;
using WebApiExportToFile;
var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddExport();
var app = builder.Build();
app.UseExport();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI(ct =>
    {
        ct.DocumentTitle = "try /WeatherForecast.xlsx";
        ct.HeadContent = "try /WeatherForecast.xlsx";
    });
}

//app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();



using Microsoft.AspNetCore.Mvc;

namespace RSCG_WebAPIExportsDemo.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class WeatherForecastController : ControllerBase
    {
        private static readonly string[] Summaries = new[]
        {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    };

        private readonly ILogger<WeatherForecastController> _logger;

        public WeatherForecastController(ILogger<WeatherForecastController> logger)
        {
            _logger = logger;
        }

        [HttpGet(Name = "GetWeatherForecast")]
        public IEnumerable<WeatherForecast> Get()
        {
            return Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
                TemperatureC = Random.Shared.Next(-20, 55),
                Summary = Summaries[Random.Shared.Next(Summaries.Length)]
            })
            .ToArray();
        }
    }
}

 

The code that is generated is

using Microsoft.AspNetCore.Rewrite;
namespace WebApiExportToFile;
#nullable enable
public static partial class Extensions
{
    static partial  void AddReturnTypesFromGenerator();
    public static IServiceCollection AddExport(this IServiceCollection services, params Type[]? typesReturnedByActions)
    {
        AddReturnTypesFromGenerator();
        //MiddlewareExportToFile.AddReturnType(typeof(Person[]));
        //MiddlewareExportToFile.AddReturnType(typeof(WeatherForecast[]));
        MiddlewareExportToFile.AddReturnTypes(typesReturnedByActions);

        return services.AddSingleton<MiddlewareExportToFile>();
    }
    public static IApplicationBuilder UseExport(this IApplicationBuilder app)
    {
        app.UseMiddleware<MiddlewareExportToFile>();
        var options = new RewriteOptions().Add(MiddlewareExportToFile.RewriteExtNeeded);
        app.UseRewriter(options);
        return app;
    }
}

#nullable disable
namespace WebApiExportToFile;

public static partial class Extensions
{
   static partial  void AddReturnTypesFromGenerator(){
       MiddlewareExportToFile.AddReturnType(typeof(RSCG_WebAPIExportsDemo.WeatherForecast[])); 
   }
}
using System.IO;
using System.Text;
using System;
using Microsoft.AspNetCore.Rewrite;
using System.Text.Json;
using ArrayToExcel;
using System.Text.Json.Serialization.Metadata;
using System.Runtime.CompilerServices;
#nullable enable
namespace WebApiExportToFile;
public class MiddlewareExportToFile : IMiddleware
{
    private static List<Type> types = new(); 
    static readonly string[] Extensions = new string[1] { ".xlsx" };
    static string key = "Export";
    public static void AddReturnTypes(params Type[]? typesReturnedByActions)
    {
        if (typesReturnedByActions?.Length > 0)
        {
            foreach (var type in typesReturnedByActions)
            {
                AddReturnType(type);
            }
        }
    }
    public static void AddReturnType(Type type)
    {
        types.Add(type);
    }
    public static void RewriteExtNeeded(RewriteContext context)
    {
        var request = context.HttpContext.Request;
        if (!(context.HttpContext.Items.ContainsKey(key) && context.HttpContext.Items[key]?.ToString() == "1"))
        {
            return;
        }
        var ext = Path.GetExtension(request.Path.Value);
        if (string.IsNullOrWhiteSpace(ext)) return;
        request.Path = request.Path.Value!.Substring(0, request.Path.Value.Length - ext.Length);

    }
    public bool ShouldIntercept(HttpContext context)
    {
        string path = context.Request.Path;
        var ext = Path.GetExtension(path);
        if (string.IsNullOrWhiteSpace(ext)) return false;
        if (!Extensions.Contains(ext, StringComparer.OrdinalIgnoreCase)) return false;
        return true;
    }
    //https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/migrate-from-newtonsoft
    static void AddMissingMemberHandling(JsonTypeInfo typeInfo)
    {
        if (typeInfo.Kind == JsonTypeInfoKind.Object &&
            typeInfo.Properties.All(prop => !prop.IsExtensionData) &&
            typeInfo.OnDeserialized is null)
        {
            // Dynamically attach dictionaries to deserialized objects.
            var cwt = new ConditionalWeakTable<object, Dictionary<string, object>>();

            JsonPropertyInfo propertyInfo =
                typeInfo.CreateJsonPropertyInfo(typeof(Dictionary<string, object>), "__extensionDataAttribute");
            propertyInfo.Get = obj => cwt.TryGetValue(obj, out Dictionary<string, object>? value) ? value : null;
            propertyInfo.Set = (obj, value) => cwt.Add(obj, (Dictionary<string, object>)value!);
            propertyInfo.IsExtensionData = true;
            typeInfo.Properties.Add(propertyInfo);
            typeInfo.OnDeserialized = obj =>
            {
                if (cwt.TryGetValue(obj, out Dictionary<string, object>? dict))
                {
                    cwt.Remove(obj);
                    throw new JsonException($"JSON properties {String.Join(", ", dict.Keys)} " +
                        $"could not bind to any members of type {typeInfo.Type}");
                }
            };
        }
    }
    public object[]? StrongDeserialize(string responseContent)
    {
        if(types.Count() == 0) throw new Exception("please add some types");
        foreach( var type in types)
        {
            try
            {
                var data = JsonSerializer.Deserialize(responseContent, type, new JsonSerializerOptions
                {
                    PropertyNameCaseInsensitive = true,
                    TypeInfoResolver = new DefaultJsonTypeInfoResolver
                    {
                        Modifiers = { AddMissingMemberHandling }
                    }
                }) as object[];
                return data;
            }
            catch(JsonException)
            {
                //do nothing 
                
            }
            
        }
        throw new Exception("no type can deserialize " +responseContent);

    }
    public async Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
        if (!ShouldIntercept(context))
        {
            await next(context);
            return;
        }
        var ext=Path.GetExtension(context.Request.Path.Value);
        var nameFile = context.Request.Path.Value?.Replace("/", "_");
        context.Items["Export"] = "1";
        var originalResponseBody = context.Response.Body;
        using var memoryStream = new MemoryStream();
        context.Response.Body = memoryStream;
        context.Response.Headers.Add("Content-Disposition", $"attachment; filename={nameFile}");
        await next(context);
        memoryStream.Seek(0, SeekOrigin.Begin);
        var responseContent = await new StreamReader(memoryStream).ReadToEndAsync();
        context.Response.Body = originalResponseBody;

        var data = StrongDeserialize(responseContent);
        ArgumentNullException.ThrowIfNull(data);
        using var excelStream = data.ToExcelStream();
        await excelStream.CopyToAsync(context.Response.Body);

        // No need to call the next middleware since the generated content has been sent
        return;
    }
    
}    

#nullable disable

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/RSCG_WebAPIExports

RSCG – Gobie

RSCG – Gobie
 
 

name Gobie
nuget https://www.nuget.org/packages/Gobie/
link https://github.com/GobieGenerator/Gobie/
author Mike Conrad

templating for classes , fields …

 

This is how you can use Gobie .

The code that you start with is


<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net7.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Gobie" Version="0.5.0-alpha" />
  </ItemGroup>
	<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>
</Project>


The code that you will use is


using GobieDemo;

Person p = new();
p.Name = "Andrei ";
//this comes from Gobie template
p.Id = 1;



using Gobie;
namespace GobieDemo;

[ClassGenAddId()]
partial class Person
{
    public string? Name { get; set; }

}





using Gobie;

namespace GobieDemo;
[GobieGeneratorName("ClassGenAddId")]
public sealed class ClassGenAddId : GobieClassGenerator
{
    [GobieFileTemplate("ID")]
    private const string LogString = @"
         using System;

            namespace {{ClassNamespace}};

            partial  class {{ClassName}}
            {
                public int Id { get; set; }
            }
    
    ";
}

 

The code that is generated is

namespace GobieDemo
{
    public partial class Person
    {
    }
}
using System;

namespace GobieDemo;
partial class Person
{
    public int Id { get; set; }
}
namespace Gobie
{
    /// <summary> This attribute will cause the generator defined by this thing here to
    /// run <see cref = "Gobie.ClassGenAddId"/> to run. </summary>
    public sealed class ClassGenAddIdAttribute : global::Gobie.GobieClassGeneratorAttribute
    {
        public ClassGenAddIdAttribute()
        {
        }
    }
}

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/Gobie

RSCG – OneOf

RSCG – OneOf
 
 

name OneOf
nuget https://www.nuget.org/packages/OneOf.SourceGenerator
https://www.nuget.org/packages/OneOf/
link https://github.com/mcintyre321/OneOf
author Harry McIntyre

Functional discriminated unions

 

This is how you can use OneOf .

The code that you start with is


<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net7.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="OneOf" Version="3.0.255" />
    <PackageReference Include="OneOf.SourceGenerator" Version="3.0.255" />
  </ItemGroup>
 <PropertyGroup>
        <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
        <CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
    </PropertyGroup>
</Project>


The code that you will use is


using OneOfDemo;

Console.WriteLine("Please enter data - string or number");

var data= Console.ReadLine();
//you can experiment with
StringOrNumber nr1 = 5;
var nr = new StringOrNumber(data);
var dataNumber = nr.TryGetNumber();
Console.WriteLine($"{dataNumber.isNumber} {dataNumber.number}");


using OneOf;
namespace OneOfDemo;

[GenerateOneOf]
public partial class StringOrNumber : OneOfBase<string, int> {
    public (bool isNumber, int number) TryGetNumber() =>
           Match( //this match function is auto generated
               s => (int.TryParse(s, out var n), n),
               i => (true, i)
           );
}


 

The code that is generated is

// <auto-generated />
using System;

#pragma warning disable 1591

namespace OneOf
{
    [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
    internal sealed class GenerateOneOfAttribute : Attribute
    {
    }
}

// <auto-generated />
#pragma warning disable 1591

namespace OneOfDemo
{
    partial class StringOrNumber
    {
        public StringOrNumber(OneOf.OneOf<string, int> _) : base(_) { }

        public static implicit operator StringOrNumber(string _) => new StringOrNumber(_);
        public static explicit operator string(StringOrNumber _) => _.AsT0;

        public static implicit operator StringOrNumber(int _) => new StringOrNumber(_);
        public static explicit operator int(StringOrNumber _) => _.AsT1;
    }
}

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/OneOf

RSCG – Ridge

RSCG – Ridge
 
 

name Ridge
nuget https://www.nuget.org/packages/Ridge/
link https://github.com/Melchy/Ridge
author Michal Motyčka

Generating test classes for controllers

 

This is how you can use Ridge .

The code that you start with is


<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net7.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.9" />
    <PackageReference Include="RidgeDotNet.AspNetCore" Version="2.0.1" />
    <PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
  </ItemGroup>
	<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>

</Project>


The code that you will use is


var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseAuthorization();

app.MapControllers();

app.Run();
public partial class Program { }


using Microsoft.AspNetCore.Mvc;
using Ridge.AspNetCore.GeneratorAttributes;

namespace RidgeDemoWebApp.Controllers
{
    [GenerateClient]
    [ApiController]
    [Route("[controller]")]
    public class WeatherForecastController : ControllerBase
    {
        private static readonly string[] Summaries = new[]
        {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    };

        private readonly ILogger<WeatherForecastController> _logger;

        public WeatherForecastController(ILogger<WeatherForecastController> logger)
        {
            _logger = logger;
        }

        [HttpGet(Name = "GetWeatherForecast")]
        public IEnumerable<WeatherForecast> Get()
        {
            return Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
                TemperatureC = Random.Shared.Next(-20, 55),
                Summary = Summaries[Random.Shared.Next(Summaries.Length)]
            })
            .ToArray();
        }
    }
}


using Microsoft.AspNetCore.Mvc.Testing;
using RidgeDemoWebApp.Controllers;
using Xunit;

namespace TestRidgeApp;

[TestClass]
public class BasicTests 
{

    
    [TestMethod]
    public async Task CallControllerUsingRidge()
    {
       
        using var webApplicationFactory =
            new WebApplicationFactory<Program>()
                .WithRidge(); // add ridge dependencies to WebApplicationFactory
        var client = webApplicationFactory.CreateClient();
        // create instance of client generated by source generator
        var examplesControllerClient = new WeatherForecastControllerClient(client, webApplicationFactory.Services);

        var response = await examplesControllerClient.Get();

        Assert.IsTrue(response.IsSuccessStatusCode);
        Assert.AreEqual(5, response.Result.Count());
    }
}

 

The code that is generated is

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by the Ridge source generator
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
#nullable enable
#pragma warning disable CS0419
using Ridge.AspNetCore;
using Ridge.AspNetCore.Serialization;
using Ridge.AspNetCore.Response;
using Ridge.AspNetCore.Parameters;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;

namespace RidgeDemoWebApp.Controllers
{
    /// <summary>
    /// Generated Api client. Calls <see cref="RidgeDemoWebApp.Controllers.WeatherForecastController" />
    /// </summary>
    public class WeatherForecastControllerClient
    {
        private readonly IApplicationClient _applicationClient;
        /// <summary>
        /// Creates client for controller. 
        /// </summary>
        /// <param name="httpClient">
        ///     HttpClient which will be used to call application.
        /// </param>
        /// <param name="serviceProvider">
        ///     Application serviceProvider.
        /// </param>
        public WeatherForecastControllerClient(HttpClient httpClient, IServiceProvider serviceProvider)
        {
            var applicationClientFactory = serviceProvider.GetService<IApplicationClientFactory>();
            if(applicationClientFactory == null)
            {
                throw new InvalidOperationException("'IApplicationClientFactory' could not be resolved. Did you forget to call WithRidge()?.");
            }
            else
            {
                _applicationClient = applicationClientFactory.CreateClient(serviceProvider, httpClient);
            }
        }
                /// <summary>
        ///     Calls <see cref="RidgeDemoWebApp.Controllers.WeatherForecastController.Get" />. 
        /// </summary> 
        public async Task<HttpCallResponse<System.Collections.Generic.IEnumerable<RidgeDemoWebApp.WeatherForecast>>> Get(params AdditionalParameter[] additionalParameters)
        {
            var methodName = nameof(RidgeDemoWebApp.Controllers.WeatherForecastController.Get);
            var actionParameters = new Type[] {
            };
            var parametersAndTransformations = new List<RawParameterAndTransformationInfo>()
            { 
            };
           return await _applicationClient.CallAction<System.Collections.Generic.IEnumerable<RidgeDemoWebApp.WeatherForecast>,RidgeDemoWebApp.Controllers.WeatherForecastController>(methodName, actionParameters, additionalParameters, parametersAndTransformations);
        }

 }
}
#pragma warning restore CS0419
#nullable restore

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/Ridge

RSCG – PropertyChangedSourceGenerator

RSCG – PropertyChangedSourceGenerator
 
 

name PropertyChangedSourceGenerator
nuget https://www.nuget.org/packages/PropertyChanged.SourceGenerator/
link https://github.com/canton7/PropertyChanged.SourceGenerator
author Antony Male

Generating PropertyChange to properties

 

This is how you can use PropertyChangedSourceGenerator .

The code that you start with is


<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net7.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="PropertyChanged.SourceGenerator" Version="1.0.8">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
  </ItemGroup>
	<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>
</Project>


The code that you will use is


using PropChangeDemo;

Person person = new ();
person.FirstName = "Andrei";
Console.WriteLine (person.FirstName);


using PropertyChanged.SourceGenerator;
namespace PropChangeDemo;

partial class Person
{
    [Notify]    
    private string? _FirstName;
}


 

The code that is generated is

// <auto-generated>
//     Auto-generated by PropertyChanged.SourceGenerator 1.0.8.0
// </auto-generated>
namespace PropertyChanged.SourceGenerator
{
    /// <summary>
    /// Specifies the accessibility of a generated property getter
    /// </summary>
    internal enum Getter
    {
        Public = 6,
        ProtectedInternal = 5,
        Internal = 4,
        Protected = 3,
        PrivateProtected = 2,
        Private = 1,
    }

    /// <summary>
    /// Specifies the accessibility of a generated property getter
    /// </summary>
    internal enum Setter
    {
        Public = 6,
        ProtectedInternal = 5,
        Internal = 4,
        Protected = 3,
        PrivateProtected = 2,
        Private = 1,
    }

    /// <summary>
    /// Instruct PropertyChanged.SourceGenerator to generate a property which implements INPC using this backing field
    /// </summary>
    [global::System.AttributeUsage(global::System.AttributeTargets.Field | global::System.AttributeTargets.Property, AllowMultiple = false)]
    [global::System.Diagnostics.Conditional("DEBUG")]
    internal class NotifyAttribute : global::System.Attribute
    {
        /// <summary>
        /// Generate a property whose name is derived from the name of this field, with a public getter and setter
        /// </summary>
        public NotifyAttribute() { }

        /// <summary>
        /// Generate a property with the given name, and optionally the given getter and setter accessibilities
        /// </summary>
        /// <param name="name">Name of the generated property</param>
        /// <param name="get">Accessibility of the generated getter</param>
        /// <param name="set">Accessibility of the generated setter</param>
        public NotifyAttribute(string name, Getter get = Getter.Public, Setter set = Setter.Public) { }

        /// <summary>
        /// Generate a property whose name is derived from the name of this field, with the given getter and optionally setter accessibilities
        /// </summary>
        /// <param name="get">Accessibility of the generated getter</param>
        /// <param name="set">Accessibility of the generated setter</param>
        public NotifyAttribute(Getter get, Setter set = Setter.Public) { }

        /// <summary>
        /// Generate a property whose name is derived from the name of this field, with a public getter and the given setter accessibility
        /// </summary>
        /// <param name="set">Accessibility of the generated setter</param>
        public NotifyAttribute(Setter set) { }

        /// <summary>
        /// If <c>true</c>, the generated property will be <c>virtual</c>.
        /// </summary>
        public bool IsVirtual { get; set; }
    }

    /// <summary>
    /// Instruct PropertyChanged.SourceGenerator to also raise INPC notifications for the named properties whenever the property this is applied to changes
    /// </summary>
    [global::System.AttributeUsage(global::System.AttributeTargets.Field | global::System.AttributeTargets.Property, AllowMultiple = true)]
    [global::System.Diagnostics.Conditional("DEBUG")]
    internal class AlsoNotifyAttribute : global::System.Attribute
    {
        /// <summary>
        /// Raise INPC notifications for the given properties when the property generated for this backing field changes
        /// </summary>
        /// <param name="otherProperties">Other properties to raise INPC notifications for</param>
        public AlsoNotifyAttribute(params string[] otherProperties) { }
    }

    /// <summary>
    /// Instruct PropertyChanged.SourceGenerator to raise INPC notifications for this property whenever one of the named generated properties is changed
    /// </summary>
    [global::System.AttributeUsage(global::System.AttributeTargets.Field | global::System.AttributeTargets.Property, AllowMultiple = false)]
    [global::System.Diagnostics.Conditional("DEBUG")]
    internal class DependsOnAttribute : global::System.Attribute
    {
        /// <summary>
        /// Raise an INPC notification for this property whenever one of the named properties is changed
        /// </summary>
        /// <param name="dependsOn">Other properties this property depends on</param>
        public DependsOnAttribute(params string[] dependsOn) { }
    }

    /// <summary>
    /// Instruct PropertyChanged.SourceGenerator to assign true to this boolean property whenver any generated member changes
    /// </summary>
    [global::System.AttributeUsage(global::System.AttributeTargets.Field | global::System.AttributeTargets.Property, AllowMultiple = true)]
    [global::System.Diagnostics.Conditional("DEBUG")]
    internal class IsChangedAttribute : global::System.Attribute
    {
    }

    /// <summary>
    /// Specifies an attribute which will be added to the generated property for this backing field
    /// </summary>
    /// <remarks>
    /// The string passed to this attribute will be placed verbatim into the generated code. All types must therefore by fully-qualified.
    /// </remarks>
    [global::System.AttributeUsage(global::System.AttributeTargets.Field | global::System.AttributeTargets.Property, AllowMultiple = true)]
    [global::System.Diagnostics.Conditional("DEBUG")]
    internal class PropertyAttributeAttribute : global::System.Attribute
    {
        /// <summary>
        /// Specify an attribute which iwll be added to the generated property for this backing field
        /// </summary>
        /// <param name="attribute">An attribute to place on the generated property</param>
        public PropertyAttributeAttribute(string attribute) { }
    }
}
// <auto-generated>
//     Auto-generated by PropertyChanged.SourceGenerator 1.0.8.0
// </auto-generated>
namespace PropertyChanged.SourceGenerator.Internal
{
    internal static class EventArgsCache
    {
        private static global::System.ComponentModel.PropertyChangedEventArgs _PropertyChanged_FirstName;
        public static global::System.ComponentModel.PropertyChangedEventArgs PropertyChanged_FirstName => _PropertyChanged_FirstName ??= new global::System.ComponentModel.PropertyChangedEventArgs(@"FirstName");
    }
}

// <auto-generated>
//     Auto-generated by PropertyChanged.SourceGenerator 1.0.8.0
// </auto-generated>
#nullable enable
namespace PropChangeDemo
{
    partial class Person : global::System.ComponentModel.INotifyPropertyChanged
    {
        /// <inheritdoc />
        public event global::System.ComponentModel.PropertyChangedEventHandler? PropertyChanged;
        public string? FirstName
        {
            get => this._FirstName;
            set
            {
                if (!global::System.Collections.Generic.EqualityComparer<string?>.Default.Equals(value, this._FirstName))
                {
                    this._FirstName = value;
                    this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_FirstName);
                }
            }
        }
        /// <summary>
        /// Raises the PropertyChanged event
        /// </summary>
        /// <param name="eventArgs">The EventArgs to use to raise the event</param>
        protected virtual void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs eventArgs)
        {
            this.PropertyChanged?.Invoke(this, eventArgs);
        }
    }
}

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/PropertyChangedSourceGenerator

RSCG – Strongly

RSCG – Strongly
 
 

name Strongly
nuget https://www.nuget.org/packages/Strongly/
link https://github.com/lucasteles/Strongly/
author Lucas Teles

Generate and customize strong id structs

 

This is how you can use Strongly .

The code that you start with is


<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net7.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Strongly" Version="1.1.0" OutputItemType="Analyzer" />
  </ItemGroup>
	<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>
</Project>


The code that you will use is


// See https://aka.ms/new-console-template for more information
using StronglyDemo;

Person p = new();
//p.SetBirthDate(1970, 4, 16);
p.SetBirthDate(new YearId(1970) , new MonthId(4),new DayId( 16));
Console.WriteLine(p.BirthDate);



using Strongly;

namespace StronglyDemo;


[Strongly(backingType: StronglyType.Int)]
public partial struct YearId { }

[Strongly(backingType: StronglyType.Int)]
public partial struct MonthId { }

[Strongly(backingType: StronglyType.Int)]
public partial struct DayId { }

internal class Person
{
    public DateTime BirthDate { get; internal set; }
    public void SetBirthDate(YearId yearId,MonthId monthId,DayId dayId)
    {
        BirthDate = new DateTime(yearId.Value, monthId.Value, dayId.Value);
    }
}


 

The code that is generated is

#if STRONGLY_TYPED_EMBED_ATTRIBUTES

using System;

namespace Strongly
{
    /// <summary>
    /// Place on partial structs to make the type a strongly-typed ID
    /// </summary>
    [AttributeUsage(AttributeTargets.Struct)]
    [System.Diagnostics.Conditional("STRONGLY_TYPED_USAGES")]
    internal sealed class StronglyAttribute : Attribute
    {
        /// <summary>
        /// Make the struct a strongly typed ID
        /// </summary>
        /// <param name="backingType">The <see cref="Type"/> to use to store the strongly-typed ID value.
        /// If not set, uses <see cref="StronglyDefaultsAttribute.BackingType"/>, which defaults to <see cref="StronglyType.Guid"/></param>
        /// <param name="converters">Converters to create for serializing/deserializing the strongly-typed ID value.
        /// If not set, uses <see cref="StronglyDefaultsAttribute.Converters"/>, which defaults to <see cref="StronglyConverter.NewtonsoftJson"/>
        /// and <see cref="StronglyConverter.TypeConverter"/></param>
        /// <param name="implementations">Interfaces and patterns the strongly typed id should implement
        /// If not set, uses <see cref="StronglyDefaultsAttribute.Implementations"/>, which defaults to <see cref="StronglyImplementations.IEquatable"/>
        /// and <see cref="StronglyImplementations.IComparable"/></param>
        public StronglyAttribute(
            StronglyType backingType = StronglyType.Default,
            StronglyConverter converters = StronglyConverter.Default,
            StronglyImplementations implementations = StronglyImplementations.Default)
        {
            BackingType = backingType;
            Converters = converters;
            Implementations = implementations;
        }

        /// <summary>
        /// The <see cref="Type"/> to use to store the strongly-typed ID value
        /// </summary>
        public StronglyType BackingType { get; }

        /// <summary>
        /// JSON library used to serialize/deserialize strongly-typed ID value
        /// </summary>
        public StronglyConverter Converters { get; }

        /// <summary>
        /// Interfaces and patterns the strongly typed id should implement
        /// </summary>
        public StronglyImplementations Implementations { get; }
    }
}
#endif
#if STRONGLY_TYPED_EMBED_ATTRIBUTES

using System;

namespace Strongly
{
    /// <summary>
    /// Converters used to to serialize/deserialize strongly-typed ID values
    /// </summary>
    [Flags]
    internal enum StronglyConverter
    {
        // Used with HasFlag, so needs to be 1, 2, 4 etc

        /// <summary>
        /// Don't create any converters for the strongly typed ID
        /// </summary>
        None = 0,

        /// <summary>
        /// Use the default converters for the strongly typed Id.
        /// This will be the value provided in the <see cref="StronglyDefaultsAttribute"/>, which falls back to
        /// <see cref="TypeConverter"/> and <see cref="SystemTextJson"/>
        /// </summary>
        Default = 1,

        /// <summary>
        /// Creates a <see cref="TypeConverter"/> for converting from the strongly typed ID to and from a string
        /// </summary>
        TypeConverter = 2,

        /// <summary>
        /// Creates a Newtonsoft.Json.JsonConverter for serializing the strongly typed id to its primitive value
        /// </summary>
        NewtonsoftJson = 4,

        /// <summary>
        /// Creates a System.Text.Json.Serialization.JsonConverter for serializing the strongly typed id to its primitive value
        /// </summary>
        SystemTextJson = 8,

        /// <summary>
        /// Creates an EF Core Value Converter for extracting the primitive value
        /// </summary>
        EfValueConverter = 16,

        /// <summary>
        /// Creates a Dapper TypeHandler for converting to and from the type
        /// </summary>
        DapperTypeHandler = 32,

        /// <summary>
        /// Creates a Swagger SchemaFilter for OpenApi documentation
        /// </summary>
        SwaggerSchemaFilter = 64,
    }
}
#endif
#if STRONGLY_TYPED_EMBED_ATTRIBUTES

using System;

namespace Strongly
{
    /// <summary>
    /// Used to control the default Place on partial structs to make the type a strongly-typed ID
    /// </summary>
    [AttributeUsage(AttributeTargets.Assembly, Inherited = false, AllowMultiple = false)]
    [System.Diagnostics.Conditional("STRONGLY_TYPED_USAGES")]
    internal sealed class StronglyDefaultsAttribute : Attribute
    {
        /// <summary>
        /// Set the default values used for strongly typed ids
        /// </summary>
        /// <param name="backingType">The <see cref="Type"/> to use to store the strongly-typed ID value.
        /// Defaults to <see cref="StronglyType.Guid"/></param>
        /// <param name="converters">JSON library used to serialize/deserialize strongly-typed ID value.
        /// Defaults to <see cref="StronglyConverter.SystemTextJson"/> and <see cref="StronglyConverter.TypeConverter"/></param>
        /// <param name="implementations">Interfaces and patterns the strongly typed id should implement
        /// Defaults to <see cref="StronglyImplementations.IEquatable"/> and <see cref="StronglyImplementations.IComparable"/></param>
        public StronglyDefaultsAttribute(
            StronglyType backingType = StronglyType.Default,
            StronglyConverter converters = StronglyConverter.Default,
            StronglyImplementations implementations = StronglyImplementations.Default)
        {
            BackingType = backingType;
            Converters = converters;
            Implementations = implementations;
        }

        /// <summary>
        /// The default <see cref="Type"/> to use to store the strongly-typed ID values.
        /// </summary>
        public StronglyType BackingType { get; }

        /// <summary>
        /// The default converters to create for serializing/deserializing strongly-typed ID values.
        /// </summary>
        public StronglyConverter Converters { get; }

        /// <summary>
        /// Interfaces and patterns the strongly typed id should implement
        /// </summary>
        public StronglyImplementations Implementations { get; }
    }
}
#endif
//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by the Strongly source generator
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

#pragma warning disable 1591 // publicly visible type or member must be documented

namespace StronglyDemo
{
    [System.Text.Json.Serialization.JsonConverter(typeof(DayIdSystemTextJsonConverter))]
    [System.ComponentModel.TypeConverter(typeof(DayIdTypeConverter))]
readonly partial struct DayId : System.IComparable<DayId>, System.IEquatable<DayId>
{
    public int Value { get; }

    public DayId(int value)
    {
        Value = value;
    }
    
    public static readonly DayId Empty = new DayId(0);

    public bool Equals(DayId other) => this.Value.Equals(other.Value);
    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        return obj is DayId other && Equals(other);
    }
    public override int GetHashCode() => Value.GetHashCode();
    public override string ToString() => Value.ToString();
    public static bool operator ==(DayId a, DayId b) => a.Equals(b);
    public static bool operator !=(DayId a, DayId b) => !(a == b);
    public static DayId Parse(string value) => new DayId(int.Parse(value));
    public static bool TryParse(string value, out DayId result)
    {
        if (int.TryParse(value, out int parseResult))
        {
            result = new DayId(parseResult);
            return true;
        }
        result = default;
        return false;
    }
public int CompareTo(DayId other) => Value.CompareTo(other.Value);


class DayIdTypeConverter : System.ComponentModel.TypeConverter
{
    public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType)
    {
        return sourceType == typeof(int) || sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
    }

    public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
    {
        return value switch
        {
            int intValue => new DayId(intValue),
            string stringValue when !string.IsNullOrEmpty(stringValue) && int.TryParse(stringValue, out var result) => new DayId(result),
            _ => base.ConvertFrom(context, culture, value),
        };
    }

    public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType)
    {
        return sourceType == typeof(int) || sourceType == typeof(string) || base.CanConvertTo(context, sourceType);
    }

    public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, System.Type destinationType)
    {
        if (value is DayId idValue)
        {
            if (destinationType == typeof(int))
            {
                return idValue.Value;
            }

            if (destinationType == typeof(string))
            {
                return idValue.Value.ToString();
            }
        }

        return base.ConvertTo(context, culture, value, destinationType);
    }
}


class DayIdSystemTextJsonConverter : System.Text.Json.Serialization.JsonConverter<DayId>
{
    public override DayId Read(ref System.Text.Json.Utf8JsonReader reader, System.Type typeToConvert, System.Text.Json.JsonSerializerOptions options)
    {
        return new DayId(reader.GetInt32());
    }

    public override void Write(System.Text.Json.Utf8JsonWriter writer, DayId value, System.Text.Json.JsonSerializerOptions options)
    {
        writer.WriteNumberValue(value.Value);
    }
}

    }
}

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by the Strongly source generator
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

#pragma warning disable 1591 // publicly visible type or member must be documented

namespace StronglyDemo
{
    [System.Text.Json.Serialization.JsonConverter(typeof(MonthIdSystemTextJsonConverter))]
    [System.ComponentModel.TypeConverter(typeof(MonthIdTypeConverter))]
readonly partial struct MonthId : System.IComparable<MonthId>, System.IEquatable<MonthId>
{
    public int Value { get; }

    public MonthId(int value)
    {
        Value = value;
    }
    
    public static readonly MonthId Empty = new MonthId(0);

    public bool Equals(MonthId other) => this.Value.Equals(other.Value);
    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        return obj is MonthId other && Equals(other);
    }
    public override int GetHashCode() => Value.GetHashCode();
    public override string ToString() => Value.ToString();
    public static bool operator ==(MonthId a, MonthId b) => a.Equals(b);
    public static bool operator !=(MonthId a, MonthId b) => !(a == b);
    public static MonthId Parse(string value) => new MonthId(int.Parse(value));
    public static bool TryParse(string value, out MonthId result)
    {
        if (int.TryParse(value, out int parseResult))
        {
            result = new MonthId(parseResult);
            return true;
        }
        result = default;
        return false;
    }
public int CompareTo(MonthId other) => Value.CompareTo(other.Value);


class MonthIdTypeConverter : System.ComponentModel.TypeConverter
{
    public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType)
    {
        return sourceType == typeof(int) || sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
    }

    public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
    {
        return value switch
        {
            int intValue => new MonthId(intValue),
            string stringValue when !string.IsNullOrEmpty(stringValue) && int.TryParse(stringValue, out var result) => new MonthId(result),
            _ => base.ConvertFrom(context, culture, value),
        };
    }

    public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType)
    {
        return sourceType == typeof(int) || sourceType == typeof(string) || base.CanConvertTo(context, sourceType);
    }

    public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, System.Type destinationType)
    {
        if (value is MonthId idValue)
        {
            if (destinationType == typeof(int))
            {
                return idValue.Value;
            }

            if (destinationType == typeof(string))
            {
                return idValue.Value.ToString();
            }
        }

        return base.ConvertTo(context, culture, value, destinationType);
    }
}


class MonthIdSystemTextJsonConverter : System.Text.Json.Serialization.JsonConverter<MonthId>
{
    public override MonthId Read(ref System.Text.Json.Utf8JsonReader reader, System.Type typeToConvert, System.Text.Json.JsonSerializerOptions options)
    {
        return new MonthId(reader.GetInt32());
    }

    public override void Write(System.Text.Json.Utf8JsonWriter writer, MonthId value, System.Text.Json.JsonSerializerOptions options)
    {
        writer.WriteNumberValue(value.Value);
    }
}

    }
}

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by the Strongly source generator
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

#pragma warning disable 1591 // publicly visible type or member must be documented

namespace StronglyDemo
{
    [System.Text.Json.Serialization.JsonConverter(typeof(YearIdSystemTextJsonConverter))]
    [System.ComponentModel.TypeConverter(typeof(YearIdTypeConverter))]
readonly partial struct YearId : System.IComparable<YearId>, System.IEquatable<YearId>
{
    public int Value { get; }

    public YearId(int value)
    {
        Value = value;
    }
    
    public static readonly YearId Empty = new YearId(0);

    public bool Equals(YearId other) => this.Value.Equals(other.Value);
    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        return obj is YearId other && Equals(other);
    }
    public override int GetHashCode() => Value.GetHashCode();
    public override string ToString() => Value.ToString();
    public static bool operator ==(YearId a, YearId b) => a.Equals(b);
    public static bool operator !=(YearId a, YearId b) => !(a == b);
    public static YearId Parse(string value) => new YearId(int.Parse(value));
    public static bool TryParse(string value, out YearId result)
    {
        if (int.TryParse(value, out int parseResult))
        {
            result = new YearId(parseResult);
            return true;
        }
        result = default;
        return false;
    }
public int CompareTo(YearId other) => Value.CompareTo(other.Value);


class YearIdTypeConverter : System.ComponentModel.TypeConverter
{
    public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType)
    {
        return sourceType == typeof(int) || sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
    }

    public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
    {
        return value switch
        {
            int intValue => new YearId(intValue),
            string stringValue when !string.IsNullOrEmpty(stringValue) && int.TryParse(stringValue, out var result) => new YearId(result),
            _ => base.ConvertFrom(context, culture, value),
        };
    }

    public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType)
    {
        return sourceType == typeof(int) || sourceType == typeof(string) || base.CanConvertTo(context, sourceType);
    }

    public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, System.Type destinationType)
    {
        if (value is YearId idValue)
        {
            if (destinationType == typeof(int))
            {
                return idValue.Value;
            }

            if (destinationType == typeof(string))
            {
                return idValue.Value.ToString();
            }
        }

        return base.ConvertTo(context, culture, value, destinationType);
    }
}


class YearIdSystemTextJsonConverter : System.Text.Json.Serialization.JsonConverter<YearId>
{
    public override YearId Read(ref System.Text.Json.Utf8JsonReader reader, System.Type typeToConvert, System.Text.Json.JsonSerializerOptions options)
    {
        return new YearId(reader.GetInt32());
    }

    public override void Write(System.Text.Json.Utf8JsonWriter writer, YearId value, System.Text.Json.JsonSerializerOptions options)
    {
        writer.WriteNumberValue(value.Value);
    }
}

    }
}

#if STRONGLY_TYPED_EMBED_ATTRIBUTES

using System;

namespace Strongly
{
    /// <summary>
    /// Interfaces and patterns the strongly typed id should implement
    /// </summary>
    [Flags]
    internal enum StronglyImplementations
    {
        // Used with HasFlag, so needs to be 1, 2, 4 etc

        /// <summary>
        /// Don't implement any additional members for the strongly typed ID
        /// </summary>
        None = 0,

        /// <summary>
        /// Use the default implementations for the strongly typed Id.
        /// This will be the value provided in the <see cref="StronglyDefaultsAttribute"/>, which falls back to
        /// <see cref="IEquatable"/> and <see cref="IComparable"/>
        /// </summary>
        Default = 1,

        // ReSharper disable once InconsistentNaming
        /// <summary>
        /// Implement the <see cref="IEquatable{T}"/> interface
        /// </summary>
        IEquatable = 2,

        // ReSharper disable once InconsistentNaming
        /// <summary>
        /// Implement the <see cref="IComparable{T}"/> interface
        /// </summary>
        IComparable = 4,
    }
}
#endif
#if STRONGLY_TYPED_EMBED_ATTRIBUTES

using System;

namespace Strongly
{
    /// <summary>
    /// The <see cref="Type"/> to use to store the value of a strongly-typed ID
    /// </summary>
    internal enum StronglyType
    {
        /// <summary>
        /// Use the default backing type (either the globally configured default, or Sequential Guid)
        /// </summary>
        Default = 0,
        Guid,
        SequentialGuid,
        GuidComb,
        Int,
        String,
        Long,
        NullableString,
        MassTransitNewId,
        BigInteger,
        Decimal,
    }
}
#endif

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/Strongly

RSCG – Injectio

RSCG – Injectio
 
 

name Injectio
nuget https://www.nuget.org/packages/Injectio/
link https://github.com/loresoft/Injectio
author LoreSoft

Attributes to DI helper

 

This is how you can use Injectio .

The code that you start with is


<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net7.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
	<ItemGroup>
		<PackageReference Include="Injectio" Version="2.6.1" />
		<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
	</ItemGroup>
	<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>
</Project>


The code that you will use is


using InjectioDemo;
using Microsoft.Extensions.DependencyInjection;

Console.WriteLine("Hello, World!");
ServiceCollection sc = new();
sc.AddInjectioDemo();
var b = sc.BuildServiceProvider();
var con = b.GetRequiredService<DatabaseCon>();
var db = b.GetRequiredService<IDatabase>();
db.Open();



using Injectio.Attributes;

namespace InjectioDemo;

[RegisterScoped]
internal class Database : IDatabase
{
    private readonly DatabaseCon con;

    public Database(DatabaseCon con)
    {
        this.con = con;
    }
    public void Open()
    {
        Console.WriteLine($"open {con.Connection}");
    }

}




namespace InjectioDemo
{
    internal interface IDatabase
    {
        public void Open();
    }
}


using Injectio.Attributes;

namespace InjectioDemo;

[RegisterSingleton]
internal class DatabaseCon
{
    public string? Connection { get; set; }
}



 

The code that is generated is

// <auto-generated />
#nullable enable

namespace Microsoft.Extensions.DependencyInjection
{
    /// <summary>
    /// Extension methods for discovered service registrations
    /// </summary>
    [global::System.CodeDom.Compiler.GeneratedCode("Injectio.Generators", "2.6.1.0")]
    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
    [global::System.Diagnostics.DebuggerStepThroughAttribute]
    public static class DiscoveredServicesExtensions
    {
        /// <summary>
        /// Adds discovered services from InjectioDemo to the specified service collection
        /// </summary>
        /// <param name="serviceCollection">The service collection.</param>
        /// <param name="tags">The service registration tags to include.</param>
        /// <returns>The service collection</returns>
        public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddInjectioDemo(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection serviceCollection, params string[]? tags)
        {
            var tagSet = new global::System.Collections.Generic.HashSet<string>(tags ?? global::System.Linq.Enumerable.Empty<string>());

            global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAdd(
                serviceCollection,
                global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor.Describe(
                    typeof(global::InjectioDemo.IDatabase),
                    typeof(global::InjectioDemo.Database), 
                    global::Microsoft.Extensions.DependencyInjection.ServiceLifetime.Scoped
                )
            );

            global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAdd(
                serviceCollection,
                global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor.Describe(
                    typeof(global::InjectioDemo.Database),
                    typeof(global::InjectioDemo.Database), 
                    global::Microsoft.Extensions.DependencyInjection.ServiceLifetime.Scoped
                )
            );

            global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAdd(
                serviceCollection,
                global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor.Describe(
                    typeof(global::InjectioDemo.DatabaseCon),
                    typeof(global::InjectioDemo.DatabaseCon), 
                    global::Microsoft.Extensions.DependencyInjection.ServiceLifetime.Singleton
                )
            );

            return serviceCollection;
        }
    }
}

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/Injectio

RSCG – NextGenMapper

RSCG – NextGenMapper
 
 

name NextGenMapper
nuget https://www.nuget.org/packages/NextGenMapper/
link https://github.com/DedAnton/NextGenMapper
author Anton Ryabchikov

Automating generating mapping between classes

 

This is how you can use NextGenMapper .

The code that you start with is


<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net7.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="NextGenMapper" Version="0.1.0-alpha.13" OutputItemType="Analyzer"  />
  </ItemGroup>
	<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>
</Project>


The code that you will use is


// See https://aka.ms/new-console-template for more information
using NextGenMapperDemo;

using NextGenMapper;

//var source = new Source("Anton", 25);

//var destination = source.Map<Destination>();

//Console.WriteLine(destination);


//record Source(string Name, int Age);
//record Destination(string Name, int Age);

Person p = new();
p.Name = "Andrei Ignat";
p.Country_Name = "Romania";

var dto = p.MapWith<PersonDTO>(
    BirthCountry:new Country()
    {
        CountryCode=p.Country_CountryCode,
        Name=p.Country_Name
    });

//Name is automatically mapped
Console.WriteLine(dto.Name);
Console.WriteLine(dto.BirthCountry!.Name);




namespace NextGenMapperDemo;

internal class Person
{
    public int ID { get; set; } 
    public string? Name { get; set; }
    public string? Country_Name { get; set; }
    public string? Country_CountryCode { get; set; }
}






namespace NextGenMapperDemo;
internal class Country
{

    public string? Name { get; set; }
    public string? CountryCode { get; set; }
}
internal class PersonDTO
{
    public int Id { get; set; }
    public string? Name { get; set; }
    public Country? BirthCountry { get; set; }
}


 

The code that is generated is

using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace NextGenMapper.Extensions
{
    internal static class MapperExtensions
    {
        /// <summary>
        /// Do not use this method, for auto-generated mapper only!
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static bool TryGetSpan<TSource>(this IEnumerable<TSource> source, out ReadOnlySpan<TSource> span)
        {
            bool result = true;
            if (source.GetType() == typeof(TSource[]))
            {
                span = Unsafe.As<TSource[]>(source);
            }
            #if NET5_0_OR_GREATER
            else if (source.GetType() == typeof(List<TSource>))
            {
                span = CollectionsMarshal.AsSpan(Unsafe.As<List<TSource>>(source));
            }
            #endif
            else
            {
                span = default;
                result = false;
            }

            return result;
        }
    }
}
#nullable enable
using NextGenMapper.Extensions;

namespace NextGenMapper
{
    internal static partial class Mapper
    {
        internal static NextGenMapperDemo.PersonDTO Map<To>(this NextGenMapperDemo.Person source) => new NextGenMapperDemo.PersonDTO()
        {
            Name = source.Name
        };
    }
}
#nullable enable
using NextGenMapper.Extensions;

namespace NextGenMapper
{
    internal static partial class Mapper
    {
        internal static NextGenMapperDemo.PersonDTO MapWith<To>
        (
            this NextGenMapperDemo.Person source,
            NextGenMapperDemo.Country BirthCountry
        )
        => new NextGenMapperDemo.PersonDTO
        {
            Name = source.Name,
            BirthCountry = BirthCountry
        };
    }
}
#nullable enable
using NextGenMapper.Extensions;

namespace NextGenMapper
{
    internal static partial class Mapper
    {
        internal static NextGenMapperDemo.PersonDTO MapWith<To>
        (
            this NextGenMapperDemo.Person source,
            int Id = default!,
            string? Name = default!,
            NextGenMapperDemo.Country? BirthCountry = default!
        )
        {
            throw new System.NotImplementedException("This method is a mock and is not intended to be called");
        }
    }
}
using System;
using System.Linq;

namespace NextGenMapper
{
    internal static partial class Mapper
    {
        internal static To Map<To>(this object source) => throw new InvalidOperationException($"Error when mapping {source.GetType()} to {typeof(To)}, mapping function was not found. Create custom mapping function.");

        internal static To MapWith<To>(this object source) => throw new InvalidOperationException($"Error when mapping {source.GetType()} to {typeof(To)}, mapping function was not found. Create custom mapping function.");
    
        internal static To Project<To>(this IQueryable<object> source) => throw new InvalidOperationException($"Error when project {source.GetType()} to {typeof(To)}, project function was not found.");
        
        internal static To ProjectWith<To>(this IQueryable<object> source) => throw new InvalidOperationException($"Error when project {source.GetType()} to {typeof(To)}, project function was not found.");
        
        internal static To Project<To>(this IQueryable source) => throw new InvalidOperationException($"Error when project {source.GetType()} to {typeof(To)}, projection for non generic IQueryable is not supported");

        internal static To ProjectWith<To>(this IQueryable source) => throw new InvalidOperationException($"Error when project {source.GetType()} to {typeof(To)}, projection for non generic IQueryable is not supported");
    }
}

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/NextGenMapper

RSCG – BenutomoAutomaticDisposeImplSourceGenerator

RSCG – BenutomoAutomaticDisposeImplSourceGenerator
 
 

name BenutomoAutomaticDisposeImplSourceGenerator
nuget https://www.nuget.org/packages/Benutomo.AutomaticDisposeImpl.SourceGenerator/
link https://github.com/benutomo-dev/RoslynComponents
author benutomo

Automatic dispose resources

 

This is how you can use BenutomoAutomaticDisposeImplSourceGenerator .

The code that you start with is


<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net7.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Benutomo.AutomaticDisposeImpl.SourceGenerator" Version="2.0.1">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
    
  </ItemGroup>
	 <PropertyGroup>
        <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
        <CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
    </PropertyGroup>

</Project>


The code that you will use is


using IDisposableGeneratorDemo;
//https://github.com/benutomo-dev/RoslynComponents
using (var db = new DALDB())
{
    Console.WriteLine("before releasing");
}
Console.WriteLine("after releasing");


namespace IDisposableGeneratorDemo;
using Benutomo;

[AutomaticDisposeImpl]
partial class DALDB :IDisposable
{
    [EnableAutomaticDispose]
    private readonly ConnectionDB cn;
    [EnableAutomaticDispose]
    private readonly ConnectionDB cn1;

    public DALDB()
    {
        cn = new ConnectionDB();
        cn1=new ConnectionDB();
    }
}



namespace IDisposableGeneratorDemo;

class ConnectionDB : IDisposable
{
    public void Dispose()
    {
        Console.WriteLine("disposing connectiondb");
    }
}


 

The code that is generated is

#pragma warning disable CS0436
#nullable enable

namespace Benutomo
{
    /// <summary>
    /// 指定したクラスに破棄(<see cref=""System.IDisposable"" />,<see cref=""System.IAsyncDisposable"" />)をサポートするメンバを破棄する<see cref=""System.IDisposable.Dispose"" />メソッドおよび<see cref=""System.IAsyncDisposable.DisposeAsync"" />メソッド(当該クラスに<see cref=""System.IAsyncDisposable"" />インターフェイスが含まれている場合のみ)を自動実装する。
    /// </summary>
    [global::System.AttributeUsage(global::System.AttributeTargets.Class)]
    internal class AutomaticDisposeImplAttribute : global::System.Attribute
    {
        /// <summary>
        /// 自動破棄実装の既定動作を設定する。
        /// </summary>
        public AutomaticDisposeImplMode Mode { get; set; }
    }
}
#pragma warning disable CS0436
#nullable enable

namespace Benutomo
{
    /// <summary>
    /// 破棄(<see cref=""System.IDisposable"" />,<see cref=""System.IAsyncDisposable"" />)をサポートするメンバを自動実装Disposeの対象とすることに関する振る舞いの指定。
    /// </summary>
    internal enum AutomaticDisposeImplMode
    {
        /// <summary>
        /// <see cref=""System.IDisposable"" />,<see cref=""System.IAsyncDisposable"" />を継承する型を持つメンバは暗黙的に自動Dispose呼び出しの対象となる。
        /// </summary>
        Implicit,

        /// <summary>
        /// <see cref=""System.IDisposable"" />,<see cref=""System.IAsyncDisposable"" />を継承する型を持つメンバは自動Dispose呼び出しの対象となる。
        /// </summary>
        Explicit,
    }
}
#pragma warning disable CS0436
#nullable enable

namespace Benutomo
{
    /// <summary>
    /// このメンバに対して、<see cref=""System.IDisposable.Dispose"" />メソッドまたは<see cref=""System.IAsyncDisposable.DisposeAsync"" />メソッドの自動呼出しは行いません。このオブジェクトで破棄するのが不適当であるかユーザ自身が<see cref=""System.IDisposable.Dispose"" />メソッドまたは<see cref=""System.IAsyncDisposable.DisposeAsync"" />メソッドの呼び出しを実装するメンバです。
    /// </summary>
    [global::System.AttributeUsage(global::System.AttributeTargets.Field | global::System.AttributeTargets.Property)]
    internal class DisableAutomaticDisposeAttribute : global::System.Attribute
    {
    }
}
#pragma warning disable CS0436
#nullable enable

namespace Benutomo
{
    /// <summary>
    /// このオブジェクトの破棄と同時に自動的に<see cref=""System.IDisposable.Dispose"" />メソッドまたは<see cref=""System.IAsyncDisposable.DisposeAsync"" />メソッドを呼び出します。
    /// </summary>
    [global::System.AttributeUsage(global::System.AttributeTargets.Field | global::System.AttributeTargets.Property)]
    internal class EnableAutomaticDisposeAttribute : global::System.Attribute
    {
        public EnableAutomaticDisposeAttribute() { }

        /// <summary>
        /// このオブジェクトの破棄と同時に自動的に<see cref=""System.IDisposable.Dispose"" />メソッドまたは<see cref=""System.IAsyncDisposable.DisposeAsync"" />メソッドを呼び出します。
        /// </summary>
        /// <param name=""linkedMembers"">このメンバの破棄に連動して破棄されるメンバ(ここで列挙されたメンバはEnable/DisableAutomaticDispose属性を省略可能)</param>
        public EnableAutomaticDisposeAttribute(params string[] dependencyMembers) { }
    }
}
#nullable enable
#pragma warning disable CS0612,CS0618,CS0619
namespace IDisposableGeneratorDemo
{
    partial class DALDB // This is implementation class by AutomaticDisposeImpl.
    {
        [global::System.ComponentModel.Browsable(false)]
        [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
        [global::System.Obsolete("AutomaticDisposeImplによって生成されたフィールドです。一般のコードから参照してはいけません。")]
        private const int __generator_internal_BeNotInitiatedAnyDispose = 0;
        [global::System.ComponentModel.Browsable(false)]
        [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
        [global::System.Obsolete("AutomaticDisposeImplによって生成されたフィールドです。一般のコードから参照してはいけません。")]
        private const int __generator_internal_InitiatedSyncDispose  = 1;
        [global::System.ComponentModel.Browsable(false)]
        [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
        [global::System.Obsolete("AutomaticDisposeImplによって生成されたフィールドです。一般のコードから参照してはいけません。")]
        private const int __generator_internal_InitiatedAsyncDispose = 2;
        [global::System.ComponentModel.Browsable(false)]
        [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
        [global::System.Obsolete("AutomaticDisposeImplによって生成されたフィールドです。一般のコードから参照してはいけません。")]
        private const int __generator_internal_DisposeAlreadyCompleted = 9;
        [global::System.ComponentModel.Browsable(false)]
        [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
        [global::System.Obsolete("AutomaticDisposeImplによって生成されたフィールドです。一般のコードから参照してはいけません。")]
        private int __generator_internal_disposeState = __generator_internal_BeNotInitiatedAnyDispose;

        public bool IsDisposed => (global::System.Threading.Thread.VolatileRead(ref __generator_internal_disposeState) != __generator_internal_BeNotInitiatedAnyDispose);

        [global::System.ComponentModel.Browsable(false)]
        [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
        [global::System.Obsolete("AutomaticDisposeImplによって生成されたフィールドです。一般のコードから参照してはいけません。")]
        private int __generator_internal_managedObjectDisposeState = 0;

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                var managedObjectDisposeState = global::System.Threading.Interlocked.Exchange(ref __generator_internal_managedObjectDisposeState, 1);
                if (managedObjectDisposeState == 0)
                {
                    try
                    {
                        (this.cn as global::System.IDisposable)?.Dispose();
                    }
                    catch (global::System.Exception ex)
                    {
                        global::System.Diagnostics.Debug.Fail($"Caught an exception in the cn.Dispose() calling. Message=\"{ex.Message}\"");
                    }
                    try
                    {
                        (this.cn1 as global::System.IDisposable)?.Dispose();
                    }
                    catch (global::System.Exception ex)
                    {
                        global::System.Diagnostics.Debug.Fail($"Caught an exception in the cn1.Dispose() calling. Message=\"{ex.Message}\"");
                    }
                }
            }
        }

        public void Dispose()
        {
            var dispose_state = global::System.Threading.Interlocked.CompareExchange(ref __generator_internal_disposeState, __generator_internal_InitiatedSyncDispose, __generator_internal_BeNotInitiatedAnyDispose);
            if (dispose_state == __generator_internal_BeNotInitiatedAnyDispose)
            {

                // Dispose managed members and release unmaneged resources.
                Dispose(disposing: true);

                global::System.Threading.Thread.VolatileWrite(ref __generator_internal_disposeState, __generator_internal_DisposeAlreadyCompleted);
            }
        }
    }
}

#pragma warning disable CS0436
#nullable enable

namespace Benutomo
{
    /// <summary>
    /// <see cref=""Benutomo.AutomaticDisposeImplAttribute""/>を利用しているクラスで、ユーザが実装するマネージドオブジェクトを非同期的な処理による破棄を行うメソッドに付与する。このメソッドはデストラクタからは呼び出されない。デストラクタからも呼び出される必要がある場合はデストラクタで必要な処理を全て同期的に行うようにした上で<see cref=""Benutomo.UnmanagedResourceReleaseMethodAttribute"">を使用すること。この属性を付与するメソッドは引数なしで戻り値は<see cref=""System.Threading.ValueTask"" />などawait可能な型である必要がある。このメソッドはこのオブジェクトのDisposeAsync()が初めて実行された時に自動実装コードから呼び出される。ただし、このメソッドを所有するクラスがIDisposableも実装していて、かつ、Dispose()によってこのオブジェクトが破棄された場合は、この属性が付与されているメソッドは呼び出されず、<see cref=""Benutomo.ManagedObjectDisposeMethodAttribute"">が付与されているメソッドが呼び出される。
    /// </summary>
    [global::System.AttributeUsage(global::System.AttributeTargets.Method)]
    internal class ManagedObjectAsyncDisposeMethodAttribute : global::System.Attribute
    {
        /// <summary>
        /// <inheritdoc cref=""Benutomo.ManagedObjectAsyncDisposeMethodAttribute""/>
        /// </summary>
        public ManagedObjectAsyncDisposeMethodAttribute() { }
    }
}
#pragma warning disable CS0436
#nullable enable

namespace Benutomo
{
    /// <summary>
    /// <see cref=""Benutomo.AutomaticDisposeImplAttribute""/>を利用しているクラスで、ユーザが実装するマネージドオブジェクトを同期的な処理による破棄を行うメソッドに付与する。このメソッドはデストラクタからは呼び出されない。デストラクタからも呼び出される必要がある場合は<see cref=""Benutomo.UnmanagedResourceReleaseMethodAttribute"">を使用すること。この属性を付与するメソッドは引数なしで戻り値はvoidである必要がある。このメソッドはこのオブジェクトのDispose()が初めて実行された時に自動実装コードから呼び出される。ただし、このメソッドを所有するクラスがIAsyncDisposableも実装していて、かつ、DisposeAsync()によってこのオブジェクトが破棄された場合は、この属性が付与されているメソッドは呼び出されず、<see cref=""Benutomo.ManagedObjectAsyncDisposeMethodAttribute"">が付与されているメソッドが呼び出される。
    /// </summary>
    [global::System.AttributeUsage(global::System.AttributeTargets.Method)]
    internal class ManagedObjectDisposeMethodAttribute : global::System.Attribute
    {
        /// <summary>
        /// <inheritdoc cref=""Benutomo.ManagedObjectDisposeMethodAttribute""/>
        /// </summary>
        public ManagedObjectDisposeMethodAttribute() { }
    }
}
#pragma warning disable CS0436
#nullable enable

namespace Benutomo
{
    /// <summary>
    /// <see cref=""Benutomo.AutomaticDisposeImplAttribute""/>を利用しているクラスで、ユーザが実装するアンマネージドリソースの解放を行うメソッド(引数なしで戻り値はvoid)に付与する。このメソッドはこのオブジェクトのDispose()またはDisposeAsync()、デストラクタのいずれかが初めて実行された時に自動実装コードから呼び出される。この属性を付与したメソッドは、実装者の責任でGCのファイナライズスレッドから呼び出されても問題無いように実装しなければならないことに注意すること。
    /// </summary>
    [global::System.AttributeUsage(global::System.AttributeTargets.Method)]
    internal class UnmanagedResourceReleaseMethodAttribute : global::System.Attribute
    {
        /// <summary>
        /// <inheritdoc cref=""Benutomo.UnmanagedResourceReleaseMethodAttribute""/>
        /// </summary>
        public UnmanagedResourceReleaseMethodAttribute() { }
    }
}

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/BenutomoAutomaticDisposeImplSourceGenerator

RSCG – SyncMethodGenerator

RSCG – SyncMethodGenerator
 
 

name SyncMethodGenerator
nuget https://www.nuget.org/packages/Zomp.SyncMethodGenerator/
link https://github.com/zompinc/sync-method-generator
author Zomp Inc.

Generating Sync method from async

 

This is how you can use SyncMethodGenerator .

The code that you start with is


<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net7.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Zomp.SyncMethodGenerator" Version="1.0.14" />
  </ItemGroup>
	<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>
</Project>


The code that you will use is


// See https://aka.ms/new-console-template for more information
using Zomp.SyncMethodGeneratorDemo;

Console.WriteLine("Hello, World!");
Writer.Haha("a.txt", "Andrei Ignat");
Writer.Write("a.txt", "andrei ignat");


namespace Zomp.SyncMethodGeneratorDemo;

partial class Writer
{
    [Zomp.SyncMethodGenerator.CreateSyncVersion]
    public static async Task WriteAsync(string file, string contents,
CancellationToken ct)
    {
        await File.WriteAllTextAsync(file, contents, ct).ConfigureAwait(true);
    }
    [Zomp.SyncMethodGenerator.CreateSyncVersion]
    public static async Task HahaAsync(ReadOnlyMemory<byte> buffer, Stream stream,
CancellationToken ct)
    => await stream.WriteAsync(buffer, ct).ConfigureAwait(true);
}


 

The code that is generated is

// <auto-generated/>
namespace Zomp.SyncMethodGenerator
{
    /// <summary>
    /// An attribute that can be used to automatically generate a synchronous version of an async method. Must be used in a partial class.
    /// </summary>
    [System.AttributeUsage(System.AttributeTargets.Method)]
    internal class CreateSyncVersionAttribute : System.Attribute
    {
    }
}
// <auto-generated/>
#nullable enable
namespace Zomp.SyncMethodGeneratorDemo;
partial class Writer
{
    public static void Haha(global::System.ReadOnlySpan<byte> buffer, global::System.IO.Stream stream)
    => stream.Write(buffer);
}

// <auto-generated/>
#nullable enable
namespace Zomp.SyncMethodGeneratorDemo;
partial class Writer
{
    public static void Write(string file, string contents)
    {
        global::System.IO.File.WriteAllText(file, contents);
    }
}

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/SyncMethodGenerator

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.