Pattern: Iterator

Description

Iterator design pattern allows to traverse a container and access the container’s elements.

Example in .NET :

DirectoryEnumerable

namespace Iterator;
internal class DirectoryEnumerableDemo
{
    public static void DirectoryEnumerableFiles(int nrMaxFiles)
    {
        var count = 0;
        //what if we called Directory.GetFiles        
        foreach (var file in Directory.EnumerateFiles(@"c:\","*.*",SearchOption.AllDirectories))
        {
            count++;
            if (count > nrMaxFiles)
                break;
            Console.WriteLine(file);
        }
    }
}

Learn More

Wikipedia

Homework

With the Yield keyword implement a function that return an IEnumerable of generic int that will return the first 10 numbers of the Fibonacci sequence

Pattern: NullObject

Description

Instead of returning null , use an object which implements the expected interface, but whose method body is empty.

Examples in .NET :

EmptyFolder

using System;
using System.IO;

namespace NullObject;
internal class EmptyFolderDemo
{
    public static void DemoWithCreateNewFolder()
    {
        //start example 1
        var env = Environment.CurrentDirectory;
        var guid = Guid.NewGuid().ToString("X");
        var fldEmpty = Path.Combine(env, guid);
        //create empty folder
        Directory.CreateDirectory(fldEmpty);
        var files= Directory.GetFiles(fldEmpty);
        Console.WriteLine($"files.Length:{files.Length}");
        //end example 1
        
    }
}

NullLogger


namespace NullObject;
internal class LogWithData
{
    ILogger _logger;
    public LogWithData(ILogger? logger=null)
    {
        _logger = logger ?? NullLogger.Instance;   
    }
    public void DoWork(string message)
    {
        _logger.LogInformation($"start work with {message}");
    }
}

Learn More

Wikipedia

Homework

When retrieving data( e.g. a Person with ID =-1 ) from a database , return a NullObject instead of null. How you will verify that the object is a NullObject?

Comparing EFCore Database Providers-part-2

IDName
1 Part 1
2 Part 2
3 Part 3

I have started with a simple table  –  Department( id autogenerated , name) and generate EFCore context and classes with scaffolding templates https://learn.microsoft.com/en-us/ef/core/managing-schemas/scaffolding/templates?tabs=dotnet-core-cli .

Then I have created a simple text ( XUnit + LightBDD ) in order to test

–  generateDatabase from classes

– CRUD

– some simple search .

Works perfectly, so to the next – more different tables ( datetime, binary, money and so on)  => scaffold code => make tests.

the first problem – column type

When generating the scaffolding the OnModelCreating generates .HasColumnType – for example,

modelBuilder.Entity<Tbl_DATETIME>(entity =>
{
    entity.HasKey(e => e.ID).HasName("PK__Tbl_DATE__3214EC27B76F5021");

    entity.Property(e => e.DataColumn).HasColumnType("datetime");
});

And PostgresSql has no type that is called “datetime”, but “timestamp” – https://www.postgresql.org/docs/current/datatype-datetime.html

So what I do is to have OnModelCreatingPartial that is called AFTER all set and re-write

modelBuilder.Entity<Tbl_DATETIME>(entity =>
{
    entity.HasKey(e => e.ID).HasName("PK__Tbl_DATE__3214EC27B76F5021");

    entity.Property(e => e.DataColumn).HasColumnType(null);
});

Even if column type is null, the CreateDatabase knows that the field is DateTime


public partial class Tbl_DATETIME
{
    public int ID { get; set; }

    public DateTime? DataColumn { get; set; }
}

so all is ok.

Second Problem – datetime in UTC

The DateTime that is on the class generates a column that supports ONLY UTC kind in datetimes . The error is

–> System.InvalidCastException : Cannot write DateTime with Kind=Local to PostgreSQL type ‘timestamp with time zone’, only UTC is supported. Note that it’s not possible to mix DateTimes with different Kinds in an array/range. See the Npgsql.EnableLegacyTimestampBehavior AppContext switch to enable legacy behavior.

Youn

Makes sense, usual in database you want just the UTC and transform for each user

Third Problem – datetime have a millisecond difference sometimes

This is the test:

var dataM = new Tbl_DATETIME();
dataM.ID = 3;
dataM.DataColumn = DateTime.Now.AddDays(100);
using (var db = await startDatabase.GetNewContext<SimpleTablesMultipleData>())
{
    await db.Tbl_DATETIMEModify(dataM);
}
using (var db = await startDatabase.GetNewContext<SimpleTablesMultipleData>())
{
    var verify = await db.Tbl_DATETIMEGetSingle(dataM.ID);
    verify.Should().NotBeNull();
    verify!.DataColumn!.Should().NotBeNull();
    verify!.DataColumn.Should().Be(dataM.DataColumn);

}

And the error is

Xunit.Sdk.XunitException : Expected verify!.DataColumn to be <2023-12-13 21:45:39.6089917>, but found <2023-12-13 21:45:39.608991>.

You can see the Ok tests at https://ignatandrei.github.io/TestEFCoreDatabaseProviders/

And the problems at https://ignatandrei.github.io/TestEFCoreDatabaseProviders/problems.html

Introduction in Roslyn Code Generators & Building Your Own Search Assistant

Azi la 19:30 avem o noua intilnire ADCES

Presentation 1 : Introduction in Roslyn Code Generators
Description:
Presenter : Ignat Andrei, http://msprogrammer.serviciipeweb.ro/
Presentation 2: # Building Your Own Search Assistant: A Hands-On Guide to Internet-Connected AI Tools
Description:
Ever marveled at Microsoft’s Copilot knack for discussing up-to-the-minute topics, despite being trained on data up to only September 2021? Curious about the magic that goes on under the hood?
Join Vlad as he peels back the curtain on the innovative techniques used to create a search assistant that can converse about current events. This interactive session will guide you through the entire process, exploring methods like fine-tuning, retrieval augmented generation, and tackling challenges such as slow model inference and context window limitations.
Expect a live demonstration of a simple yet effective application and walk away with the knowledge to craft your very own assistant using just Python, pandas, and the Azure OpenAI APIs. Don’t miss the chance to move from concept to code in this practical deep dive!

Presenter: Vlad Iliescu, https://vladiliescu.net/

Va astept aici: https://www.meetup.com/bucharest-a-d-c-e-s-meetup/events/298422420/

Comparing EFCore Database Providers-part-1

IDName
1 Part 1
2 Part 2
3 Part 3

I wanted to see if there are any differences in EFCore database providers listed at 

https://learn.microsoft.com/en-us/ef/core/providers/?tabs=dotnet-core-cli

I want to test the capabilities for each one within a standard choice of tables , in order to know the capabilities

I choose only those that have a version for current STS / LTS , whatever it is current.

( I am particularly interested in SqlServer vs Sqlite )

Problem 1: Conflicting namespaces

For MySql – there are 2 providers , Pomelo.EntityFrameworkCore.MySql  and MySql.EntityFrameworkCore  . Both have the same namespace and class for .UseMySql  ( and other stuff)

So how to do it ? Fortunately, nuget supports alias .

So the code in csproj is

<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="7.0.0" Aliases="PomeloEFMySql"  />
<PackageReference Include="MySqlConnector" Version="2.2.5" Aliases="MySqlConnect" />

<PackageReference Include="MySql.EntityFrameworkCore" Version="7.0.5" Aliases="MySqlEFOracle" />
<PackageReference Include="MySql.Data" Version="8.1.0" Aliases="OracleMySql"/>
		

And the code in global.cs

extern alias OracleMySql;

extern alias PomeloEFMySql;
extern alias MySqlConnect;

global using MySqlCNBOracle = MySqlEFOracle.Microsoft.EntityFrameworkCore.MySQLDbContextOptionsExtensions;
global using MySqlOracle = OracleMySql.MySql.Data.MySqlClient;
global using MySqlEF = MySqlEFOracle::Microsoft.EntityFrameworkCore;
 
global using PomeloCN= MySqlConnect::MySqlConnector;
global using PomeloEF = PomeloEFMySql::Microsoft.EntityFrameworkCore;
global using PomeloMySqlCNB =PomeloEFMySql::Microsoft.EntityFrameworkCore.MySqlDbContextOptionsBuilderExtensions;

And the code to use it

case EFCoreProvider.Pomelo_EntityFrameworkCore_MySql:

    var serverVersion = PomeloEF.ServerVersion.AutoDetect(con);
    StepExecution.Current.Comment("version " + serverVersion.ToString());
    PomeloMySqlCNB.UseMySql(builder,con, serverVersion)
        .EnableSensitiveDataLogging()
        .EnableDetailedErrors();
    
    break;
case EFCoreProvider.MySql_EntityFrameworkCore:
    MySqlCNBOracle.UseMySQL(builder,con);
    break;

You can find the results at https://github.com/ignatandrei/TestEFCoreDatabaseProviders and https://ignatandrei.github.io/TestEFCoreDatabaseProviders/

Problem 2 : Conflict on container ports

When a container is started for a test it works on a port ( 1433 for SqlServer). When a new test arrives ( with new tables ) , it cannot be on the same port . So the docker containers must be disposed when the test finishes. Also , the tests must be done in serial, not in paralel.

For parallelism, it is simple ( LightBDD + XUnit)

[assembly: CollectionBehavior(DisableTestParallelization = true)]
[assembly: ClassCollectionBehavior(AllowTestParallelization = false)]

For disposing, can use IScenarioTearDown (LightBDD) or IAsyncLifetime (XUnit)

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

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.