RSCG – ActorSrcGen

 
 

name ActorSrcGen
nuget https://www.nuget.org/packages/ActorSrcGen/
link https://github.com/aabs/ActorSrcGen
author Andrew Matthews

Generating source code for actors in C#.

 

This is how you can use ActorSrcGen .

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="ActorSrcGen" Version="1.1.2" />
    <PackageReference Include="ActorSrcGen.Abstractions" Version="1.1.2" />
    <PackageReference Include="Gridsum.DataflowEx" Version="2.0.0" />
  </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 ActorDemo;
using Gridsum.DataflowEx;

Person person = new Person { Name = "Andrei Ignat" };

DayWorkflow dayAndreiIgnat = new ();
var input = dayAndreiIgnat.InputBlock;
//async
await dayAndreiIgnat.SendAsync(person);
//sync
while (dayAndreiIgnat.Call(person))
{
    await Task.Delay(100);
}

Console.WriteLine("Done");
Console.ReadLine();


using ActorSrcGen;
using System.Diagnostics.Metrics;

namespace ActorDemo;
[Actor]
partial class DayWorkflow
{
    [FirstStep("StartDay")]
    //[Receiver]
    [NextStep(nameof(WashFace))]
    [NextStep(nameof(LogMessage))]
    public async Task<Person> StartDay(Person p)
    {
        await Task.Delay(1000 );
        return p;
    }

    

    [Step]
    [NextStep(nameof(LogMessage))]
    [NextStep(nameof(Eat))]
    public async Task<Person> WashFace(Person p)
    {
        await Task.Delay(1000);
        return p;
    }
    


    [Step]
    [NextStep(nameof(LogMessage))]
    [NextStep(nameof(Sleep))]
    public async Task<Person> Eat(Person p)
    {
        await Task.Delay(1000);
        return p;
    }
    

    [NextStep(nameof(LogMessage))]
    public async Task<int> Sleep(Person p)
    {
        await Task.Delay(1000);
        return p.Name.Length;
    }

    [LastStep]
    public void LogMessage(Person x)
    {
        Console.WriteLine("Incoming Message: " + x?.Name);
    }


}




namespace ActorDemo;
public class Person
{
    public string Name { get; set; }
}


 

The code that is generated is

// Generated on 2024-06-02
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
#pragma warning disable CS0108 // hides inherited member.

