C# Game Of Life

The Game Of Life is a cell life simulator devised by the Cambridge mathematician John Conway.

Game Of Life in a Console Application

The simulator serves as an excellent coding example to learn new programming languages. The rules are simple. You have a grid in a predetermined size, each cell can live or die based on a set of rules:

  • If a cell is alive, and there are 0 or 1 living neighbors, the cell dies of loneliness.
  • If a cell is alive, and there are 2 or 3 living neighbors, the cell survives.
  • If a cell is alive, and there are 4 or more living neighbors, the cell dies of overpopulation.
  • If a cell is dead, and there are 3 living neighbors, the cell comes alive.

This is an example of a simple game of life implementation in C# and .net 8. The simulation uses the console output to visualize the game progress.

UPDATE: Code have been updated to recalculate the complete board before progressing. Thanks to Alexander Gats for that one!

STEP 1: DEFINE THE GRID SIZE AND PRE-SEED A NUMBER OF CELLS

const int BOARD_WIDTH = 80;
const int BOARD_LENGTH = 25;
const int ACTIVE_CELLS_AT_START = 400;

bool[,] board = new bool[BOARD_WIDTH, BOARD_LENGTH];
var random = new Random();

// Populate a number of cells to begin with
for (int i = 0; i < ACTIVE_CELLS_AT_START; i++)
{
  board[random.Next(board.GetLength(0)), random.Next(board.GetLength(1))] = true;
}

First we define the game prerequisites; the size of the grid (called board) and how many cells should be alive when the simulation starts.

Then we populate the grid by randomly setting cells in the grid to true (true meaning alive, false meaning dead).

STEP 2: MAKE AN ALGORITHM TO CALCULATE THE NUMBER OF LIVING NEIGHBORS

int CountNeighbors(bool[,] board, int x, int y)
{
  int ac = 0;
  // Double loop from 1 up and 1 left to 1 down and 1 right
  // Gives 9 iterations
  for (int i = x - 1; i <= x + 1; i++)
  {
    for (int j = y - 1; j <= y + 1; j++)
    {
      // Only count cells within the board (IsValidIndex)
      // and do not count the cell itself (IsYourself)
      if (IsValidIndex(i, j, board.GetLength(0), board.GetLength(1)) 
          && !IsYourself(x, y, i, j) 
          && board[i, j])
        ac++;
    }
  }

  return ac;
}

bool IsValidIndex(int i, int j, int rows, int cols)
{
  return i >= 0 && i < rows && j >= 0 && j < cols;
}

bool IsYourself(int x, int y, int i, int j)
{
  return x == i && y == j;
}

The CountNeighBors takes the game grid and the x/y position of the cell to calculate how many of the 8 neighboring cells are alive.

The white cells are the cells to check

The algorithm needs a double loop to check the neighbors.

But it also needs to be smart enough to count even if the cell is on the edge of the grid. The IsValidIndex checks to see if the cell being investigated is within the board.

Also, we should not count the cell being investigated. The IsYourself checks to see if the cell being investigated is the current cell, disregard that cell.

STEP 3: CALCULATE THE CELL LIFE

bool CalculateCellLife(bool[,] board, int x, int y)
{
  int neighbors = CountNeighbors(board, x, y);
  // If cell is alive:
  if (board[x, y] == true)
  {
    // 0 or 1 neighbors: Die of loneliness
    if (neighbors <= 1)
      return false;
    // 4 or more neighbors: Die of overpopulation
    if (neighbors >= 4)
      return false;
    return true;
  }
  // If cell is not alive:
  else
  {
    // 3 neighbors give birth to a new cell
    if (neighbors == 3)
      return true;
    return false;
  }
}

This is the actual game of life algorithm.

The method uses the CountNeighbors function to calculate the number of alive cells surrounding a specified cell, and return true if cell is alive and false if cell is dead.

As stated before, the rules are that a living cell will die if there are 0,1 or more than 4 alive surrounding cells, and come alive if there is precisely 3 living cells surrounding it.

STEP 4: MAKE A FUNCTION TO PROGRESS THE SIMULATION BY ONE STEP

