Category: .NET

RSCG – ThisAssembly.Strings

RSCG – ThisAssembly.Strings
 
 

name ThisAssembly.Strings
nuget https://www.nuget.org/packages/ThisAssembly.Strings/
link https://github.com/devlooped/ThisAssembly
author Daniel Cazzulino

generating code from resx files

 

This is how you can use ThisAssembly.Strings .

The code that you start with is


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

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

  <ItemGroup>
    <PackageReference Include="ThisAssembly.Strings" Version="1.4.3">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
  </ItemGroup>

  <ItemGroup>
    <Compile Update="Demo.Designer.cs">
      <DesignTime>True</DesignTime>
      <AutoGen>True</AutoGen>
      <DependentUpon>Demo.resx</DependentUpon>
    </Compile>
  </ItemGroup>

  <ItemGroup>
    <EmbeddedResource Update="Demo.resx">
      <Generator>ResXFileCodeGenerator</Generator>
      <LastGenOutput>Demo.Designer.cs</LastGenOutput>
    </EmbeddedResource>
  </ItemGroup>
	<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>
</Project>


The code that you will use is


Console.WriteLine(ThisAssembly.Strings.PersonName("Andrei Ignat"));



<?xml version="1.0" encoding="utf-8"?>
<root>
  <!-- 
    Microsoft ResX Schema 
    
    Version 2.0
    
    The primary goals of this format is to allow a simple XML format 
    that is mostly human readable. The generation and parsing of the 
    various data types are done through the TypeConverter classes 
    associated with the data types.
    
    Example:
    
    ... ado.net/XML headers & schema ...
    <resheader name="resmimetype">text/microsoft-resx</resheader>
    <resheader name="version">2.0</resheader>
    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
        <value>[base64 mime encoded serialized .NET Framework object]</value>
    </data>
    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
        <comment>This is a comment</comment>
    </data>
                
    There are any number of "resheader" rows that contain simple 
    name/value pairs.
    
    Each data row contains a name, and value. The row also contains a 
    type or mimetype. Type corresponds to a .NET class that support 
    text/value conversion through the TypeConverter architecture. 
    Classes that don't support this are serialized and stored with the 
    mimetype set.
    
    The mimetype is used for serialized objects, and tells the 
    ResXResourceReader how to depersist the object. This is currently not 
    extensible. For a given mimetype the value must be set accordingly:
    
    Note - application/x-microsoft.net.object.binary.base64 is the format 
    that the ResXResourceWriter will generate, however the reader can 
    read any of the formats listed below.
    
    mimetype: application/x-microsoft.net.object.binary.base64
    value   : The object must be serialized with 
            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
            : and then encoded with base64 encoding.
    
    mimetype: application/x-microsoft.net.object.soap.base64
    value   : The object must be serialized with 
            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
            : and then encoded with base64 encoding.

    mimetype: application/x-microsoft.net.object.bytearray.base64
    value   : The object must be serialized into a byte array 
            : using a System.ComponentModel.TypeConverter
            : and then encoded with base64 encoding.
    -->
  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
    <xsd:element name="root" msdata:IsDataSet="true">
      <xsd:complexType>
        <xsd:choice maxOccurs="unbounded">
          <xsd:element name="metadata">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" />
              </xsd:sequence>
              <xsd:attribute name="name" use="required" type="xsd:string" />
              <xsd:attribute name="type" type="xsd:string" />
              <xsd:attribute name="mimetype" type="xsd:string" />
              <xsd:attribute ref="xml:space" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="assembly">
            <xsd:complexType>
              <xsd:attribute name="alias" type="xsd:string" />
              <xsd:attribute name="name" type="xsd:string" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="data">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
              <xsd:attribute ref="xml:space" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="resheader">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" use="required" />
            </xsd:complexType>
          </xsd:element>
        </xsd:choice>
      </xsd:complexType>
    </xsd:element>
  </xsd:schema>
  <resheader name="resmimetype">
    <value>text/microsoft-resx</value>
  </resheader>
  <resheader name="version">
    <value>2.0</value>
  </resheader>
  <resheader name="reader">
    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <resheader name="writer">
    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <data name="PersonName" xml:space="preserve">
    <value>The person name is {0}</value>
    <comment>the person name</comment>
  </data>