using ActorSrcGen;
using System.Diagnostics.Metrics;
namespace ActorDemo;
using System.Threading.Tasks.Dataflow;
using Gridsum.DataflowEx;
public partial class DayWorkflow : Dataflow<Person>, IActor<Person>
{
    public DayWorkflow() : base(DataflowOptions.Default)
    {
        _LogMessage = new ActionBlock<Person>(        (Person x) => {
            try
            {
                LogMessage(x);
            }catch{}
        },
            new ExecutionDataflowBlockOptions() {
                BoundedCapacity = 5,
                MaxDegreeOfParallelism = 8
        });
        RegisterChild(_LogMessage);
        _Eat = new TransformManyBlock<Person,Person>(       async (Person x) => {
           var result = new List<Person>();
           try
           {
               var newValue = await Eat(x);
               result.Add(newValue);
           }catch{}
           return result;
       },
            new ExecutionDataflowBlockOptions() {
                BoundedCapacity = 5,
                MaxDegreeOfParallelism = 8
        });
        RegisterChild(_Eat);
        _EatBC = new BroadcastBlock<Person>(    (Person x) => x,
            new ExecutionDataflowBlockOptions() {
                BoundedCapacity = 5,
                MaxDegreeOfParallelism = 8
        });
        RegisterChild(_EatBC);
        _WashFace = new TransformManyBlock<Person,Person>(       async (Person x) => {
           var result = new List<Person>();
           try
           {
               var newValue = await WashFace(x);
               result.Add(newValue);
           }catch{}
           return result;
       },
            new ExecutionDataflowBlockOptions() {
                BoundedCapacity = 5,
                MaxDegreeOfParallelism = 8
        });
        RegisterChild(_WashFace);
        _WashFaceBC = new BroadcastBlock<Person>(    (Person x) => x,
            new ExecutionDataflowBlockOptions() {
                BoundedCapacity = 5,
                MaxDegreeOfParallelism = 8
        });
        RegisterChild(_WashFaceBC);
        _StartDay = new TransformManyBlock<Person,Person>(       async (Person x) => {
           var result = new List<Person>();
           try
           {
               var newValue = await StartDay(x);
               result.Add(newValue);
           }catch{}
           return result;
       },
            new ExecutionDataflowBlockOptions() {
                BoundedCapacity = 5,
                MaxDegreeOfParallelism = 8
        });
        RegisterChild(_StartDay);
        _StartDayBC = new BroadcastBlock<Person>(    (Person x) => x,
            new ExecutionDataflowBlockOptions() {
                BoundedCapacity = 5,
                MaxDegreeOfParallelism = 8
        });
        RegisterChild(_StartDayBC);
        _Eat.LinkTo(_EatBC, new DataflowLinkOptions { PropagateCompletion = true });
        _EatBC.LinkTo(_LogMessage, new DataflowLinkOptions { PropagateCompletion = true });
        _WashFace.LinkTo(_WashFaceBC, new DataflowLinkOptions { PropagateCompletion = true });
        _WashFaceBC.LinkTo(_LogMessage, new DataflowLinkOptions { PropagateCompletion = true });
        _WashFaceBC.LinkTo(_Eat, new DataflowLinkOptions { PropagateCompletion = true });
        _StartDay.LinkTo(_StartDayBC, new DataflowLinkOptions { PropagateCompletion = true });
        _StartDayBC.LinkTo(_LogMessage, new DataflowLinkOptions { PropagateCompletion = true });
        _StartDayBC.LinkTo(_WashFace, new DataflowLinkOptions { PropagateCompletion = true });
    }

    ActionBlock<Person> _LogMessage;

    TransformManyBlock<Person,Person> _Eat;

    BroadcastBlock<Person> _EatBC;

    TransformManyBlock<Person,Person> _WashFace;

    BroadcastBlock<Person> _WashFaceBC;

    TransformManyBlock<Person,Person> _StartDay;

    BroadcastBlock<Person> _StartDayBC;
    public override ITargetBlock<Person> InputBlock { get => _StartDay; }
    public bool Call(Person input)
        => InputBlock.Post(input);

    public async Task<bool> Cast(Person input)
        => await InputBlock.SendAsync(input);
}

Code and pdf at

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

[ADCES] Generative AI for .NET Dev & open-source LLMs using Semantic Kernel and LLaVA

Presentation 1 : Generative AI for .NET Dev
Description: TBD
Presenter : Andrei Ignat, http://msprogrammer.serviciipeweb.ro/

Presentation 2 : Deep dive into open-source LLMs using Semantic Kernel and LLaVA
Description: LLaVA, the Large Language and Vision Assistant, is a multimodal model that combines vision and language understanding. It can reason over images, aiding in decision-making based on the environment, thereby enhancing autonomy.

Let’s see how we can combine this promising model with the power of an AI orchestrator like Microsoft Semantic Kernel.

Presenter : Daniel Costea, https://www.linkedin.com/in/danielcostea

https://www.meetup.com/bucharest-a-d-c-e-s-meetup/events/300509200/

Pattern: AbstractFactory

Description

Abstract Factory is a creational design pattern that lets you produce families of related objects without specifying their concrete classes.

Example in .NET :

AbstractFactory


using Microsoft.Data.SqlClient;
using System.Data.Common;