bool[,] StepSimulation(bool[,] currentBoard)
{
  // Recalculate a new board based on the values
  // of the current board
  bool[,] newBoard = new bool[BOARD_WIDTH, BOARD_LENGTH];
  for (int x = 0; x < board.GetLength(0); x++)
  {
    for (int y = 0; y < board.GetLength(1); y++)
    {
      newBoard[x,y] = CalculateCellLife(currentBoard, x, y);
    }
  }
  return newBoard;
}

Here we calculate the next step in the simulation by creating a new board based on the values of the old board.

STEP 5: DRAW A CELL IN THE CONSOLE

void DrawCell(int x, int y, bool isAlive)
{
  Console.SetCursorPosition(x, y);
  if (!isAlive)
    Console.BackgroundColor = ConsoleColor.Black;
  else
    Console.BackgroundColor = ConsoleColor.White;
  Console.Write(" ");
}

To draw a cell in the console I simply move the cursor to the determined position and draws a space with either black (dead) or white (alive) background.

STEP 6: THE MAIN GAME LOOP

while (true)
{
  // Progress the simulation
  board = StepSimulation(board);
  // Draw the new board
  for (int x = 0; x < board.GetLength(0); x++)
  {
    for (int y = 0; y < board.GetLength(1); y++)
    {
      DrawCell(x, y, board[x, y]);
    }
  }
}

This is the main game loop that loops forever, takes each cell on the board, calculates if the cell must live or die, and draws the result to screen.

COMPLETE CODE:

This is the code in its completion:

const int BOARD_WIDTH = 80;
const int BOARD_LENGTH = 25;
const int ACTIVE_CELLS_AT_START = 400;

bool[,] board = new bool[BOARD_WIDTH, BOARD_LENGTH];
var random = new Random();

// Populate a number of cells to begin with
for (int i = 0; i < ACTIVE_CELLS_AT_START; i++)
{
  board[random.Next(board.GetLength(0)), random.Next(board.GetLength(1))] = true;
}

while (true)
{
  // Progress the simulation
  board = StepSimulation(board);
  // Draw the new board
  for (int x = 0; x < board.GetLength(0); x++)
  {
    for (int y = 0; y < board.GetLength(1); y++)
    {
      DrawCell(x, y, board[x, y]);
    }
  }
}

bool[,] StepSimulation(bool[,] currentBoard)
{
  // Recalculate a new board based on the values
  // of the current board
  bool[,] newBoard = new bool[BOARD_WIDTH, BOARD_LENGTH];
  for (int x = 0; x < board.GetLength(0); x++)
  {
    for (int y = 0; y < board.GetLength(1); y++)
    {
      newBoard[x,y] = CalculateCellLife(currentBoard, x, y);
    }
  }
  return newBoard;
}

bool CalculateCellLife(bool[,] board, int x, int y)
{
  int neighbors = CountNeighbors(board, x, y);
  // If cell is alive:
  if (board[x, y] == true)
  {
    // 0 or 1 neighbors: Die of loneliness
    if (neighbors <= 1)
      return false;
    // 4 or more neighbors: Die of overpopulation
    if (neighbors >= 4)
      return false;
    return true;
  }
  // If cell is not alive:
  else
  {
    // 3 neighbors give birth to a new cell
    if (neighbors == 3)
      return true;
    return false;
  }
}

int CountNeighbors(bool[,] board, int x, int y)
{
  int ac = 0;
  // Double loop from 1 up and 1 left to 1 down and 1 right
  // Gives 9 iterations
  for (int i = x - 1; i <= x + 1; i++)
  {
    for (int j = y - 1; j <= y + 1; j++)
    {
      // Only count cells within the board (IsValidIndex)
      // and do not count the cell itself (IsYourself)
      if (IsValidIndex(i, j, board.GetLength(0), board.GetLength(1)) 
          && !IsYourself(x, y, i, j) 
          && board[i, j])
        ac++;
    }
  }

  return ac;
}

bool IsValidIndex(int i, int j, int rows, int cols)
{
  return i >= 0 && i < rows && j >= 0 && j < cols;
}

bool IsYourself(int x, int y, int i, int j)
{
  return x == i && y == j;
}