</root>

 

The code that is generated is

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//
//     ThisAssembly.Strings: 1.4.3
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
using System;
using System.Globalization;

partial class ThisAssembly
{
    public static partial class Strings
    {
        /// <summary>
        /// the person name
        /// </summary>
        public static string PersonName(object arg0) => string.Format(CultureInfo.CurrentCulture, Strings.GetResourceManager("StringsDemo.Demo").GetString("PersonName"), arg0);
    }
}
using System.Collections.Concurrent;
using System.Resources;
using System.Runtime.CompilerServices;

/// <summary>
/// Provides access to the current assembly information as pure constants, 
///  without requiring reflection.
/// </summary>
partial class ThisAssembly
{
    /// <summary>
    /// Access the strings provided by resource files in the project.
    /// </summary>
    [CompilerGenerated]
    public static partial class Strings
    {
        static ConcurrentDictionary<string, ResourceManager> resourceManagers = new ConcurrentDictionary<string, ResourceManager>();

        static ResourceManager GetResourceManager(string resourceName)
            => resourceManagers.GetOrAdd(resourceName, name => new ResourceManager(name, typeof(Strings).Assembly));
    }
}

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/ThisAssembly.Strings

RSCG – ThisAssembly.Metadata

RSCG – ThisAssembly.Metadata    

name ThisAssembly.Metadata
nuget https://www.nuget.org/packages/ThisAssembly.Metadata/
link https://github.com/devlooped/ThisAssembly
author Daniel Cazzulino

Generating code from assembly metadata

 

This is how you can use ThisAssembly.Metadata .

The code that you start with is

  <Project Sdk="Microsoft.NET.Sdk">    <PropertyGroup>     <OutputType>Exe</OutputType>     <TargetFramework>net8.0</TargetFramework>     <ImplicitUsings>enable</ImplicitUsings>     <Nullable>enable</Nullable>   </PropertyGroup> 	<ItemGroup> 		<AssemblyMetadata Include="MyName" Value="Andrei" /> 	</ItemGroup> 	<ItemGroup> 	  <PackageReference Include="ThisAssembly.Metadata" Version="1.4.3"> 	    <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

  [assembly: System.Reflection.AssemblyMetadataAttribute("Name", "Test")]  Console.WriteLine(ThisAssembly.Metadata.Name); Console.WriteLine(ThisAssembly.Metadata.MyName);   

  The code that is generated is

 //------------------------------------------------------------------------------ // <auto-generated> //     This code was generated by a tool. // //     Changes to this file may cause incorrect behavior and will be lost if //     the code is regenerated. // </auto-generated> //------------------------------------------------------------------------------  using System.CodeDom.Compiler; using System.Runtime.CompilerServices;  /// <summary> /// Provides access to the current assembly information as pure constants,  ///  without requiring reflection. /// </summary> partial class ThisAssembly {     /// <summary>     /// Gets the assembly metadata.     /// </summary>     [GeneratedCode("ThisAssembly.Metadata", "1.4.3")]     [CompilerGenerated]     public static partial class Metadata     {         /// <summary>Name = Test</summary>         public const string Name =  """ Test """;          /// <summary>MyName = Andrei</summary>         public const string MyName =  """ Andrei """;      } } 

Code and pdf at https://ignatandrei.github.io/RSCG_Examples/v2/docs/ThisAssembly.Metadata

RSCG – Pekspro.BuildInformationGenerator

RSCG – Pekspro.BuildInformationGenerator    

name Pekspro.BuildInformationGenerator
nuget https://www.nuget.org/packages/Pekspro.BuildInformationGenerator/
link https://github.com/pekspro/BuildInformationGenerator
author pekspro

adding git build information

  

This is how you can use Pekspro.BuildInformationGenerator .

The code that you start with is