namespace AbstractFactory;
internal class AbstractFactoryDemo
{
    public static void Demo()
    {
        //create DbConnection factory by using the instance of SqlConnection
        DbConnection connection = new SqlConnection();
        //create DbCommand instance by using the instance of SqlConnection
        DbCommand command = connection.CreateCommand();
        //really, the DBCommand is a SqlCommand
        SqlCommand? sqlCommand = command as SqlCommand;
        //check if the DbCommand is a SqlCommand
        Console.WriteLine($"DbCommand is SqlCommand: {sqlCommand != null}");
    }
}

Learn More

Source Code for Microsoft implementation of AbstractFactory

SourceCode DbConnection

Learn More

Wikipedia

}

Homework

Imagine you want to produce loggers. You have a logger that logs to a file and a logger that logs to a console and a Nothing Logger – a logger that does nothing. Implement an abstract factory that will allow you to create a logger factory that will create a logger that logs to a file or to a console or nothing.

Pattern: Flyweight

Description

Flyweight pattern is used to reduce the memory and resource usage for complex models containing a large number of similar objects.

Example in .NET :

Flyweight

using System.Text;

namespace Flyweight;
internal class FlyweightDemo
{
    public static void Demo()
    {
        var str = "Andrei Ignat";
        var str2 = string.Intern(str);
        var str3 = new StringBuilder("Andrei").Append(" Ignat").ToString();
        Console.WriteLine($"str == str2: Value {str==str2} Reference {Object.ReferenceEquals(str,str2)}");
        Console.WriteLine($"str == str3: Value {str==str3} Reference {Object.ReferenceEquals(str,str3)}");

    }
}

Learn More

Source Code for Microsoft implementation of Flyweight

SourceCode String.Intern

Learn More

Wikipedia

}

Homework

Make an exchange rate system. The symbol and names of the currency are the same for all the currencies. The exchange rate is different for each currency. Implement a flyweight that will allow you to create a currency with a symbol and a name and to get the exchange rate for the currency.

Pattern: Observer

Description

Observer pattern is a behavioral design pattern that defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

Example in .NET :

Observer


using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace Observer;

