Chrome Live Caption Became Slower

Have you found Chrome Live Caption become slower after you updated to new version of Google Chrome?

There may be a trick to resolve this issue. Go to Settings > Search for “Caption” > Click the Right Triangle and then remove the language from your caption to turn Live Caption off. Then, restart your Chrome and enable the Live Caption again. Chrome will re-download the file for the Live Caption.

Try to play some audios or videos, this might solve the Live Caption slow issue.

Run a Python Script in a Virtual Environment Using .NET

How to run a python script inside a venv using .NET? First, let us create a new python venv in Windows.

python -m venv dotnet-venv

Then, we activate the virtual environment to do some testing.

# Use this command for command prompt
C:\> <venv>\Scripts\activate.bat
# Use this command for Powershell
PS C:\> <venv>\Scripts\Activate.ps1

After activated, you will able to see venv name on the left-hand side before the current path e.g. (dotnet-venv) PS C:\PythonProjects\dotnet-venv>.

We will install some packages in the venv, so the virtual environment has more packages than the system.

pip install pandas
pip install pyarrow

Write a python test code such as below and save it as py-dotnet-test.py:

# Import pandas
import pandas as pd

# Create a DataFrame with some data
df = pd.DataFrame({
    "name": ["Alice", "Bob", "Charlie"],
    "age": [25, 30, 35],
    "gender": ["F", "M", "M"]
})

# Write the DataFrame to a CSV file
df.to_csv("result.csv", sep=" ", encoding="utf-8", header=False, index=False)

Try executing it on venv, you should be able to see the result.csv on your directory.

To execute the python script in venv using .NET, just use the code below and point the execution path to venv python.exe.

string venvDir = @"C:\<venv>\dotnet-venv\";
ProcessStartInfo start = new ProcessStartInfo();
// Please note the python.exe must be the one inside venv, else you will get error such as ModuleNotFoundError: No module named 'pandas'
start.FileName = venvDir + @"Scripts\python.exe";
start.Arguments = venvDir + @"py-dotnet-test.py"; // Script parameters can be put at the end.
start.WorkingDirectory = venvDir;
// You can temp comment out the two lines below to see the running progress
start.WindowStyle = ProcessWindowStyle.Hidden;
start.CreateNoWindow = true;

using (Process process = Process.Start(start))
{
	process.WaitForExit();
}

After you run the dotnet code, you should be able to see the result.csv in your directory :).

Specify Targeted .NET Version for dotnet new Command and then dotnet Command Version

It is quite common for our development machine to install multiple versions of .NET SDK. By default, if we run dotnet new command from .NET CLI to create a new project, it will be creating a .NET project targeted to latest version regardless is it in preview or release candidate (RC). In order to specify the targeted .NET version, we can use the -f net7.0 option. The help message is like below:

$ dotnet new webapi --help