<project sdk="Microsoft.NET.Sdk">

  <propertygroup>
    <outputtype>Exe</outputtype>
    <targetframework>net8.0</targetframework>
    <implicitusings>enable</implicitusings>
    <nullable>enable</nullable>
  </propertygroup>

  <itemgroup>
    <packagereference version="0.2.0" include="Pekspro.BuildInformationGenerator">
  </packagereference>
	<propertygroup>
		<emitcompilergeneratedfiles>true</emitcompilergeneratedfiles>
		<compilergeneratedfilesoutputpath>$(BaseIntermediateOutputPath)\GX</compilergeneratedfilesoutputpath>
	</propertygroup>
</itemgroup>


The code that you will use is


using BuildInfo;

Console.WriteLine(MyBuildInfo.Git.CommitId);
Console.WriteLine(MyBuildInfo.Git.Branch);
Console.WriteLine(MyBuildInfo.AssemblyVersionString);




using Pekspro.BuildInformationGenerator;

namespace BuildInfo;
[BuildInformation(AddBuildTime = true, 
    AddGitCommitId = true,
    AddAssemblyVersion = true,
    AddDotNetSdkVersion = true,
    AddGitBranch = true,
    AddLocalBuildTime= true,
    AddOSVersion = true,    
    FakeIfDebug =false,
    FakeIfRelease =false)]
partial class MyBuildInfo
{
}


   The code that is generated is

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

namespace BuildInfo
{
    /// <summary>
    /// Build information.
    /// </summary>
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Pekspro.BuildInformationGenerator", "0.2.0")]
    static partial class MyBuildInfo
    {

        /// <summary>
        /// Build time: 2024-07-18 19:57:53
        /// Value was taken from the system clock.
        /// </summary>
        public static readonly global::System.DateTime BuildTime = new global::System.DateTime(638569294731432248L, global::System.DateTimeKind.Utc);

        /// <summary>
        /// Local build time: 2024-07-18 22:57:53 (+03:00)
        /// Value was taken from the system clock.
        /// </summary>
        public static readonly global::System.DateTimeOffset LocalBuildTime = new global::System.DateTimeOffset(638569402731432248L, new global::System.TimeSpan(108000000000));

        /// <summary>
        /// Build information related to git.
        /// </summary>
        static public partial class Git
        {

            /// <summary>
            /// The commit id in git at the time of build.
            /// Value was taken from the AssemblyInformationalVersion attribute.
            /// </summary>
            public const string CommitId = "51a6dd67bbe091af607870fd80a52ea54d249e47";

            /// <summary>
            /// The short commit id in git at the time of build.
            /// Value was taken from the AssemblyInformationalVersion attribute.
            /// </summary>
            public const string ShortCommitId = "51a6dd67";

            /// <summary>
            /// The git branch used at build time.
            /// Value was taken from the git branch command.
            /// </summary>
            public const string Branch = "278-httpsgithubcompeksprobuildinformationgenerator";

        }

        /// <summary>
        /// Version of the assembly.
        /// Value was taken from assembly version attribute.
        /// </summary>
        public const string AssemblyVersionString = "1.0.0.0";

        /// <summary>
        /// OS version of the building machine.
        /// Value was taken from Environment.OSVersion.
        /// </summary>
        public const string OSVersion = "Microsoft Windows NT 6.2.9200.0";

        /// <summary>
        /// .NET SDK version used at build time.
        /// Value was taken from the dotnet --version command.
        /// </summary>
        public const string DotNetSdkVersion = "8.0.303";

    }
}

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/Pekspro.BuildInformationGenerator

RSCG – ThisAssembly.Constants

RSCG – ThisAssembly.Constants    

name ThisAssembly.Constants
nuget https://www.nuget.org/packages/ThisAssembly.Constants/
link https://github.com/devlooped/ThisAssembly
author Daniel Cazzulino

Generating Constants from csproj

 

This is how you can use ThisAssembly.Constants .