void DrawCell(int x, int y, bool isAlive)
{
  Console.SetCursorPosition(x, y);
  if (!isAlive)
    Console.BackgroundColor = ConsoleColor.Black;
  else
    Console.BackgroundColor = ConsoleColor.White;
  Console.Write(" ");
}

MORE TO READ:

Posted in .net, .NET Core, c#, General .NET | Tagged , , , , | 2 Comments

C# Connect to 2 or more databases using a Datacontext

This is more of a design pattern than it is a requirement. I use this technique when my application grows in size. When my application needs to connect to 2 or more databases, I use this encapsulation method to allow me to name my database connections before using them.

STEP 1: ADD 2 CONNECTION STRINGS TO THE appsettings.config

  "ConnectionStrings": {
    "Source": "************",
    "Destination": "************",
  },

STEP 2: MAKE 2 ALMOST IDENTICAL CLASSES, ONE FOR EACH CONNECTION STRING

The basic “DataContext” class looks like this:

using Microsoft.Data.SqlClient;
using Microsoft.Extensions.Configuration;

namespace MyCode
{
    public class SourceDataContext
    {
        private readonly string _connectionString;

        public SourceDataContext(IConfiguration configuration)
        {
            _connectionString = configuration.GetConnectionString("Source");
        }

        public IDbConnection CreateConnection() => new SqlConnection(_connectionString);
    }
}

namespace MyCode
{
    public class DestinationDataContext
    {
        private readonly string _connectionString;

        public DestinationDataContext(IConfiguration configuration)
        {
            _connectionString = configuration.GetConnectionString("Destination");
        }

        public IDbConnection CreateConnection() => new SqlConnection(_connectionString);
    }
}

The only difference is the connection string name. These names are only as demonstration, although they could work in a production environment. Names like “ConsumerDatacontext” or “ProductsDatacontext” are probably more common.

STEP 3: ADD THE DataContext CLASSES IN THE Program.cs

Add both classes as singletons:

services.AddSingleton<SourceDataContext>();
services.AddSingleton<DestinationDataContext>();

Now I have some good names for my database connections. The connection string is hidden, and can now be changed without having to rename multiple code files. And I can add things like Azure Managed Identity to one or more connections without interfering with calling code.

STEP 4: USE THE CLASSES IN YOUR REPOSITORIES:

This piece of pseudocode is using Dapper to select from a table. What you should be focusing on is line 17, where I create the database connection.

using Dapper;
using Microsoft.Data.SqlClient;

namespace MyCode
{
    public class MyRepository
    {
        private readonly SourceDataContext _dataContext;

        public AdvertBrandRepository(SourceDataContext dataContext)
        {
            _destinationDataContext = destinationDataContext;
        }

        public async Task<IEnumerable<MyData>> GetAll()
        {
            using var connection = _sourceDataContext.CreateConnection();
            mydata = await connection.QueryAsync<MyData>($"SELECT * from MyTable");
            return mydata;
        }
    }
}

WHAT OTHER SHENANIGANS CAN YOÚ DO WHEN YOU HAVE A Datacontext class?

You can have the connection string og one database controlled by Azure Managed Identity, and another not.

See an example on how to use Azure Managed Identity here.

You can add all available table names to the Datacontext:

namespace MyCode
{
    public class SourceDataContext
    {
        private readonly string _connectionString;

        public SourceDataContext(IConfiguration configuration)
        {
            _connectionString = configuration.GetConnectionString("Source");
        }

        public SqlConnection CreateConnection() => new SqlConnection(_connectionString);

        public string TableName1 = "TableName1";
        public string TableName2 = "TableName2";

    }
}

// Then use the table names in the code:

using Dapper;
using Microsoft.Data.SqlClient;

namespace MyCode
{
    public class MyRepository
    {
        ...
        ...

        public async Task<IEnumerable<MyData>> GetAll()
        {
            using var connection = _sourceDataContext.CreateConnection();
            mydata = await connection.QueryAsync<MyData>($"SELECT * from {_sourceDataContext.TableName1}");
            return mydata;
        }
    }
}