ASP.NET Core Web API (C#)
Author: Microsoft
Description: A project template for creating a RESTful Web API using ASP.NET Core controllers or minimal APIs, with optional support for OpenAPI and authentication.
# Omitting
  -f, --framework <net6.0|net7.0|net8.0|netcoreapp3.1>  The target framework for the project.
                                                        Type: choice
                                                          net8.0         Target net8.0
                                                          net7.0         Target net7.0
                                                          net6.0         Target net6.0
                                                          netcoreapp3.1  Target netcoreapp3.1
# Omitting

For example, the command below will create a new webapi project targeted .NET 7.0.

dotnet new webapi -o net7.0

Besides that, if you want your .NET CLI command to default to the version you want to use, you need to specify it too. The -f just for specify the targeted .NET project version. To do so, first you need to figure .NET version installed on your machine:

$ dotnet --list-sdks
3.1.416 [/home/ubuntu/dotnet/sdk]
6.0.400 [/home/ubuntu/dotnet/sdk]
7.0.202 [/home/ubuntu/dotnet/sdk]
8.0.100-rc.1.23455.8 [/home/ubuntu/dotnet/sdk]

If you want to use 7.0.202 for the dotnet command on the project folder, you can run the command below:

$  dotnet new globaljson --sdk-version 7.0.202
The template "global.json file" was created successfully.

The command above will create a global.json file inside the project folder. All directories including sub-directory will default to the version you specify.

{
  "sdk": {
    "version": "7.0.202"
  }
}

StackExchange.Redis.RedisTimeoutException: Timeout awaiting response

Are you getting the RedisTimeoutException like below? But you have tried to take a look at the article suggested from the error message https://stackexchange.github.io/StackExchange.Redis/Timeouts but still unable to solve it?

fail: Microsoft.AspNetCore.Server.Kestrel[13]
      Connection id "xxxxxx", Request id "yyyyy:00000002": An unhandled exception was thrown by the application.
      StackExchange.Redis.RedisTimeoutException: Timeout awaiting response (outbound=0KiB, inbound=0KiB, 5283ms elapsed, timeout is 5000ms), command=HMGET, active: HMGET REDIS_ID, next: HMGET REDIS_ID, inst: 0, qu: 0, qs: 9, aw: False, rs: ComputeResult, ws: Idle, in: 0, in-pipe: 2037, out-pipe: 490, serverEndpoint: 192.168.1.107:6379, mc: 1/1/0, mgr: 8 of 10 available, clientName: zzzzz, IOCP: (Busy=0,Free=1000,Min=16,Max=1000), WORKER: (Busy=18,Free=32749,Min=16,Max=32767), v: 2.2.4.27433 (Please take a look at this article for some common client-side issues that can cause timeouts: https://stackexchange.github.io/StackExchange.Redis/Timeouts)
         at Microsoft.Extensions.Caching.StackExchangeRedis.RedisExtensions.HashMemberGetAsync(IDatabase cache, String key, String[] members)
         at Microsoft.Extensions.Caching.StackExchangeRedis.RedisCache.GetAndRefreshAsync(String key, Boolean getData, CancellationToken token)
         at Microsoft.Extensions.Caching.StackExchangeRedis.RedisCache.GetAsync(String key, CancellationToken token)
         at Microsoft.Extensions.Caching.Distributed.DistributedCacheExtensions.GetStringAsync(IDistributedCache cache, String key, CancellationToken token)

If this is your scenario and you are using docker container for your app and Redis, you can try check either feasible for your architecture to configure as your app connect to Redis using same docker network internally directly instead of expose Redis port to the docker host and then connect your app to Redis port via docker host. It might be able to resolve your RedisTimeoutException issue.

The Hidden Danger of Using Large Numbers in JavaScript – ASP.NET Core Json Numeric lost precision

Are you ever aware that if you have a large Number data type value in JavaScript, the number precession will be lost after a certain point.

I first became aware of this after entering an exceptionally large value number on C# ASP.NET Core. Below is a sample example.

var builder = WebApplication.CreateBuilder();

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddCors(s =>
{
	s.AddPolicy("AllowAll", s => s.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod());
});

var app = builder.Build();

app.Urls.Add("http://*:5001");
app.UseSwagger();
app.UseSwaggerUI();
app.UseCors("AllowAll");

app.MapGet("/", () => "Please go to /swagger for Swagger UI.").WithName("Hello");

var number = 12345678901234567890;
app.MapGet("/largenumber", () => new { Number = number, StringMessage = number.ToString() });

app.Run();

Below is the result of the swagger response, as you can see the number “12345678901234567890” will become 12345678901234567000.

Further investigation, you will find it was due to a limitation on JavaScript number. If you try to type in the value in Chrome Browser console, you will already be able to simulate the issue.

Therefore, the lost precision is a limitation on JavaScript instead of ASP.NET Core problem. If refer to the MDN documentation, it does mention clearly it can only safely store integers in the range -(2^53 − 1) (Number.MIN_SAFE_INTEGER) to 2^53 − 1 (Number.MAX_SAFE_INTEGER).

Reference:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#number_type
https://jsoneditoronline.org/indepth/parse/why-does-json-parse-corrupt-large-numbers/
https://softwareengineering.stackexchange.com/questions/353131/best-practice-for-potentially-large-json-numeric-vaues

.NET Architecture Weekly #6

Introduction

.NET Architecture Weekly is a journal about learning software architecture on .NET and not just limited* to .NET only.
PS: Why? Because software architecture designs normally can use in any languages or frameworks :).

Week #6

1) Microservices Architecture Design Patterns – Part 4