The code that you start with is

  <Project Sdk="Microsoft.NET.Sdk">    <PropertyGroup>     <OutputType>Exe</OutputType>     <TargetFramework>net8.0</TargetFramework>     <ImplicitUsings>enable</ImplicitUsings>     <Nullable>enable</Nullable>   </PropertyGroup>    <ItemGroup> 	  <Constant Include="TimeOut" Value="100" Comment="Test" />     <PackageReference Include="ThisAssembly.Constants" Version="1.4.3">       <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

  Console.WriteLine(ThisAssembly.Constants.TimeOut);  

  The code that is generated is

 //------------------------------------------------------------------------------ // <auto-generated> //     This code was generated by a tool. // //     ThisAssembly.Constants: 1.4.3 // //     Changes to this file may cause incorrect behavior and will be lost if //     the code is regenerated. // </auto-generated> //------------------------------------------------------------------------------ using System; using System.Globalization;  partial class ThisAssembly {     public static partial class Constants     {         /// <summary>         /// Test         /// </summary>         public const string TimeOut = """ 100 """;     } } 

Code and pdf at https://ignatandrei.github.io/RSCG_Examples/v2/docs/ThisAssembly.Constants

RSCG – JKToolKit.TemplatePropertyGenerator

RSCG – JKToolKit.TemplatePropertyGenerator    

name JKToolKit.TemplatePropertyGenerator
nuget https://www.nuget.org/packages/JKToolKit.TemplatePropertyGenerator/
link https://github.com/JKamsker/JKToolKit.TemplatePropertyGenerator
author Jonas Kamsker

String templating for a class

  

This is how you can use JKToolKit.TemplatePropertyGenerator .

The code that you start with is


<project sdk="Microsoft.NET.Sdk">

  <propertygroup>
    <outputtype>Exe</outputtype>
    <targetframework>net8.0</targetframework>
    <implicitusings>enable</implicitusings>
    <nullable>enable</nullable>
  </propertygroup>

  <itemgroup>
    <packagereference version="0.0.4" include="JKToolKit.TemplatePropertyGenerator">
  </packagereference>
	<propertygroup>
		<emitcompilergeneratedfiles>true</emitcompilergeneratedfiles>
		<compilergeneratedfilesoutputpath>$(BaseIntermediateOutputPath)\GX</compilergeneratedfilesoutputpath>
	</propertygroup>
</itemgroup>


The code that you will use is


using SimpleTemplate;

Person person = new();
person.FirstName = "Andrei";
person.LastName = "Ignat";
Console.WriteLine(new Person.HelloClass().Format(person.FirstName,person.LastName));


namespace SimpleTemplate;
[TemplateProperty("Hello", "Hello {name1}, {name2}!")]
internal partial class Person
{
    public string? FirstName { get; set; }
    public string? LastName { get; set; }
}


   The code that is generated is

global using JKToolKit.TemplatePropertyGenerator.Attributes;

using System;

namespace JKToolKit.TemplatePropertyGenerator.Attributes;