Or you can have an “IsEnabled” to allow yourself to do DryRuns:

namespace MyCode
{
    public class SourceDataContext
    {
        private readonly string _connectionString;

        public SourceDataContext(IConfiguration configuration)
        {
            _connectionString = configuration.GetConnectionString("Source");
            // In this example, there is a setting called "SourceEnabled" in the config file
            Enabled = _configuration.Get<bool>("SourceEnabled");
        }

        public SqlConnection CreateConnection() => new SqlConnection(_connectionString);
        public bool Enabled { get; private set; };
    }
}

// Then use the enabled in your code:

using Dapper;
using Microsoft.Data.SqlClient;

namespace MyCode
{
    public class MyRepository
    {
        ...
        ...

        public async Task<IEnumerable<MyData>> GetAll()
        {
            if (!_sourceDataContext.Enabled)
                return Enumerable.Empty<MyData>();
            using var connection = _sourceDataContext.CreateConnection();
            mydata = await connection.QueryAsync<MyData>($"SELECT * from {_sourceDataContext.TableName1}");
            return mydata;
        }
    }
}

MORE TO READ:

Posted in .net, .NET Core, c#, General .NET, Microsoft Azure | Tagged , , , , , , , , , , | Leave a comment

C# SqlBulkCopy insert a list of objects

The SqlBulkCopy class is able to bulk insert data from anything that comes from a DataTable or can be read using a SqlDataReader.

So what do you do if you have an array of objects?

You have to convert the array into a DataTable, then call SqlBulkCopy.

First of all, this is my table:

CREATE TABLE [dbo].[Favorites](
    [rowId] [int] IDENTITY(1,1) NOT NULL,
    [userKey] [int] NOT NULL,
    [favoriteId] [nvarchar](255) NULL,
    [lastModified] [datetime] NULL,
    [isDeleted] [bit] NULL,
    [created] [datetime] NULL,
 CONSTRAINT [PK_Favorites] PRIMARY KEY CLUSTERED 
(
    [rowID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

I have mapped the table into a DTO. Please notice that names AND THE ORDER of the fields in the DTO matches the names order of the fields in the SQL table:

public class FavoriteDTO
{
    public int RowId { get; set; } 
    public int UserKey { get; set; } 
    public string FavoriteId { get; set; } 
    public DateTime? LastModified { get; set; } 
    public bool? IsDeleted { get; set; } 
    public DateTime? Created { get; set; } 
}

STEP 1: CONVERT THE DTO INTO A DATATABLE

First we must convert the array of FavoriteDTO into a DataTable. This extension method will take care of that:

using System.ComponentModel;
using System.Data;
 
namespace MyCode
{
  public static class IEnumerableExtensions
  {
    public static DataTable ToDataTable<T>(this IEnumerable<T> data)
    {
      PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T));
      DataTable table = new DataTable();
      foreach (PropertyDescriptor prop in properties)
      {
        table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
      }
      foreach (T item in data)
      {
        DataRow row = table.NewRow();
        foreach (PropertyDescriptor prop in properties)
          row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
        table.Rows.Add(row);
      }
      return table;
    }
  }
}

STEP 2: THE BULKCOPY METHOD

This BulkInsertAsync method will call the ToDataTable() extension method before calling WriteToServerAsync, allowing us to insert the objects in bulk.

using Microsoft.Data.SqlClient;

public async Task BulkInsertAsync(IEnumerable<FavoriteDTO> favorites)
{
    try
    {
        using var connection = new SqlConnection("your connection string");
        using (SqlBulkCopy bulkCopy = new SqlBulkCopy(connection))
        {
            connection.Open();
            bulkCopy.DestinationTableName = "Favorites";
            await bulkCopy.WriteToServerAsync(favorites.ToDataTable());
        }
    }
    catch (SqlException e)
    {
        throw new Exception($"Failed to insert favorites: {e.Message}", e);
    }
}

That’s it. Happy coding.

MORE TO READ:

Posted in .net, .NET Core, c#, General .NET | Tagged , , , | 1 Comment

C# Convert array of objects into a DataTable

This extension method lets you convert an array of objects into a DataTable. DataTables are used for SQL inserts.

This solution is not mine, in fact it has been copied so many times that I don’t know who came up with it in the first place.

STEP 1: DEFINE YOUR CLASS

The class must represent the SQL table, INCLUDING THE SORT ORDER OF THE DATABASE FIELDS. If the sort order is not correct, you will not be able to use the DataTable for SQL inserts.

This is just an example of a class. Yours will look different.

public class FavoriteDTO
{
    public int RowId { get; set; } 
    public int UserKey { get; set; } 
    public string FavoriteId { get; set; } 
    public DateTime? LastModified { get; set; } 
    public bool? IsDeleted { get; set; } 
    public DateTime? Created { get; set; } 
}

STEP 2: IMPLEMENT THIS EXTENSION METHOD

using System.ComponentModel;
using System.Data;

namespace MyCode
{
  public static class IEnumerableExtensions
  {
    public static DataTable ToDataTable<T>(this IEnumerable<T> data)
    {
      PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T));
      DataTable table = new DataTable();
      foreach (PropertyDescriptor prop in properties)
      {
        table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
      }
      foreach (T item in data)
      {
        DataRow row = table.NewRow();
        foreach (PropertyDescriptor prop in properties)
          row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
        table.Rows.Add(row);
      }
      return table;
    }
  }
}