XII) Asynchronous Messaging Pattern

If we use a pattern such as Chain of Responsibility, the process tends to be blocked after we send a request since we need to wait for response. This issue can be solved using the Asynchronous Messaging Pattern since it makes a request to Event Bus or Queue that does not wait for an immediate response.

---
title: Asynchronous Messaging Pattern
---
graph LR
    UP[User Profile Service] --> |User Update Event| EB[(Event Bus / Queue)]
    EB --> |User Updated Event| B[Basket Service]
    EB --> |User Updated Event| O[Ordering Service]

    style UP fill:#f9d0c4 
    style EB fill:#f0b37e 
    style B fill:#a9dc76 
    style O fill:#a9dc76 

XIII) Shared Database Anti-Pattern

Shared Database Pattern in microservices architecture is considered an anti-pattern since microservices get intertangled and both will depend on a single database. But it does have some advantages if you need to have a better transaction control.

---
title: Shared Database Anti-Pattern
---
graph LR
    UI[Browser] -->|HTTP request| GW[API Gateway]
    GW -->|REST call| US[User Service]
    GW -->|REST call| TS[Task Service]
    GW -->|REST call| NS[Notification Service]
    US -->|DB call| DB[(Database)]
    TS -->|DB call| DB
    NS -->|DB call| DB

    style US fill:#f9d0c4 
    style TS fill:#f0b37e 
    style NS fill:#a9dc76

XIV) Decomposition Pattern

Decomposition pattern just like the name, it can use to decompose a monolith application to microservices such as by business capability, subdomain, or bounded context.

---
title: Decomposition Pattern
---
graph LR
    subgraph Monolith Todo App
        UM[User Module]
        TM[Task Module]
        NM[Notification Module]
    end

    subgraph Todo Microservices
        UM --> US[User Service]
        TM --> TS[Task Service]
        NM --> NS[Notification Service]
    end

    style US fill:#f9d0c4 
    style TS fill:#f0b37e 
    style NS fill:#a9dc76

These three patterns for this week mark the end of microservices notes. Next time we will try to explore other things on .NET architectures :).

Reference:
https://learn.microsoft.com/en-us/dotnet/architecture/microservices/architect-microservice-container-applications/asynchronous-message-based-communication

Microservices Decomposition Design Patterns

How to read values from appsettings.json in C# .NET Core 6/7/8?

How to read values from appsettings.json in C# .NET 6/7/8 for a console app or you need to read it inside asp.net core which does not have dependency injection such as Entity Framework Core DBContext there? Either console app or asp.net core DBContext you can read it using Microsoft.Extensions.Configuration.

First, if you don’t have an existing app, you can create a new console application using this command > dotnet new console.

Then, you need to add two nuget packages as below:

dotnet add package Microsoft.Extensions.Configuration
dotnet add package Microsoft.Extensions.Configuration.Json

Add an appsettings.json to read the value from:

{
    "AppSettings": {
      "Title": "My Console App",
      "Version": "1.0.0"
    },
    "ConnectionStrings": {
        "MySql": "server=localhost; database=dotnet; user=test; password=test",
        "SqlServer": "Data Source=localhost; Initial Catalog=dotnet; User Id=test; Password=test"
    }
}

To copy appsettings.json to debug/release folder, edit the .csproj and add the line below to ItemGroup:

    <None Update="appsettings.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>

Inside Program.cs, add the code below. Please note, it has few ways to read the values, the example below demonstrates three ways only.

using Microsoft.Extensions.Configuration;

var configuration = new ConfigurationBuilder()
    .SetBasePath(AppDomain.CurrentDomain.BaseDirectory)
    .AddJsonFile("appsettings.json")
    .Build();