[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
internal class TemplatePropertyAttribute : Attribute
{
    public string Name { get; }
    public string Format { get; }

    public TemplatePropertyAttribute(string name, string format)
    {
        Name = name;
        Format = format;
    }
}
// <auto-generated>
using System;
namespace SimpleTemplate
{
    internal partial class Person
    {
        public static readonly HelloClass Hello = new();

        public class HelloClass
        {
            public string Template =&gt; "Hello {name1}, {name2}!";

            internal HelloClass()
            {
            }

            public string Format(string name1, string name2)
            {
                return $"Hello {name1}, {name2}!";
            }

            public FormattableString AsFormattable(string name1, string name2)
            {
                return $"Hello {name1}, {name2}!";
            }
        }
    }
}

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/JKToolKit.TemplatePropertyGenerator

RSCG – MinimalApis.Discovery

name MinimalApis.Discovery
nuget https://www.nuget.org/packages/MinimalApis.Discovery/
link https://github.com/shawnwildermuth/MinimalApis
author Shawn Wildermuth

Controller like API registering

 

This is how you can use MinimalApis.Discovery .

The code that you start with is


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

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

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.4" />
    <PackageReference Include="MinimalApis.Discovery" Version="1.0.7" />
    <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


using Microsoft.AspNetCore.Http.HttpResults;
using MinimalApis.Discovery;
namespace APIDemo;

public class PersonAPI : IApi
{
    public void Register(IEndpointRouteBuilder builder)
    {
        var grp = builder.MapGroup("/api/Person");
        grp.MapGet("", GetFromId);
        grp.MapGet("{id:int}", GetFromId);
        //todo: add more routes
    }
    public static async Task<Person[]> GetAll()
    {       
        await Task.Delay(1000);
        return new[] { new Person { FirstName = "Ignat", LastName = "Andrei" } };
    }

    public static async Task<Results<Ok<Person>,NotFound<string>>> GetFromId(int id)
    {
        await Task.Delay(1000);
        if (id == 1)
        {
            return TypedResults.Ok<Person>(new Person { FirstName = "Ignat", LastName = "Andrei" });
        }
        return TypedResults.NotFound<string>("Person not found");
    }
}



namespace APIDemo;

public class Person
{
    public string? FirstName { get; set; }
    public string? LastName { get; set; }
}



using MinimalApis.Discovery;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
// 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.UseHttpsRedirection();

var summaries = new[]
{
    "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};

app.MapGet("/weatherforecast", () =>
{
    var forecast = Enumerable.Range(1, 5).Select(index =>
        new WeatherForecast
        (
            DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
            Random.Shared.Next(-20, 55),
            summaries[Random.Shared.Next(summaries.Length)]
        ))
        .ToArray();
    return forecast;
})
.WithName("GetWeatherForecast")
.WithOpenApi();

app.MapApis();

app.Run();

internal record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
{
    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}


 

The code that is generated is

// <auto-generated/>
using System;
using Microsoft.AspNetCore.Builder;

namespace MinimalApis.Discovery
{
  public static class MinimalApisDiscoveryGeneratedExtensions
  {
    public static WebApplication MapApis(this WebApplication app)
    {
      // Call Register on all classes that implement IApi
      new global::APIDemo.PersonAPI().Register(app);
      return app;
    }
  }
}

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/MinimalApis.Discovery

Pattern: IOC

Description

Inversion of Control is a principle in software engineering by which the control of objects or portions of a program is transferred to a container or framework. It’s a design principle in which custom-written portions of a computer program receive the flow of control from a generic framework.

Examples in .NET :

IOC

namespace IOC;
public class NotificationService
{
    private readonly IMessageService _messageService;

    public NotificationService(IMessageService messageService)
    {
        _messageService = messageService;
    }

    public void SendNotification(string message)
    {
        _messageService.SendMessage(message);
    }
}
public interface IMessageService
{
    void SendMessage(string message);
}

DI


namespace IOC;
public class SMSService : IMessageService
{
    public void SendMessage(string message)
    {
        Console.WriteLine("Sending SMS: " + message);
    }
}

public class EmailService : IMessageService
{
    public void SendMessage(string message)
    {
        Console.WriteLine("Sending email: " + message);
    }
}

Learn More

Source Code for Microsoft implementation of IOC

SourceCode ServiceCollection

Learn More

dofactory
DPH

}

Homework

Implement a simple IoC container that will allow you to register and resolve dependencies. The container should be able to resolve dependencies by type and by name.

Pattern: Lazy

Description

Lazy initialization is the tactic of delaying the creation of an object, the calculation of a value, or some other expensive process until the first time it is needed.

Example in .NET :

Lazy

namespace Lazy;
internal class LazyDemo
{
    public DateTime dateTimeConstructClass =DateTime.Now;
    
    public Lazy<DateTime> DateTimeLazy = new(() =>
    {
        Console.WriteLine("Lazy<DateTime> is being initialized ONCE!");
        return DateTime.Now;
    });
}

Learn More

Source Code for Microsoft implementation of Lazy

SourceCode Lazy

Learn More

C2Wiki
Wikipedia

Homework

Implement a lazy initialization of a logger that logs to a file and to a console. The logger should be created only when it is needed.

RSCG – Farskeptic.AutoCompose

RSCG – Farskeptic.AutoCompose
 
 

name Farskeptic.AutoCompose
nuget https://www.nuget.org/packages/Farskeptic.AutoCompose/
link https://github.com/farskeptic/AutoCompose
author farskeptic/jmagel

Generating decorators for classes that implements interfaces.

 

This is how you can use Farskeptic.AutoCompose .

The code that you start with is


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

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

  <ItemGroup>
    <PackageReference Include="Farskeptic.AutoCompose" Version="1.0.1" />
  </ItemGroup>
	<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>
</Project>


The code that you will use is


using Decorator;

ICoffee c = new Coffee();
c = new CoffeeWithLogging(c);
await c.Prepare();
var ingredients = c.GetIngredients();


namespace Decorator;

internal class Coffee : ICoffee
{
    public string? Name { get; set; }
    public async Task<bool> Prepare()
    {
        Console.WriteLine("start prepare coffee");
        await Task.Delay(1000);
        Console.WriteLine("finish prepare coffee");
        return true;
    }
    public string[] GetIngredients() => new[] { "water", "coffee" };

}




namespace Decorator;
internal interface ICoffee
{
    Task<bool> Prepare();

    string[] GetIngredients();
}




using AutoCompose.Generator.Attributes;

namespace Decorator;

[AutoCompose(typeof(ICoffee), nameof(_cof))]
internal partial class CoffeeWithLogging : ICoffee
{
    protected ICoffee _cof;

    public CoffeeWithLogging(ICoffee cof)
    {
        this._cof = cof;
    }
    public string[] GetIngredients()
    {
        Console.WriteLine("CoffeeWithLogging.GetIngredients");
        return this._cof.GetIngredients();
    }
}

 

The code that is generated is

// <auto-generated> 
// WARNING: THIS CODE IS AUTO-GENERATED AT COMPILE-TIME.  ANY CHANGES WILL BE OVERWRITTEN ON NEXT COMPILE.
// </auto-generated> 



namespace Decorator
{
    internal partial class CoffeeWithLogging
    {


        public virtual Task<bool> Prepare()
        {
            return _cof.Prepare();
        }


    }
}

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/Farskeptic.AutoCompose

RSCG – TypeUtilities

RSCG – TypeUtilities
 
 

name TypeUtilities
nuget https://www.nuget.org/packages/TypeUtilities/
link https://github.com/DragonsLord/TypeUtilities
author Yevhenii Serdiuk

Pick/Omit for classes ( also have some mapping )

 

This is how you can use TypeUtilities .

The code that you start with is


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

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
	<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>
	<ItemGroup>
	  <PackageReference Include="TypeUtilities" Version="0.0.1" />
	</ItemGroup>
</Project>


The code that you will use is


using UtilDemo;

var p=new PersonFull();
p.FirstName="Andrei";
p.LastName="Ignat";
//Person1 p1=(Person1)p ;
//Person2 p2=(Person2)p ;
Person1 p1 = new();
p1.FirstName = p.FirstName;

Person2 p2=new();
p2.LastName = p.LastName;


Console.WriteLine(p1.FirstName);
Console.WriteLine(p2.LastName);


using TypeUtilities;
using static TypeUtilities.Abstractions.MemberDeclarationFormats;
using TypeUtilities.Abstractions;

namespace UtilDemo;
public class PersonFull
{
    public string? FirstName { get; set; }
    public string? LastName { get; set; }
    public int Salary { get; set; }

}

[Map(typeof(PersonFull),
      MemberDeclarationFormat = $"{Tokens.Accessibility} string Mapped{Tokens.Name}{Tokens.Accessors}",
      MemberKindSelection = MemberKindFlags.AnyProperty
    )]
[Omit(typeof(PersonFull), nameof(PersonFull.Salary))]
public partial class Person2
{
    
}

[Pick(typeof(PersonFull), nameof(PersonFull.FirstName), nameof(PersonFull.LastName))]
public partial class Person1
{
    
}



 

The code that is generated is

namespace UtilDemo;

public partial class Person1
{
	public string? FirstName { get; set; }
	public string? LastName { get; set; }
}

namespace UtilDemo;

public partial class Person2
{
	public string MappedFirstName { get; set; }
	public string MappedLastName { get; set; }
	public string MappedSalary { get; set; }
}

namespace UtilDemo;

public partial class Person2
{
	public string? FirstName { get; set; }
	public string? LastName { get; set; }
}

Code and pdf at

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

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.