/// <summary>
/// INotifyPropertyChanged is an interface that provides a mechanism for the object to notify clients that a property value has changed.
/// </summary>
public class Person: INotifyPropertyChanged
{
    private string name=string.Empty;
    public string Name
    {
        get => name;
        set
        {
            if (name == value) return;
            name = value;
            OnPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler? PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
internal class ObserverDemo
{
    public static void Demo()
    {
        Person person = new ();
        //subscribe to the event to observe the changes
        person.PropertyChanged += (sender, args) =>
        {
            var p = sender as Person;
            Console.WriteLine($"Property {args.PropertyName} changed to {p?.Name}");
        };
        person.Name = "Andrei Ignat" ;
    }
}

Learn More

Source Code for Microsoft implementation of Observer

SourceCode INotifyPropertyChanged

Learn More

Wikipedia

}

Homework

Imagine you have a logger that logs to a file and to a console. Implement an observable logger that will allow you to subscribe to the logger and to be notified when the logger logs a message.

RSCG – MSTest

name MSTest
nuget https://www.nuget.org/packages/MSTest.SourceGeneration/
link https://github.com/microsoft/testfx
author Microsoft

AOP for MSTest

 

This is how you can use MSTest .

The code that you start with is


<!-- file: UnitTestProject1.csproj -->
<Project Sdk="Microsoft.NET.Sdk">

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

		<OutputType>exe</OutputType>
		<PublishAot>true</PublishAot>
	</PropertyGroup>

	<ItemGroup>
		<!-- 
      Experimental MSTest Engine & source generator, 
      close sourced, licensed the same as our extensions 
      with Microsoft Testing Platform Tools license.
    -->
		<PackageReference Include="MSTest.Engine" Version="1.0.0-alpha.24163.4" />
		<PackageReference Include="MSTest.SourceGeneration" Version="1.0.0-alpha.24163.4" />

		<PackageReference Include="Microsoft.CodeCoverage.MSBuild" Version="17.10.4" />
		<PackageReference Include="Microsoft.Testing.Extensions.CodeCoverage" Version="17.10.4" />

		<PackageReference Include="Microsoft.Testing.Extensions.TrxReport" Version="1.0.2" />
		<PackageReference Include="Microsoft.Testing.Platform.MSBuild" Version="1.0.2" />
		<PackageReference Include="MSTest.TestFramework" Version="3.2.2" />
		<PackageReference Include="MSTest.Analyzers" Version="3.2.2" />

	</ItemGroup>

	<ItemGroup>
		<ProjectReference Include="..\MyImportantClass\MyImportantClass.csproj" />
	</ItemGroup>

	<ItemGroup>
		<Using Include="Microsoft.VisualStudio.TestTools.UnitTesting" />
	</ItemGroup>

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

</Project>

The code that you will use is


// file: UnitTest1.cs
using MyImportantClass;

namespace DemoTest;

[TestClass]
public class UnitTest1
{
    [TestMethod]
    public void TestMethod1()
    {
        Assert.AreEqual(3, new Class1().Add(1, 2));
    }

    [TestMethod]
    [DataRow(1, 2)]
    [DataRow(100, -97)]
    public void TestMethod2(int left, int right)
    {
        Assert.AreEqual(3, new Class1().Add(left, right));
    }
}

 

The code that is generated is

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by Microsoft Testing Framework Generator.
// </auto-generated>
//------------------------------------------------------------------------------

namespace DemoTest
{
    using Threading = global::System.Threading;
    using ColGen = global::System.Collections.Generic;
    using CA = global::System.Diagnostics.CodeAnalysis;
    using Sys = global::System;
    using Msg = global::Microsoft.Testing.Platform.Extensions.Messages;
    using MSTF = global::Microsoft.Testing.Framework;

    [CA::ExcludeFromCodeCoverage]
    public static class DemoTest_UnitTest1
    {
        public static readonly MSTF::TestNode TestNode = new MSTF::TestNode
        {
            StableUid = "DemoTest.DemoTest.UnitTest1",
            DisplayName = "UnitTest1",
            Properties = new Msg::IProperty[1]
            {
                new Msg::TestFileLocationProperty(@"D:\gth\RSCG_Examples\v2\rscg_examples\MSTest\src\DemoTest\UnitTest1.cs", new(new(6, -1), new(22, -1))),
            },
            Tests = new MSTF::TestNode[]
            {
                new MSTF::InternalUnsafeActionTestNode
                {
                    StableUid = "DemoTest.DemoTest.UnitTest1.TestMethod1()",
                    DisplayName = "TestMethod1",
                    Properties = new Msg::IProperty[2]
                    {
                        new Msg::TestMethodIdentifierProperty(
                            "DemoTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
                            "DemoTest",
                            "UnitTest1",
                            "TestMethod1",
                            Sys::Array.Empty<string>(),
                            "System.Void"),
                        new Msg::TestFileLocationProperty(@"D:\gth\RSCG_Examples\v2\rscg_examples\MSTest\src\DemoTest\UnitTest1.cs", new(new(9, -1), new(13, -1))),
                    },
                    Body = static testExecutionContext =>
                    {
                        var instance = new UnitTest1();
                        try
                        {
                            instance.TestMethod1();
                        }
                        catch (global::System.Exception ex)
                        {
                            testExecutionContext.ReportException(ex, null);
                        }
                    },
                },
                new MSTF::InternalUnsafeActionParameterizedTestNode<MSTF::InternalUnsafeTestArgumentsEntry<(int left, int right)>>
                {
                    StableUid = "DemoTest.DemoTest.UnitTest1.TestMethod2(int, int)",
                    DisplayName = "TestMethod2",
                    Properties = new Msg::IProperty[2]
                    {
                        new Msg::TestMethodIdentifierProperty(
                            "DemoTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
                            "DemoTest",
                            "UnitTest1",
                            "TestMethod2",
                            new string[2]
                            {
                                "System.Int32",
                                "System.Int32",
                            },
                            "System.Void"),
                        new Msg::TestFileLocationProperty(@"D:\gth\RSCG_Examples\v2\rscg_examples\MSTest\src\DemoTest\UnitTest1.cs", new(new(15, -1), new(21, -1))),
                    },
                    GetArguments = static () => new MSTF::InternalUnsafeTestArgumentsEntry<(int left, int right)>[]
                    {
                        new MSTF::InternalUnsafeTestArgumentsEntry<(int left, int right)>((1, 2), "left: 1, right: 2"),
                        new MSTF::InternalUnsafeTestArgumentsEntry<(int left, int right)>((100, -97), "left: 100, right: -97"),
                    },
                    Body = static (testExecutionContext, data) =>
                    {
                        var instance = new UnitTest1();
                        try
                        {
                            instance.TestMethod2(data.Arguments.left, data.Arguments.right);
                        }
                        catch (global::System.Exception ex)
                        {
                            testExecutionContext.ReportException(ex, null);
                        }
                    },
                },
            },
        };
    }
}


namespace Microsoft.Testing.Framework.SourceGeneration
{
    public static class SourceGeneratedTestingPlatformBuilderHook
    {
        public static void AddExtensions(Microsoft.Testing.Platform.Builder.ITestApplicationBuilder testApplicationBuilder, string[] _)
        {
            testApplicationBuilder.AddTestFramework(new Microsoft.Testing.Framework.Configurations.TestFrameworkConfiguration(System.Environment.ProcessorCount),
                new DemoTest.SourceGeneratedTestNodesBuilder());
        }
    }
}
//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by Microsoft Testing Framework Generator.
// </auto-generated>
//------------------------------------------------------------------------------

namespace DemoTest
{
    using DemoTest;
    using ColGen = global::System.Collections.Generic;
    using CA = global::System.Diagnostics.CodeAnalysis;
    using Sys = global::System;
    using Tasks = global::System.Threading.Tasks;
    using Msg = global::Microsoft.Testing.Platform.Extensions.Messages;
    using MSTF = global::Microsoft.Testing.Framework;
    using Cap = global::Microsoft.Testing.Platform.Capabilities.TestFramework;
    using TrxReport = global::Microsoft.Testing.Extensions.TrxReport.Abstractions;

    [CA::ExcludeFromCodeCoverage]
    public sealed class SourceGeneratedTestNodesBuilder : MSTF::ITestNodesBuilder
    {
        private sealed class ClassCapabilities : TrxReport::ITrxReportCapability
        {
            bool TrxReport::ITrxReportCapability.IsSupported { get; } = true;
            void TrxReport::ITrxReportCapability.Enable() {}
        }

        public ColGen::IReadOnlyCollection<Cap::ITestFrameworkCapability> Capabilities { get; } = new Cap::ITestFrameworkCapability[1] { new ClassCapabilities() };

        public Tasks::Task<MSTF::TestNode[]> BuildAsync(MSTF::ITestSessionContext testSessionContext)
        {
            ColGen::List<MSTF::TestNode> namespace1Tests = new();
            namespace1Tests.Add(DemoTest_UnitTest1.TestNode);

            MSTF::TestNode root = new MSTF::TestNode
            {
                StableUid = "DemoTest",
                DisplayName = "DemoTest",
                Properties = Sys::Array.Empty<Msg::IProperty>(),
                Tests = new MSTF::TestNode[]
                {
                    new MSTF::TestNode
                    {
                        StableUid = "DemoTest.DemoTest",
                        DisplayName = "DemoTest",
                        Properties = Sys::Array.Empty<Msg::IProperty>(),
                        Tests = namespace1Tests.ToArray(),
                    },
                },
            };

            return Tasks::Task.FromResult(new MSTF::TestNode[1] { root });
        }
    }
}

Code and pdf at

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

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.