// Method 1: Get directly
Console.WriteLine(configuration["AppSettings:Title"]);

// Method 2: Get via GetSection method.
var appSettings = configuration.GetSection("AppSettings");
Console.WriteLine(appSettings["Version"]);

// Method 3: Get via GetConnection method for connection string
var mysqlConnString = configuration.GetConnectionString("MySql");
Console.WriteLine($"MySQL Connection String: {mysqlConnString}");

Lastly, run dotnet run, the result will be like below:

$ dotnet run
My Console App
1.0.0
MySQL Connection String: server=localhost; database=dotnet; user=test; password=test

Github repo:
https://github.com/sanme98/dotnet-read-appsettings

.NET Architecture Weekly #5

Introduction

.NET Architecture Weekly is a journal about learning software architecture on .NET and not just limited* to .NET only.
PS: Why? Because software architecture designs normally can use in any languages or frameworks :).

Week #5

1) Microservices Architecture Design Patterns – Part 3

XI) Retry Pattern

Retry pattern is a common pattern for recovering from transient errors such as errors that occur for a short period of time. The retry can be implemented in different ways such as Fixed delay, Incremental delay, Exponential Backoff and etc.

---
title: Retry Pattern
---
flowchart LR
    task(Task Service) --x|Request 1| notif(Notification Service)
    task --x |Fixed Delay - Request 2| notif
    task --x |Increment Delay - Request 3| notif
    task --x |Exponential Backoff - Request 4| notif
    notif --> |Response or give up| task

    style task fill:#0f0
    style notif fill:#ff0
    linkStyle 0,1,2,3 stroke:#f00, 2px; 
    linkStyle 4 stroke:#0f0, 2px;

XII) Sidecar Pattern

Sidecar pattern is used to promote separation of concerns by offloading processing of some kind to a separate / side module that gets along with a primary app. Common sidecar modules are logging service, configuration service, monitor service and etc.

---
title: Sidecar Pattern
---
flowchart TD
    main[Primary App] <--> log(Logging Service)
    main <--> config(Configuration Service)
    main <--> monitor(Monitoring Service)
    subgraph Sidecar
        log <--> config
        config <--> monitor
        monitor <--> log
    end

    style main fill:#0f0
    style log fill:#ff0
    style config fill:#f66
    style monitor fill:#0ff

XIII) Strangler / Vine Pattern

Strangler / Vine pattern is a pattern that gradually migrates parts of a monolith to fully microservices architecture.

%%{init: { 'logLevel': 'debug', 'theme': 'default' , 'themeVariables': {
              'cScale0': '#ff0000', 'cScaleLabel0': '#ffffff',
              'cScale1': '#00ff00',
              'cScale2': '#0000ff', 'cScaleLabel2': '#ffffff'
       } } }%%
timeline
    title Strangling the Todo monolith app
        section Monolith
            Jan : Monolith (API Gateway, User, Task, Notification)
        section Monolith + Microservices
            Feb : Monolith (User, Task, Notification) : Service API Gateway
            Mar : Monolith (Task, Notification) : Service API Gateway : Service User
            Apr : Monolith (Notification): Service API Gateway : Service User : Service Task
        section Microservices
            May : Service API Gateway : Service User : Service Task : Service Notification
        

XIV) Aggregator Pattern

The Aggregator pattern is a composition pattern in microservices architecture. It looks quite similar with API Gateway pattern initially from diagram, but if you go in detail, the key difference is it will composite response from other services.

---
title: Aggregator Pattern
---
graph LR
  GW[API Gateway] --> Agg[Post Aggregate]
  subgraph Aggregator
    Agg -->|REST call| A[Post Service]What 
    Agg -->|REST call| B[Comment Service]
    Agg -->|REST call| C[Share Service]
    A -->|DB call| D[DB A]
    B -->|DB call| E[DB B]
    C -->|DB call| F[DB C]
  end
   
  style Agg fill:#f9d0c4
  style GW fill:#f0b37e
  style A fill:#a9dc76
  style B fill:#a9dc76
  style C fill:#a9dc76
  style D fill:#78dce8
  style E fill:#78dce8
  style F fill:#78dce8