STEP 3: HOW TO USE THE EXTENSION METHOD

List<FavoriteDTO> favorites = new List<FavoriteDTO>();
favorites.Add(new FavoriteDTO() { RowId = 0, UserKey = 4 });
favorites.Add(new FavoriteDTO() { RowId = 1, UserKey = 2 });
favorites.Add(new FavoriteDTO() { RowId = 2, UserKey = 0 });
favorites.Add(new FavoriteDTO() { RowId = 3, UserKey = 6 });
favorites.Add(new FavoriteDTO() { RowId = 3, UserKey = 9 });

var dataTable = favorites.ToDataTable();

That’s it. Happy coding.

MORE TO READ:

Posted in .net, .NET Core, c#, General .NET | Tagged , , , | 1 Comment

C# Inject ILogger into Dapper Polly Retry Policy

There are many articles describing how to make a retry policy when using Dapper. I especially like this extension method implementation. But you cannot inject any class into extension methods because extension methods reside in a static class. Therefore you don’t have a constructor where the injection can happen.

Instead, another easy solution is to create a base class for your Dapper repository classes.

STEP 1: THE PREREQUISITES

You need Dapper and Polly of course:

Then you need a reference to a class called “SqlServerTransientExceptionDetector“. You can either grab the code and paste in into your class, or you can reference Microsoft.EntityFrameworkCore.SqlServer NuGet:

STEP 2: THE BASE CLASS

I’m assuming you use standard dependency injection, and that you have an ILogger to be used.

using Microsoft.Extensions.Logging;
using Polly.Retry;
using Polly;
using System.ComponentModel;
using System.Data.SqlClient;
using System.Data;
using Dapper;

namespace MyCode
{
  public class DapperRetryPolicyBaseClass<T>
  {
    private readonly ILogger<T> _logger;
    private static readonly IEnumerable<TimeSpan> _retryTimes = new[]
    {
        TimeSpan.FromSeconds(2),
        TimeSpan.FromSeconds(4),
        TimeSpan.FromSeconds(8)
    };

    private readonly AsyncRetryPolicy _asyncRetryPolicy;

    public DapperRetryPolicyBaseClass(ILogger<T> logger)
    {
      _logger = logger;

      _asyncRetryPolicy = Policy
        .Handle<SqlException>(SqlServerTransientExceptionDetector.ShouldRetryOn)
        .Or<TimeoutException>()
        .OrInner<Win32Exception>(SqlServerTransientExceptionDetector.ShouldRetryOn)
        .WaitAndRetryAsync(_retryTimes,
                        (exception, timeSpan, retryCount, context) =>
                        {
                          _logger.LogWarning(exception, "{InstanceType} SQL Exception. retry #{RetryCount}. Exception {Exception}", _logger.GetType(), retryCount, exception);
                        });
    }