XV) Chain of Responsibility Pattern

Chain of Responsibility Pattern combines multiple chained outputs and returns it as a single output. A client sends a request to Service A, and from A to Service B and to Service C, then from C, it returns to B and back to A, lastly, it’s from A, it returns response to the client. Therefore, the chains should not be too many, else it will be slowing down the response.

---
title: Chain of Responsibility Pattern
---
graph RL
    UI[Browser] <--> GW[API Gateway]
    GW --> |1. REST call| A[Service A] 
    subgraph Chained 
        A -->|2. REST call| B[Service B] 
        B -->|3. REST call| C[Service C] 
        C -->|4. Response C| B 
        B -->|5. Response B+C| A 
    end 
    A -->|6. Response A+B+C| GW

    style UI fill:#f9d0c4 
    style GW fill:#f0b37e 
    style A fill:#a9dc76 
    style B fill:#a9dc76 
    style C fill:#a9dc76 

    linkStyle default stroke:#000, 1px; 
    linkStyle 1,2,3 stroke:#00f, 2px; 
    linkStyle 4,5,6 stroke:#0f0, 2px;

Reference:
https://blog.bitsrc.io/retry-pattern-3f7221913e41
https://dzone.com/articles/sidecar-design-pattern-in-your-microservices-ecosy-1
https://learn.microsoft.com/en-us/azure/architecture/patterns/sidecar

ORA-01652: unable to extend temp segment in tablespace on .NET EF Core

If you are using Oracle DB and you run a .NET Entity Framework Core LINQ query, but you receive the error below:

ORA-01652: unable to extend temp segment in tablespace

One of the common solutions is to increase the temp segment in Oracle tablespace disk space. But what happens if it is not working, or if it happens after some time?

One of the reasons the error raised by Oracle DB is the SQL query generated by EF Core is too complex and requires a lot of temp tablespaces to temporarily hold the query data. Below is one example generated by EF Core for the where criteria

WHERE ((((:No_0 IS NULL ) OR ((("m"."NO" IS NOT NULL) AND ((("m"."NO" LIKE :no_0 || N'%') AND (SUBSTR("m"."NO", 1, LENGTH(:no_0)) = :no_0)))))))

The SQL query seems complex with many parentheses and causes the oracle to be out of temp segment space. For the same query, if try in Oracle SQL Developer will get the same error but if remove the null and length check, it gains better performance and fix out of segment space issue.

WHERE (("m"."NO" IS NOT NULL) AND ("m"."NO" LIKE N'1234%'))

It seems like cannot get the similar SQL script on above to generate by EF Core, but found if using Dynamic LINQ which with System.Linq.Dynamic.Core, it can generate the query on above. So, the issue was solved.

BadImageFormatException: How to Switch IIS Express to 64-Bit / 32-Bit for ASP.NET?

Are you getting the error below while running your ASP.NET Web Application?

Server Error in ‘/’ Application.
Could not load file or assembly ‘ClassLibrary1’ or one of its dependencies. An attempt was made to load a program with an incorrect format.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.BadImageFormatException: Could not load file or assembly ‘ClassLibrary1’ or one of its dependencies. An attempt was made to load a program with an incorrect format.

From the message above, the .NET assembly ‘ClassLibrary1’ is the culprit. The ‘ClassLibrary1’ is an x64 bit library, but the ASP.NET application we want to run on IIS Express is 32-bit, which is also default bitness before Visual Studio 2022.

To change the bitness from x86 (32-bit) to x64 (64-bit), just go to Tools -> Options > Projects and Solutions -> Web Projects > Use the 64 bit version of IIS Express for web sites and projects and check it. This setting is available since VS 2013.

After that, run your project again and the error message should disappear.

Lastly, in the latest Visual Studio 2022, you can set the IIS Express bitness per project instead of all the projects you open in Visual Studio. Just right click your project on Solution Explorer > Properties > Web -> Bitness.