    protected async Task<int> ExecuteAsyncWithRetry(IDbConnection connection, 
                                                    string sql, 
                                                    object param = null,
                                                    IDbTransaction transaction = null, 
                                                    int? commandTimeout = null,
                                                    CommandType? commandType = null
                                                   ) =>
        await _asyncRetryPolicy.ExecuteAsync(async () => await connection.ExecuteAsync(sql, param, transaction, commandTimeout, commandType));

    protected async Task<IEnumerable<T>> QueryAsyncWithRetry<T>(IDbConnection connection, 
                                                                string sql, 
                                                                object param = null,
                                                                IDbTransaction transaction = null, 
                                                                int? commandTimeout = null,
                                                                CommandType? commandType = null
                                                               ) =>
        await _asyncRetryPolicy.ExecuteAsync(async () => await connection.QueryAsync<T>(sql, param, transaction, commandTimeout, commandType));
  }
}

Being a base class, we suddenly have a constructor where the injection can happen. Also, the Polly retry policy can be defined in the constructor where the ILogger is available.

STEP 3: HOW TO USE THE BASE CLASS

using System.Data.SqlClient;
using Dapper;
using Microsoft.Extensions.Logging;

namespace MyCode
{
  public class MyRepository : DapperRetryPolicyBaseClass<MyRepository>
  {

    private readonly ILogger<JsonEventRepository> _logger;

    public JsonEventRepository(ILogger<MyRepository> logger) : base(logger)
    {
      _logger = logger;
    }

    public async Task Execute(string someDateToBeInsertedIntoATable)
    {
      using var sqlConnection = new SqlConnection("some-connection-string");
      await base.ExecuteWithRetryAsync(sqlConnection, "insert into some_table (field) values (@field)", 
        new
        {
            Field = someDateToBeInsertedIntoATable,
        }
      );
    }
  }
}

Inherit from DapperRetryPolicyBaseClass and define the class type, and the ILogger of the base class will have the same type as the MyRepository.

Then use the base retry methods. On any retry, the base class will log the retry using the ILogger.

That’s it. You are now a Dapper expert. Happy coding.

MORE TO READ:

Posted in .net, .NET Core, c#, General .NET | Tagged , , , , , , | Leave a comment

C# Dapper Async Bulk Inserts

In order for Dapper to do bulk inserts, you need a third-party library:

UPDATE 2023-12-08: Please note that Z.Dapper.Plus is a licensed product and you must pay to use it. For a solution without licensing, check out this article: C# SqlBulkCopy insert a list of objects

Once you have this package, bulk inserts are at your fingertips. Async is supported in a rather special way. Lets see how it’s done.

First of all, this is my table:

CREATE TABLE [dbo].[Favorites](
    [rowId] [int] IDENTITY(1,1) NOT NULL,
    [userKey] [int] NOT NULL,
    [favoriteId] [nvarchar](255) NULL,
    [lastModified] [datetime] NULL,
    [isDeleted] [bit] NULL,
    [created] [datetime] NULL,
 CONSTRAINT [PK_Favorites] PRIMARY KEY CLUSTERED 
(
    [rowID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

Dapper is an O/R mapper, so you’ll need a model class that represents the SQL table. The model class does not need to have the same name as the SQL table, but the fields need to be the same:

public class FavoriteDTO
{
    public int RowId { get; set; } 
    public int UserKey { get; set; } 
    public string FavoriteId { get; set; } 
    public DateTime? LastModified { get; set; } 
    public bool? IsDeleted { get; set; } 
    public DateTime? Created { get; set; } 
}

Now all you have to do is to create a repository with a bulk insert method:

using Dapper;
using Z.Dapper.Plus;
using Microsoft.Data.SqlClient;
using System.Data;

namespace MyCode
{
  public class FavoritesRepository
  {
    public FavoritesRepository()
    {
    }

    public async Task BulkInsertAsync(IEnumerable<FavoriteDTO> favorites)
    {
      using var connection = new SqlConnection("your-sql-connection-string");
      // Specifying that the "FavoriteDTO" model class should be bulk inserted
      // into the "Favorites" SQL table
      DapperPlusManager.Entity<FavoriteDTO>().Table("Favorites");        
      // Do the actual bulk insert
      await connection.BulkActionAsync(x => x.BulkInsert<FavoriteDTO>(favorites));
    }
  }
}

That’s it. You are now a Dapper expert. Happy coding.

MORE TO READ:

Posted in .net, .NET Core, c#, General .NET | Tagged , , , , | 1 Comment

C# Dapper convert numbers to 2 decimals

I had this problem when inserting numbers into a SQL database using Dapper. All of my numbers did not round correctly when being inserted, even when using Math.Round() before doing the insert.

My class is something like this:

public class Product
{
    public int ID { get; set; }
    public float Price { get; set; }
    public float VolumePrice { get; set; }
    public float VolumeQuantity { get; set; }
}

And before I did the SQL insert, I made sure that the price was rounded to 2 digits:

// Pseudocode, imagine that you have a 
// source item and you update the SQL
// destination item 
var product = new Product();
product.ID = souce.ID;
product.Price = Math.Round(source.Price, 2);
product.VolumePrice = Math.Round(source.VolumePrice, 2);
product.VolumeQuantity = Math.Round(source.VolumeQuantity, 2);

But even with the Math.Round(), the SQL database inserts this:

Invalid rounded numbers when inserting floats
Invalid rounded numbers when inserting floats

It turns out, that inserting the data type float will cause the rounding issue. When I changed the data type to double, the issue went away:

public class Product
{
    public int ID { get; set; }
    public double Price { get; set; }
    public double VolumePrice { get; set; }
    public double VolumeQuantity { get; set; }
}

With the double data type, the rounding is accepted:

Using double as datatype, and dapper will recognize the rounding
Using double as datatype, and dapper will recognize the rounding

In other words: do not use float as datatype. Use double.

MORE TO READ:

Posted in .net, .NET Core, c#, General .NET | Tagged , , , , | 1 Comment

Sitecore: Insert Link tree does not expand

In my development Sitecore, I had this strange situation that when I tried to insert a link, the tree would not fold out for certain items:

The content tree does not expand in the "insert link" dialog
The content tree does not expand in the “insert link” dialog

I can see that I get the following error back from the /-/item/v1/sitecore/shell?sc_itemid= call:

{“statusCode”:500,”error”:{“message”:”Template is null.”}}

With a little bit of searching, it turns out, that this branch of the content tree contains an item with no template:

This item's template have been deleted
This item’s template have been deleted

This reminiscence from a test long lost in time is visible in the content editor, but makes the “Insert Link” window fail.

To delete the item you need to use the good old DbBrowser, a tool found in Sitecore since the dawn of times. You find the tool here:

/sitecore/admin/dbbrowser.aspx

This tool is a near-database tool that allows you to do things that the shell does not. Here you can select the item in question and delete it:

DbBrowser gives you direct access to the database content
DbBrowser gives you direct access to the database content

After I deleted the item, the Insert Link works again.

MORE TO READ:

Posted in Sitecore 10, Sitecore 5, Sitecore 6, Sitecore 7, Sitecore 8, Sitecore 9 | Tagged , , | Leave a comment

Sitecore: Unable to load the service index for source https://sitecore.myget.org/F/sc-packages/api/v3/index.json.Sitecore – The remote name could not be resolved: ‘sitecore.myget.org’

Here is what you do if you receive messages like this:

Errors in packages.config projects
Unable to find version ‘9.0.171219’ of package ‘Sitecore.Kernel.NoReferences’.
https://sitecore.myget.org/F/sc-packages/api/v3/index.json: Unable to load the service index for source https://sitecore.myget.org/F/sc-packages/api/v3/index.json.
An error occurred while sending the request.
The remote name could not be resolved: ‘sitecore.myget.org’

Sitecore have been hosting NuGet packages on MyGet for a long time. But Sitecore have decided to move the package storage elsewhere. The move should have been executed November 2023, but because of MyGet service have been unstable, the decision have been accelerated.

This means that you need to update your build environment:

Old UrlNew Url
https://sitecore.myget.org/F/sc-packages/api/v3/index.jsonhttps://nuget.sitecore.com/resources/v3/index.json
https://sitecore.myget.org/gallery/sc-npm-packageshttps://www.npmjs.com/

Furthermore, the artifact provider is now https://cloudsmith.io/~sitecore/repos/resources/groups/

HOW TO UPDATE NUGET

In your NuGet.config, make sure the packageSources points to nuget.sitecore.com:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <solution>
    <add key="disableSourceControlIntegration" value="true" />
  </solution>
  <packageSources>
    <clear />
    <add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
    <add key="Sitecore" value="https://nuget.sitecore.com/resources/v3/index.json" />
  </packageSources>
</configuration>

MORE TO READ:

Posted in Sitecore 10, Sitecore 6, Sitecore 7, Sitecore 8, Sitecore 9 | Tagged , , , | Leave a comment

C# Application Insights Errors are Logged as Traces – How do I log them as Exceptions?

So when I log to Application Insights, I first follow this guide to set up Application Insights AND File logging:

C# Log to Application Insights and File from your .NET 6 Application

To create a log statement, I can now use the ILogger interface, and log my exceptions:

public class MyClass
{
    private ILogger<MyClass> _logger;

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

    public void MyMethod()
    {
        try
        {
            // some code goes here
        }
        catch (Exception ex)
        {
            _logger.LogError("Failed to process message GUID: {exception}", message, ex)
        }
    }
}

The logging is this line:

_logger.LogError("some error message goes here");

But hey? My error message is logged as a Trace in Application Insights:

The exception is being logged as a Trace with the severity level "Error".
The exception is being logged as a Trace with the severity level “Error”.

SO WHAT IF I WOULD LIKE TO LOG THIS AS AN EXCEPTION?

To log errors as exceptions, add the exception as the first parameter of the call:

public void MyMethod()
{       
    try
    {
        // some code goes here
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, "Failed to process message GUID: {exception}", message, ex)
    }
}

Notice the “ex” as the first parameter for LogError()? This will now log the error as an exception:

Using the exception as the first parameter will log any errors as exceptions
Using the exception as the first parameter will log any errors as exceptions

If you don’t have an exception, you can call the method with an empty exception:

// Calling with an empty exception. This will leave an empty exception line in 
// Application Insights:
_logger.LogError(new Exception(), "ExecuteAsync failed");

// So you should add a text to the exception.
// You can also use an exception of a different type
// if you wish:
_logger.LogError(new InvalidOperationException("Invalid operation"), "ExecuteAsync failed");

// You can still use custom properties in the following text:
_logger.LogError(new InvalidOperationException("Invalid operation"), "ExecuteAsync failed: {ErrorCode}", 404);

SIDE NOTE: WHAT IS THE DIFFERENCE BETWEEN AN ERROR AND AN EXCEPTION?

There are many thoughts on the difference between an error and an exception. But when it comes to Application Insights, traces with severity level “Error” will be part of your trace stack. If your log lines have a shared operation ID, you can click on any message and see all messages from the same operation ID:

The trace with severity level: Error is part of the same operation.
The trace with severity level: Error is part of the same operation.

If you click an exception, however, only the exception is shown:

When clicking exceptions, the trace stack is not shown
When clicking exceptions, the trace stack is not shown

You will need to click the tiny trace stack button to see the relation between the exception and the trace stack:

The exception is part of the trace stack, but only after clicking the tiny stack button previously
The exception is part of the trace stack, but only after clicking the tiny stack button previously

SO WHAT DO I DO? DO I USE Trace OR Exception THEN?

This is where it gets opinionated. In my opinion, you either use Traces for your entire application, or you use exceptions for your entire application. Looking for errors 2 places can be confusing. I see one exception to this rule:

Let’s say that you have an application that reads an event from a datasource like a queue or a database. If the connection to this datasource fails, you could argue that this is so severe, that the application cannot continue, and should throw an exception. If the processing of the message fails because of a business rule failing, you should throw a Trace with severity level “error”.

But it’s up to you. Anyway, you are now an Application Insights Expert. Happy coding.

MORE TO READ:

Posted in .net, .NET Core, c#, General .NET | Tagged , , , , | 1 Comment