tag:blogger.com,1999:blog-121766342024-03-18T03:00:29.122+00:00R. Bovills .NET Blog.NET Tricks, Tips, and SnippetsR. Bovillhttp://www.blogger.com/profile/13806761971999862684noreply@blogger.comBlogger414125tag:blogger.com,1999:blog-12176634.post-60217046700214565182023-10-16T15:23:00.002+01:002023-10-16T15:23:18.785+01:00Bit Operations
<p>An extension class that allows various bitwise operations on an integer to be performed when dealing with a [Flags] Enum type.
</p>
<pre>
// Bit wise operations on an integer as an extensions class
public static class IntBitwiseOperationsExtender
{
// Returns bits set in lhs or rhs or both
public static int BitwiseUnion(this int lhs, int rhs)
{
int bitWiseOr = lhs | rhs;
return bitWiseOr;
}
// Return bits set common to both lhs and rhs
public static int BitwiseIntersection(this int lhs, int rhs)
{
return lhs & rhs;
}
// Returns bits set in lhs or rhs but not in both
public static int BitwiseExclusiveOr(this int lhs, int rhs)
{
int exclusiveOr = BitwiseUnion(lhs, rhs) - BitwiseIntersection(lhs, rhs);
return exclusiveOr;
}
// Return lhs bits inverted, 0s becomes 1s and vice versa
public static int BitwiseInvert(this int lhs)
{
int bitWiseOr = ~lhs;
return bitWiseOr;
}
// Return lhs bits set minus any rhs bits set
public static int BitwiseSubtract(this int lhs, int rhs)
{
int common = BitwiseIntersection(lhs, rhs); // Find the bits common to both sides
int res = (int)lhs - (int)common;
return res;
}
// Return a value that has all the bits sets from the lhs and the rhs
public static int BitwiseAdd(this int lhs, int rhs)
{
return BitwiseUnion(lhs, rhs); // Same as a Bitwise Union
}
// Return true if lhs contains all the bits set within rhs
public static bool BitwiseContains(this int lhs, int rhs)
{
int common = lhs & rhs;
return (common == rhs);
}
// Return true if lhs contains one of the bits set within rhs
public static bool BitwiseContainsOneOf(this int lhs, int rhs)
{
int common = lhs & rhs;
return common > 0;
}
}
</pre>R. Bovillhttp://www.blogger.com/profile/13806761971999862684noreply@blogger.com0tag:blogger.com,1999:blog-12176634.post-65791687066758226702023-10-14T07:20:00.004+01:002023-10-16T15:17:29.394+01:00Simple Line Editor 2
Here is the new LineEditor, it can work on a file or a string. This timing using the power of LINQ.
<pre class="prettyprint lang-cs">
// Here is the definition of a line edit
public interface ILineEdit
{
string ApplyEdit(string input);
}
/// <summary>
/// Perform a bunch of edits on a text file or string on a line by line basis.
/// Only search and replace type edits are available.
/// </summary>
/// <summary>
/// Perform a bunch of edits on a IEnumerable<string>.
/// </summary>
public class TextLineEditor
{
private List<ILineEdit> _pendingEdits = new List<ILineEdit>();
public TextLineEditor()
{
}
public IEnumerable<string> ApplyEdits(IEnumerable<string> inputs)
{
foreach (var line in inputs)
{
string tmp = line;
foreach (ILineEdit edit in _pendingEdits)
{
tmp = edit.ApplyEdit(tmp);
}
yield return tmp;
}
}
public void AddEdit(ILineEdit edit)
{
Trace.Assert(edit != null);
_pendingEdits.Add(edit);
}
}
</pre>
<p>We can use this as the basis of a line by line text file ediitor:</p>
<pre class="prettyprint lang-cs">
/// <summary>
/// Perform a bunch of edits on a text file
/// Only search and replace type edits are available.
/// </summary>
public class FileTextLineEditor : TextLineEditor
{
public void ApplyEditsToFile(string inputPath, bool keepOriginal = true)
{
Debug.Assert(!string.IsNullOrWhiteSpace(inputPath));
var inputFileInfo = new FileInfo(inputPath);
Debug.Assert(inputFileInfo.Exists);
var outputPath = inputPath + ".new";
var outputFileInfo = new FileInfo(outputPath);
Debug.Assert(!outputFileInfo.Exists);
File.WriteAllLines(outputFileInfo.FullName, ApplyEdits(File.ReadLines(inputFileInfo.FullName)));
inputFileInfo.MoveTo(inputPath + ".bak");
outputFileInfo.Refresh();
outputFileInfo.MoveTo(inputPath);
if (!keepOriginal)
{
File.Delete(inputPath + ".bak");
}
}
}
</pre>
Here is a LineEdit example. Note that it can do deletes by replacing with a blank string.
<pre class="prettyprint lang-cs">
public class SearchAndReplace : ILineEdit
{
public enum RegExUsage
{
None = 0,
Use
}
public SearchAndReplace()
{
}
public SearchAndReplace(string searchFor, string replaceWith, bool extendedChars = false, RegExUsage regExUse = RegExUsage.None)
{
if (string.IsNullOrEmpty(searchFor))
throw new ArgumentException("Cannot be null or an empty string", "searchFor");
if (replaceWith == null)
throw new ArgumentException("Cannot be null", "replaceWith");
this.SearchFor = searchFor;
this.ReplaceWith = replaceWith;
this.ExtendedChars = extendedChars;
}
public bool IsRegEx { get; set; } = false;
public string SearchFor { get; set; } = "";
public string ReplaceWith { get; set; } = "";
public bool ExtendedChars { get; set; } = false;
public string ApplyEdit(string input)
{
if (ExtendedChars)
{
SearchFor = SearchFor.Replace("\\t","\t").Replace("\\n","\n").Replace("\\r","\r");
}
return (IsRegEx) ? SearchReplaceInString(input) : SearchReplaceInStringRegEx(input);
}
private string SearchReplaceInString(string input)
{
string output = input.Replace(SearchFor, ReplaceWith);
return output;
}
private string SearchReplaceInStringRegEx(string input)
{
string output = Regex.Replace(input, SearchFor, ReplaceWith);
return output;
}
}
</pre>
Here is an example usage that attempts to replace usage of Moq in a CS file to usage of NSubstitute:
<pre class="prettyprint lang-cs">
void Main()
{
string inputFile = @"a:/path/SomeFileName.cs";
FileTextLineEditor resEditor = new();
resEditor.AddEdits(this.Edits());
resEditor.ApplyEditsToFile(inputFile, keepOriginal: false);
}
internal IEnumerable<ILineEdit> Edits()
{
yield return new SearchAndReplace("using Moq", "using NSubstitute");
yield return new SearchAndReplace("MockBehavior.Strict", "");
yield return new SearchAndReplace("MockBehavior.Loose", "");
yield return new SearchAndReplace(".Object", "");
yield return new SearchAndReplace("It.IsAny", "Arg.Any");
yield return new SearchAndReplace("It.Is", "Arg.Is");
yield return new SearchAndReplace(@"(new\sMock\<([A-Za-z\<\>\-_]+)\>)", @"Substitute.For<$2>", false, SearchAndReplace.RegExUsage.Use);
yield return new SearchAndReplace(@"(Mock\<([A-Za-z\<\>\-_]+)\>\s([A-Za-z\<\>\-_]+)\s\=\snew)", @"var $3 = Substitute.For<$2>", false, SearchAndReplace.RegExUsage.Use);
// yield return new SearchAndReplace(@" = new()", @" = Substitute.For(??)");
//yield return new SearchAndReplace(@"(^\sMock\<([A-Za-z\<\>\-_]+)", "var", false, SearchAndReplace.RegExUsage.Use);
yield return new SearchAndReplace(@"(Setup\([a-z]+\s\=\>\s[a-z]+\.)", "", false, SearchAndReplace.RegExUsage.Use);
yield return new SearchAndReplace(@"(SetupGet\([a-z]+\s\=\>\s[a-z]+\.)", "", false, SearchAndReplace.RegExUsage.Use);
yield return new SearchAndReplace(@"(Verify\([a-z]+\s\=\>\s[a-z]+)", "Received(?)", false, SearchAndReplace.RegExUsage.Use);
yield return new SearchAndReplace("Times.Once", "1");
yield return new SearchAndReplace("Times.Never", "0");
yield return new SearchAndReplace(@"Returns\(\(\)\s\=\>\s", "Returns(");
}
</pre>
R. Bovillhttp://www.blogger.com/profile/13806761971999862684noreply@blogger.com0tag:blogger.com,1999:blog-12176634.post-4333922030285728042023-08-15T12:09:00.002+01:002023-08-15T14:21:38.279+01:00Simple Line Editor
Here is the LineEditor, it can work on a file or a string:
<pre class="prettyprint lang-cs">
// Here is the definition of a line edit
public interface ILineEdit
{
string ApplyEdit(string input);
}
/// <summary>
/// Perform a bunch of edits on a text file or string on a line by line basis.
/// Only search and replace type edits are available.
/// </summary>
public class TextLineEditor
{
private List<ILineEdit> _pendingEdits = new List<ILineEdit>();
public TextLineEditor()
{
}
public void ApplyEditsToFile(string inputPath, string outputPath)
{
Debug.Assert(!string.IsNullOrWhiteSpace(inputPath));
Debug.Assert(!string.IsNullOrWhiteSpace(outputPath));
Debug.Assert(!outputPath.Equals(inputPath));
var inputFileInfo = new FileInfo(inputPath);
var outputFileInfo = new FileInfo(outputPath);
Debug.Assert(inputFileInfo.Exists);
Debug.Assert(!outputFileInfo.Exists);
using (TextWriter writer = new StreamWriter(outputFileInfo.FullName))
{
using (TextReader reader = new StreamReader(inputFileInfo.FullName))
{
ApplyEdits(writer, reader);
}
}
}
public string ApplyEditsToString(string text)
{
Trace.Assert(text != null);
StringBuilder sb = new StringBuilder();
using (TextWriter writer = new StringWriter(sb))
{
using (TextReader reader = new StringReader(text))
{
ApplyEdits(writer, reader);
}
}
return sb.ToString();
}
private void ApplyEdits(TextWriter writer, TextReader reader)
{
string line = null;
while ((line = reader.ReadLine()) != null)
{
foreach (ILineEdit edit in _pendingEdits)
{
line = edit.ApplyEdit(line);
}
if (line != null)
writer.WriteLine(line);
}
}
public void AddEdit(ILineEdit edit)
{
Trace.Assert(edit != null);
_pendingEdits.Add(edit);
}
}
</pre>
This TextLineEditor could be replaced to work with IEnumerab<string> for line input and returning a IEnumerable<string> as an output.
Here is a LineEdit example. Note that it can do deletes by replacing with a blank string.
<pre class="prettyprint lang-cs">
public class SearchAndReplace : ILineEdit
{
public enum RegExUsage
{
None = 0,
Use
}
public SearchAndReplace()
{
}
public SearchAndReplace(string searchFor, string replaceWith, bool extendedChars = false, RegExUsage regExUse = RegExUsage.None)
{
if (string.IsNullOrEmpty(searchFor))
throw new ArgumentException("Cannot be null or an empty string", "searchFor");
if (replaceWith == null)
throw new ArgumentException("Cannot be null", "replaceWith");
this.SearchFor = searchFor;
this.ReplaceWith = replaceWith;
this.ExtendedChars = extendedChars;
}
public bool IsRegEx { get; set; } = false;
public string SearchFor { get; set; } = "";
public string ReplaceWith { get; set; } = "";
public bool ExtendedChars { get; set; } = false;
public string ApplyEdit(string input)
{
if (ExtendedChars)
{
SearchFor = SearchFor.Replace("\\t","\t").Replace("\\n","\n").Replace("\\r","\r");
}
return (IsRegEx) ? SearchReplaceInString(input) : SearchReplaceInStringRegEx(input);
}
private string SearchReplaceInString(string input)
{
string output = input.Replace(SearchFor, ReplaceWith);
return output;
}
private string SearchReplaceInStringRegEx(string input)
{
string output = Regex.Replace(input, SearchFor, ReplaceWith);
return output;
}
}
</pre>
Here is an example usage:
<pre class="prettyprint lang-cs">
{
var inputResFilePath = ...
var outputResNewCsvFilePath ...
var outputResCsvFilePath = ...
var inputCnbFilePath = ...
var outputCnbNewCsvFilePath ...
var outputCnbCsvFilePath = ...
var replaceCommentChars = new SearchAndReplace("#", "*");
{
var replaceTabsWithGT = new SearchAndReplace("\t", " > ");
TextLineEditor resEditor = new();
resEditor.AddEdit(replaceTabsWithGT);
resEditor.AddEdit(replaceCommentChars);
resEditor.ApplyEditsToFile(inputResFilePath, outputResNewCsvFilePath);
File.Delete(outputResCsvFilePath);
File.Move(outputResNewCsvFilePath, outputResCsvFilePath);
}
{
var replaceExclamationChars = new SearchAndReplace("!", "*");
var replaceTabWithHyphen = new SearchAndReplace("\t", " - ");
TextLineEditor cnbEditor = new();
cnbEditor.AddEdit(replaceTabWithHyphen);
cnbEditor.AddEdit(replaceCommentChars);
cnbEditor.AddEdit(replaceExclamationChars);
cnbEditor.ApplyEditsToFile(inputCnbFilePath, outputCnbNewCsvFilePath);
File.Delete(outputCnbCsvFilePath);
File.Move(outputCnbNewCsvFilePath, outputCnbCsvFilePath);
}
}
</pre>
R. Bovillhttp://www.blogger.com/profile/13806761971999862684noreply@blogger.com0tag:blogger.com,1999:blog-12176634.post-46363957603184278132023-07-21T06:34:00.005+01:002023-08-03T08:13:03.056+01:00Startup For Dependency Injection, Settings and Logging<p>The Startup format for a Console program with a Serilog logger</p>
You need quite a few packages:
<pre>
<PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.4" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.TraceSource" Version="7.0.0" />
<PackageReference Include="Serilog" Version="2.12.0" />
<PackageReference Include="Serilog.Enrichers.Environment" Version="2.2.0" />
<PackageReference Include="Serilog.Extensions.Hosting" Version="7.0.0" />
<PackageReference Include="Serilog.Extensions.Logging" Version="7.0.0" />
<PackageReference Include="Serilog.Formatting.Compact" Version="1.1.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="7.0.0" />
<PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
</pre>
<p>Here is a Startup class:</p>
<pre class="prettyprint lang-cs">
using System;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Console;
using Serilog;
using Serilog.Core;
using Serilog.Formatting.Compact;
public class Startup
{
IConfigurationRoot? Configuration { get; init; }
public AppSettings AppSettings { get; private set; } = new AppSettings();
public Startup()
{
Configuration = BuildConfiguration();
}
public IServiceCollection ConfigureServices(string loggingFilePath)
{
IServiceCollection services = new ServiceCollection();
services.AddLogging(builder => builder.AddSerilog(
new LoggerConfiguration()
#if DEBUG
.MinimumLevel.Debug() // Log Debug level and higher
#else
.MinimumLevel.Information() // Log Information level and higher
#endif
// To read the settings from the configuration file:
//.ReadFrom.Configuration(this.Configuration!)
.WriteTo.Console() // To make the console output window a logger sink
// To write to a custom logging file and render object properties in JSon format
.WriteTo.File(new RenderedCompactJsonFormatter(), loggingFilePath)
.CreateLogger()));
RegisterDependencyInjectedServices(services);
return services;
}
private static IServiceCollection RegisterDependencyInjectedServices(IServiceCollection services)
{
// Use this format: services.AddScoped/Transient/Singleton<ISomething, Something>();
// For example
services.AddSingleton<IResDataRepo, ResDataRepo>();
services.AddSingleton<ICnbDataRepo, CnbDataRepo>();
services.AddTransient<IGuiInputListener, GuiListener>();
return services;
}
private IConfigurationRoot BuildConfiguration()
{
// To create a custom AppSetting file:
const string settingsFileName = "{MyApplicationName}.AppSettings.json";
string currentDirectory = Directory.GetCurrentDirectory();
var builder = new ConfigurationBuilder()
.SetBasePath(currentDirectory)
.AddJsonFile(settingsFileName, optional: false);
var config = builder.Build();
var settings = config.GetSection("AppSettings").Get<AppSettings>();
if (settings == null)
{
Console.WriteLine($"ERR: {settingsFileName} not found in current directory {currentDirectory}, using defaults!");
}
AppSettings = settings ?? new AppSettings();
return config;
}
}
</pre>R. Bovillhttp://www.blogger.com/profile/13806761971999862684noreply@blogger.com0tag:blogger.com,1999:blog-12176634.post-38421692924722769692023-02-13T07:16:00.004+00:002023-04-04T08:36:38.501+01:00File Renamer<p>Rename a file when you have a path. This was written as a LinqPad script</p>
<pre class="prettyprint lang-cs">
void Main()
{
Console.WriteLine(FileRenamer. CalcNewFilePath (@"c:/some/Directory/File.pdf"));
Console.WriteLine(FileRenamer. CalcNewFilePath (@"File.pdf"));
Console.WriteLine(FileRenamer. CalcNewFilePath (@"C:File.pdf"));
Console.WriteLine(FileRenamer. CalcNewFilePath (@"/some/Directory/File.pdf"));
Console.WriteLine(FileRenamer. CalcNewFilePath (@"/some\Directory/File.pdf"));
Console.WriteLine(FileRenamer. CalcNewFilePath (@"e:\zdump\some\Directory\File.pdf"));
}
public static class FileRenamer
{
const string Suffix = @"-xx";
/// <summary>
/// Calculate the file path of the file, changing the file name
/// in the process
/// </summary>
/// <param name="filePath"></param>
/// <returns></returns>
public static string CalcNewFilePath(string filePath)
{
if (string.IsNullOrWhiteSpace(filePath))
return "";
var ext = Path.GetExtension(filePath);
var bareFileName = Path.GetFileNameWithoutExtension(filePath);
var path = GetFilePathStem(filePath);
var fileNameTemplate = $"{path}{bareFileName}{Suffix}{ext}";
return fileNameTemplate;
}
/// <summary>
/// Get the stem of the file path, everything before the actual filename.
/// It assumes that the last part of the path after the
/// final Directory Seperator character is the filename
/// </summary>
/// <param name="filePath"></param>
/// <returns></returns>
public static string GetFilePathStem(string filePath)
{
var path = "";
var endofPath1 = filePath.LastIndexOf(Path.DirectorySeparatorChar);
var endofPath2 = Path.AltDirectorySeparatorChar !=
Path.DirectorySeparatorChar ? filePath.LastIndexOf(Path.AltDirectorySeparatorChar) : -1;
var endofPath = Math.Max(endofPath1, endofPath2);
if (endofPath > 0)
{
path = filePath.Substring(0, endofPath + 1);
}
else // When the root does not use a directory separator char eg. "c:filename.pdf"
{
var root = Path.GetPathRoot(filePath);
path = root;
}
return path;
}
</pre>R. Bovillhttp://www.blogger.com/profile/13806761971999862684noreply@blogger.com0tag:blogger.com,1999:blog-12176634.post-15365256521672477332023-01-08T17:06:00.009+00:002023-01-08T17:12:51.108+00:00Powershell AfterLogin & CheckIpAddressChangeAfterLogin.ps1<br/>
<pre class="prettyprint lang-ps">
#$host.UI.RawUI.ForegroundColor = "Green"
Write-Host "*** After Login Prepartion ***" -Foregroundcolor Green
$host.UI.RawUI.WindowTitle = "*** After Login Prepartion ***"
Set-Variable -Name "AzureStorageEmulatorExe" -Value "C:\Program Files (x86)\Microsoft SDKs\Azure\Storage Emulator\AzureStorageEmulator.exe"
Write-Output "*** Start Azure Storage Emulator ***"
& $AzureStorageEmulatorExe start
#Write-Host "*** LoggING into Azure ***" -Foregroundcolor Green
#& az login --tenant 12345567-8910-47ff-0a65-2765a99ebbce
#Write-Host "*** LoggED into Azure ***" -Foregroundcolor Green
& $AzureStorageEmulatorExe status
& "C:\Users\...\Documents\BatchFiles\CheckIpAddressChange.ps1"
Pause
</pre>
CheckIpAddressChange.ps1
<pre class="prettyprint lang-ps">$currIpAddress = (Invoke-WebRequest -uri https://ifconfig.me/ip).Content
$prevIpAddress = "155.15.12.31"
$arkpdFirewallAccess = https://.../resource/subscriptions/resourceGroups/networking
#$host.UI.RawUI.ForegroundColor = "Green"
Write-Output "*** Checking for IP Address Change ***" -Foregroundcolor Green
Write-Output "My current IP address: $($currIpAddress)"
Write-Output "My previous IP address: $($prevIpAddress)"
$lastDifferentIpAddress = "15.17.107.14"
Write-Output "My last different IP address: $($lastDifferentIpAddress)"
Write-Output "if it changes, re-run ~\Scripts\IpRestrictionTool\allow-my-ip.ps1"
Write-Output "Also navigate to '$($arkpdFirewallAccess)' to update the firewall rule"
</pre>R. Bovillhttp://www.blogger.com/profile/13806761971999862684noreply@blogger.com0tag:blogger.com,1999:blog-12176634.post-47986736615878498302023-01-08T16:54:00.006+00:002023-01-27T06:51:24.394+00:00Powershell - Find My Ip Address<pre class="prettyprint lang-ps">
Clear
$currIpAddress = (Invoke-WebRequest -uri https://ifconfig.me/ip).Content
$prevIpAddress = "155.171.12.2"
Write-Output ""
Write-Output "My current IP address: $($currIpAddress)"
Write-Output "My previous IP address: $($prevIpAddress)"
Write-Output "if it changes, re-run ~\Scripts\IpRestrictionTool\allow-my-ip.ps1"
</pre>R. Bovillhttp://www.blogger.com/profile/13806761971999862684noreply@blogger.com0tag:blogger.com,1999:blog-12176634.post-58221207208382340612022-12-11T10:11:00.006+00:002023-01-27T06:49:16.152+00:00Json Parsing Tests - ExpandoObject or Json strings<p>Setting Up Jason Parsing Tests Using Dynamic Objects Or Strings</p>
<pre class="prettyprint lang-cs">
[Test]
public void SomeTestUsingExpandoObject()
{
// arrange
const string getItemEventDataValue = @"{
""_PrimaryId"": ""F:\\dasda\\ToMO-005775.json"",
""_SecondaryId"": ""123456"",
""RetrievalResult"": ""Processed Successfully""
}";
<b>dynamic</b> indexItemRaw = new <b>ExpandoObject</b>();
indexItemRaw.id = sourceIndexItemId;
indexItemRaw.version = sourceIndexItemVersion.ToString(CultureInfo.InvariantCulture);
<b>dynamic</b> source = new <b>ExpandoObject</b>();
source.getItemEventData = getItemEventDataValue;
indexItemRaw.source = source;
// act
IEnumerable<ClaimDto> claimsEnum = someMapper.Map(JObject.FromObject(indexItemRaw));
var claimDtos = claimsEnum.ToList();
//assert
...
}
[Test]
public void SomeTestUsingJSonStringOnly()
{
//How to set up unit tests using strings to set up the data, instead of dynamic objects.
//Sometimes it can be simpler, especially if you retrieve the raw data from a run
//Here is the above test, arranged using a string as an example
//In this case I thought it was more complicated to set everything up as a string
//(because one of the json string object entries (the "getItemEventData") was itself a json string)
// arrange
string rawJToken = @"
{
""id"": ""{sourceIndexItemId}"",
""version"": ""1.0"",
""source"": {
""getItemEventData"": ""{
\""PrimaryId\"": \""F:\\\\dasda\\\\ToMO-005775.json\"",
\""SecondaryId\"": \""123456\""
\""RetrievalResult\"": \""Processed Successfully\""
}""
}
}";
rawJToken = rawJToken.Replace("{sourceIndexItemId}", sourceIndexItemId);
JToken jtoken = JToken.Parse(rawJToken);
//act
IEnumerable<ClaimDto> claimsEnum = someMapper.Map(jtoken);
var claimDtos = claimsEnum.ToList();
//assert
...
}
</pre>R. Bovillhttp://www.blogger.com/profile/13806761971999862684noreply@blogger.com0tag:blogger.com,1999:blog-12176634.post-80203543494035485252022-12-04T08:55:00.018+00:002022-12-11T09:58:24.367+00:00Plant UML<p>Quite often I need to examine some existing code to find a solution to a problem. Sometimes this requires going in detail about what calls what and when. However, usually the code your looking at has many calls to many other objects. We don't want the details of every single call, just the important ones. Here sequence diagrams are very useful to document this visually. I prefer to use PlantUML for this because it allows you to write the diagram as text, PlantUml takes care of spacing everything appropriately. You can just get your object name/type and the method called on it from your IDE.<br/>PlantUML is found at this web address: <a href="https://www.plantuml.com/">https://www.plantuml.com/</a> .<br/>
Here is some good documentation on how to use it: <a href="https://crashedmind.github.io/PlantUMLHitchhikersGuide/">https://crashedmind.github.io/PlantUMLHitchhikersGuide/</a> .<br/>
Here is an example of using Plant UML to create a sequence diagram in UML:</p>
<textarea readonly="readonly" cols="110" rows="39" name="plantUml">
@startuml
actor Requestor as Actor
participant MyApiController #9999ff
'note right of MyApiController
'All Api methods go through TryProcessRequest()
'end note
Actor -/ MyApiController: Indexing
note left: Normally this would be\ninitiated by a message\non the ServiceBus
activate MyApiController
MyApiController -> MyApiController: TryProcessRequest
activate MyApiController
note right: All Api methods go\nthrough TryProcessRequest()
participant ServiceBus #99FF99
MyApiController -/ ServiceBus : PublishToTopic("topic.fromAlpha", IndexingCommand)
deactivate MyApiController
deactivate MyApiController
ServiceBus -/ MyIndexingCommandHandler : HandleMessageAsync ()
activate MyIndexingCommandHandler 
MyIndexingCommandHandler -> Indexer : RunIndexingJobAsync()
activate Indexer
note right: Number of retries of this method\nis controlled by SystemSetting\n"MyIndexingRetries", "FoobarService", 10\nIt is using the default value of 10!
Indexer -> MyIndexingCommandHandler : IndexMyItem()
note right: IndexMyItem is invoked as a task\nwith a timeout controlled by SystemSetting\n"MyIndexingTimeoutSeconds", "FoobarService", 600\nIt is being set to 3 minutes (180).
MyIndexingCommandHandler -> Indexer : DoIndexingAsync()
Indexer -> IndexingOperations : AddOrUpdateAsync()
activate IndexingOperations
IndexingOperations -[#red]> IIndexRepository : SaveAsync()
deactivate IndexingOperations
deactivate Indexer
deactivate MyIndexingCommandHandler
ServiceBus -[#red]/ UpdateIndexingSourceHandler : HandleMessageAsync<IndexingUpdateEvent>()
note left #FFAAAA: Only if the\nSaveAsync()\ncall succeeds
@enduml
</textarea>
<p>Here is the resulting image, click on it to open it full size:</p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7Aj3JU7bHeyoqEeqmCj60O4zRUJF2HMwIOUC0MnFcdvRSZujzWuFdCKsE1AoU_dER7XHIJdBP2zzhGXOqCTl4wNitw0flgN35rXTnSofGIowKUPh-GV-K7Un58ZYnMqDT9td46inZRzKCz-vk_dwoEBPsL_2E48Dy5UjnyitoVonAJZmbpCI/s1660/SamplePlantUml.png" style="display: block; padding: 1em 0; text-align: center; clear: left; float: left;"><img alt="" border="0" width="600" data-original-height="668" data-original-width="1660" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7Aj3JU7bHeyoqEeqmCj60O4zRUJF2HMwIOUC0MnFcdvRSZujzWuFdCKsE1AoU_dER7XHIJdBP2zzhGXOqCTl4wNitw0flgN35rXTnSofGIowKUPh-GV-K7Un58ZYnMqDT9td46inZRzKCz-vk_dwoEBPsL_2E48Dy5UjnyitoVonAJZmbpCI/s600/SamplePlantUml.png"/></a></div>
R. Bovillhttp://www.blogger.com/profile/13806761971999862684noreply@blogger.com0tag:blogger.com,1999:blog-12176634.post-1837516297103938282022-12-04T08:01:00.006+00:002022-12-04T08:13:36.708+00:00Linqpad Notepad++ Hyperlink ExtensionWhen searching through some files using LinqPad it can be useful to print out the results as a hyperlink that will open the file in Notepad++ at a particular line number.
<pre class="prettyprint lang-cs">
public static class MyExtensions
{
// Write custom extension methods here. They will be available to all queries.
private const string notePadppPath = @"C:\Program Files\Tools\Notepad++\notepad++.exe";
public static Hyperlinq CreateNotePadppHyperLink(this string filePath, int lineNumber)
{
var filelink = new Hyperlinq(() => Process.Start(notePadppPath, filePath + " -n" + lineNumber.ToString()), filePath);
return filelink;
}
}
</pre>
Usage
<pre class="prettyprint lang-cs">
string filePath = .... ;
filePath.CreateNotePadppHyperLink(lineNumber);
</pre>R. Bovillhttp://www.blogger.com/profile/13806761971999862684noreply@blogger.com0tag:blogger.com,1999:blog-12176634.post-16363189247941382392022-08-08T21:53:00.007+01:002022-08-12T17:09:26.190+01:00String Replace with StringComparisonI found this <a href="https://stackoverflow.com/questions/244531/is-there-an-alternative-to-string-replace-that-is-case-insensitive/13847351#comment31063745_244933" target="_blank">https://stackoverflow.com/questions/244531/is-there-an-alternative-to-string-replace-that-is-case-insensitive/13847351#comment31063745_244933</a> I added a few defensive checks.
<pre class="prettyprint lang-cs">
public static string Replace(
this string src,
string oldValue,
string newValue,
StringComparison comparison)
{
if (string.IsNullOrEmpty(oldValue))
throw new ArgumentException("String cannot be of zero length or null.", "oldValue");
if (string.IsNullOrEmpty(newValue)) // It can be an empty string though
throw new ArgumentException("String cannot be null.", "newValue");
// skip the loop entirely if oldValue and newValue are the same
if (string.Compare(oldValue, newValue, comparison) == 0)
return src;
if (oldValue.Length > src.Length)
return src;
int index = src.IndexOf(oldValue, comparison);
if (index == -1)
return src;
var sb = new StringBuilder(src.Length);
int previousIndex = 0;
while (index != -1)
{
sb.Append(src.Substring(previousIndex, index - previousIndex));
sb.Append(newValue);
index += oldValue.Length;
previousIndex = index;
index = src.IndexOf(oldValue, index, comparison);
}
sb.Append(src.Substring(previousIndex));
return sb.ToString();
}
</pre>R. Bovillhttp://www.blogger.com/profile/13806761971999862684noreply@blogger.com0tag:blogger.com,1999:blog-12176634.post-11471208179610917602022-07-12T16:37:00.008+01:002022-07-12T16:38:46.966+01:00xUnit vs MSTest vs NUnitThere is a good comparison of the 3 unit test libraries:<br/>
<br/>
<a href="https://xunit.net/docs/comparisons" target="_blank">https://xunit.net/docs/comparisons</a>R. Bovillhttp://www.blogger.com/profile/13806761971999862684noreply@blogger.com0tag:blogger.com,1999:blog-12176634.post-84282082257963032772022-06-06T15:07:00.014+01:002022-06-21T11:59:57.803+01:00EventAggregator in Javascript<p>I found an EventAggregator in Javascript here: <a href="https://davidwalsh.name/pubsub-javascript" target="_blank">https://davidwalsh.name/pubsub-javascript</a>. It was nice and simple, but it was using a hidden javascript object to map the topic string to an array of listeners. I thought that a Map is more appropriate for this functionality so I changed the code to use one.</p>
<p> I do a lot of logging because this class tends to be the thing that fires all the domain events in a project, so logging here is very useful. I would like to convert it to Typescript some time. In my case the events were mostly sent to the Controller portion of MVC controllers which would decide if they needed to update their Models/Views</p>
<pre><code class="language-js">
"use strict";
// Originally from here: https://davidwalsh.name/pubsub-javascript,
// changed to use the Map class
// Allows objects to publish and subscribe to custom events/topics
// Changed to use a Map class rather than using an object
var eventAggregator = (function () {
// maps a topic string to an array of subscriber/listener functions,
// each subscriber/listener function takes a single data object parameter
var _topicsMap = new Map();
var _publishingEnabled = false;
return {
// topic is a string, listener is a function that takes a
// single data object parameter
Subscribe: function (topic, listener) {
// Create the topic's object if not yet created,
// the key is the topic string, the value is an empty array (of listeners)
if (!_topicsMap.has(topic)) _topicsMap.set(topic, []);
// Add the listener to the array
var index = _topicsMap.get(topic).push(listener) - 1;
// Provide handle back for removal of topic
return {
Dispose: function () {
delete _topicsMap.get(topic)[index];
}
};
},
Enabled: function (isEnabled) {
if (_publishingEnabled != isEnabled) {
_publishingEnabled = isEnabled;
let isEnabledStr = _publishingEnabled ? "enabled" : "disabled";
console.log("Events publishing is " + isEnabledStr);
}
},
Publish: function (topic, data) {
// If publishing is disabled, or the topic doesn't exist,
// or there's no listeners in the array, just leave
if (!_publishingEnabled) {
console.log("'" + topic + "' Events: Publishing is disabled");
return;
}
else if (!_topicsMap.has(topic)) {
console.warn("Events: Either the topic '" + topic +
"' doesn't exist, or there's no listeners in queue");
return;
}
let dataSafe = (data) ? data : "";
if (typeof dataSafe === 'string' || dataSafe instanceof String) {
console.log("Events: event raised on topic: '"
+ topic + "' with data '" + dataSafe + "'");
} else {
console.log("Events: event raised on topic: '"
+ topic + "' with '" + JSON.stringify({ data }) + "'");
}
// Cycle through topics array, invoking each listener function
_topicsMap.get(topic).forEach(function (item) {
item(data != undefined ? data : {});
});
}
};
})();
</code></pre>R. Bovillhttp://www.blogger.com/profile/13806761971999862684noreply@blogger.com0tag:blogger.com,1999:blog-12176634.post-45089391705572948592022-05-05T09:41:00.009+01:002022-06-06T19:42:32.154+01:00IWebHostEnvironment MapPath ExtensionBrings MapPath() to .NET Core
<pre><code class="language-csharp" name="code">
public static class IWebHostEnvironmentExtender
{
public static string MapPath(
this IWebHostEnvironment self, string path)
{
var res = path;
var ix = path.IndexOf('~');
if (ix == 0)
{
res = path.Replace("~", self.WebRootPath);
}
return res;
}
}
</code>
</pre>R. Bovillhttp://www.blogger.com/profile/13806761971999862684noreply@blogger.com0tag:blogger.com,1999:blog-12176634.post-50803587871356946892022-05-05T07:06:00.005+01:002022-06-09T14:52:50.836+01:00FileSystemWatcher ExtensionsWe can use extension classes to allow React to be used with other FileSystemWatcher usage cases:<br/>
<pre class="prettyprint lang-cs">
public static class FileSystemWatcherExtender
{
// Returns an IObservable, which is effectively a publisher
// which can be subscribed to and disposed of when finished with
public static IObservable<FileSystemEventArgs>
GetFileChangedPublisher(this FileSystemWatcher fileSystemWatcher)
{
Debug.Assert(fileSystemWatcher != null);
var result = Observable
.FromEventPattern<FileSystemEventHandler, FileSystemEventArgs>(
ev => fileSystemWatcher.Changed += ev,
ev => fileSystemWatcher.Changed -= ev)
.Select(ev => ev.EventArgs);
return result;
}
// Use this to subscribe to FileSystemWatcher errors
public static IObservable<ErrorEventArgs>
GetErrorPublisher(this FileSystemWatcher fileSystemWatcher)
{
Debug.Assert(fileSystemWatcher != null);
return Observable
.FromEventPattern<ErrorEventHandler, ErrorEventArgs>(
ev => fileSystemWatcher.Error += ev,
ev => fileSystemWatcher.Error -= ev)
.Select(x => x.EventArgs);
}
}
</pre>
And to use it<br/>
<pre class="prettyprint lang-cs">
FileSystemWatcher filesystemwatcher = BuildMyFileSystemWatcher();
...
// Subscribed to filesystem watcher changes
// where "onChanged" is a Func<FileSystemEventArgs>
IDisposable subscription = fileSystemWatcher.GetFileChangedPublisher().Subscribe(onChanged);
...
subscription.Dispose(); // Finish with our subscription
...
</pre>R. Bovillhttp://www.blogger.com/profile/13806761971999862684noreply@blogger.com0tag:blogger.com,1999:blog-12176634.post-82095175365158851662022-05-04T17:11:00.014+01:002022-08-06T06:01:27.837+01:00A File Change Monitor Class in C#Wrote this File changes monitor service to monitor 1 or more files in a particular directory for file write accesses.<br/>
<pre class="prettyprint lang-cs">
public class FileChangedEvent
{
public FileChangedEvent(string fullPath, string fileName,
WatcherChangeTypes changeType)
{
FullPath = fullPath;
FileName = fileName;
ChangeType = changeType;
}
// Full file path and name
public string FullPath { get; private set; }
// File name only
public string FileName { get; private set; }
// The type of change
public WatcherChangeTypes ChangeType { get; private set; }
}
public interface IFileChangeMonitor
{
void Dispose();
string DirectoryPath { get; }
void Start();
public bool IsActive { get; }
void Stop();
IDisposable SubscribeOnChanged(string fileName,
Action<FileChangedEvent> onChanged, double throttle_ms = 100);
IDisposable SubscribeOnError(Action<ErrorEventArgs> onError);
}
public class FileChangeMonitor : IDisposable, IFileChangeMonitor
{
// https://weblogs.asp.net/ashben/31773
private readonly FileSystemWatcher _fileSystemWatcher;
private readonly DirectoryInfo _directory; // directory being monitored
private const string DefaultFileFilter = @"*.json";
// TODO: add NotifyFilter parameter
public FileChangeMonitor(DirectoryInfo dir,
string defaultFileFilter = DefaultFileFilter)
{
//const int MAX_NUM_FILES_TO_MONITOR = 10;
//const int AVG_FILE_NAME_LENGTH = 30;
//const int FOUR_KB = 1024 * 4;
Debug.Assert(dir != null);
Debug.Assert(dir.Exists == true);
_directory = new DirectoryInfo(dir.FullName);
//int bufferSize = ((MAX_NUM_FILES_TO_MONITOR *
// (16 + AVG_FILE_NAME_LENGTH * 2) % FOUR_KB) + 1) * FOUR_KB;
_fileSystemWatcher = new FileSystemWatcher(_directory.FullName)
{
//InternalBufferSize = bufferSize, default is enough
NotifyFilter = NotifyFilters.CreationTime | NotifyFilters.LastWrite,
Filter = defaultFileFilter,
IncludeSubdirectories = false
};
}
public string DirectoryPath => _directory?.FullName;
public IDisposable SubscribeOnChanged(string fileName,
Action<FileChangedEvent> onChanged,
double throttle_ms = 200)
{
Debug.Assert(_directory?.Exists == true);
Trace.WriteLine($"*** Subscribing for changes to
{Path.Combine(DirectoryPath, fileName)}");
return GetFileChangedPublisher(fileName, throttle_ms)
.Subscribe(onChanged);
}
public void Start()
{
Debug.Assert(_directory?.Exists == true);
_fileSystemWatcher.EnableRaisingEvents = true;
}
public bool IsActive => _fileSystemWatcher.EnableRaisingEvents;
public void Stop()
{
Debug.Assert(_directory?.Exists == true);
_fileSystemWatcher.EnableRaisingEvents = false;
}
public void Dispose()
{
if (IsActive)
Stop();
GC.SuppressFinalize(this);
_fileSystemWatcher.Dispose();
}
// Returns an IObservable, which is effectively a publisher which
// can be subscribed to and disposed of when finished with
private IObservable<FileChangedEvent> GetFileChangedPublisher(
string targetFileName,
double throttle_ms = 200)
{
Debug.Assert(_directory?.Exists == true);
IObservable<EventPattern<FileSystemEventArgs>> fswCreated =
Observable.FromEventPattern<FileSystemEventArgs>(_fileSystemWatcher, "Changed");
return fswCreated.
Where((EventPattern<FileSystemEventArgs> ev) => ev.EventArgs.Name == targetFileName).
// Rx Extension Sample() will only allow the last event within a time
// interval to pass through. This effectively throttles events
Sample(TimeSpan.FromMilliseconds(throttle_ms)).
Select(ev => new FileChangedEvent(ev.EventArgs.FullPath, ev.EventArgs.Name, ev.EventArgs.ChangeType));
}
private IObservable<ErrorEventArgs> GetErrorPublisher()
{
Debug.Assert(_directory?.Exists == true);
return Observable
.FromEventPattern<ErrorEventHandler, ErrorEventArgs>(
ev => _fileSystemWatcher.Error += ev,
ev => _fileSystemWatcher.Error -= ev)
.Select(x => x.EventArgs);
}
public IDisposable SubscribeOnError(Action<ErrorEventArgs> onError)
{
Debug.Assert(_directory?.Exists == true);
return GetErrorPublisher().Subscribe(onError);
}
}
</pre>
<p>The thing is it is an example of how to integrate React with a file system watcher or an existing class that publishes events using standard C# events. Here is an example of how to use the service</p>
<pre class="prettyprint lang-cs">
DirectoryInfo dir = new DirectoryInfo("\\Some\Directory");
FileChangeMonitor datafileMonitor = new FileChangeMonitor(dir);
datafileMonitor.Start();
...
// Subscribed to just 1 file but we could subscribe to multiple in the same directory
IDisposable subscription = datafileMonitor.SubscribeOnChanged(
"FileToMonitor.json",
fce => DoSomethingWith(fce));
...
subscription.Dispose(); // Finish with our subscription to a specific file's changes
...
datafileMonitor.Stop(); // Optionally we could just call just Dispose()
datafileMonitor.Dispose();
</pre>
<p>We could change the class so that the type of changes monitored could be passed in through the class constructor (the NotifyFilter enumeration). Also, an option to change the internal buffer size could be required.</p>R. Bovillhttp://www.blogger.com/profile/13806761971999862684noreply@blogger.com0tag:blogger.com,1999:blog-12176634.post-85612205829725246992022-05-01T08:42:00.016+01:002022-08-12T17:12:16.523+01:00Logging Assertions in ASP .Net CoreSometimes it useful to extend the logging system so that you can log assertions/code contracts.
Simple logger add-on so that assertions/code contracts can be logged in ASP.Net:<br/>
<pre class="prettyprint lang-cs">
using System.Runtime.CompilerServices;
public static class ILoggerAssertionExtender
{
public static bool Assert(this ILogger logger,
Func<bool> predicate,
Func<string> message)
{
bool assertion = predicate();
if (!assertion)
{
logger.LogError("Code contract/assertion failed :" + message());
}
// The assertion outcome is returned so the user can react to it (throw an exception,
// insert a DebuggerBreak(), ...) after the logging is done.
return assertion;
}
// This one adds Caller attributes to get more details
public static bool CallerAssert(this ILogger logger,
Func<bool> predicate,
Func<string> message,
[CallerMemberName] string member = "",
[CallerFilePath] string file = "",
[CallerLineNumber] int line = -1)
{
bool assertion = predicate();
if (!assertion)
{
logger.LogError($"Code contract/assertion failed in Member {member}, File {file}, Line {line}: " + message());
}
// The assertion outcome is returned so the user can react to it (throw an exception,
// insert a DebuggerBreak(), ...) after the logging is done.
return assertion;
}
}
</pre>R. Bovillhttp://www.blogger.com/profile/13806761971999862684noreply@blogger.com0tag:blogger.com,1999:blog-12176634.post-44076329295326209382022-04-28T11:05:00.009+01:002022-05-01T10:46:55.075+01:00Sample HostApplicationLifetime Events Class for ASP .Net Core<p>There are some environments where you are using an underlying technology to assist you, for example, WPF, Win Forms, and ASP.NET. It is useful sometimes in these environments to be able to initialise application code just after the underlying technology has been initialised, and have some termination/finalisation code invoked just before the underlying technology is unwound.</p>
<p>In my case, I had to call a service that started a FileSystemWatcher when the web hosting service started and stopped it when the web hosting service was about to end. These events are very important in the hosts lifetime, so I added logging to record them. I also used my own application configuration file which was beyond that provided by the ASP.Net Core services, but the location of which was set within it.
</p>
Here is a class to do this in .NET Core:
<pre class="csharp" name="code">
/// <summary>
/// This class tells you when the Web Application
/// Starts (just prior to listening for web requests)
/// and Stops and has Stopped, so you can start things up
/// and shut them down at the end if they fit that kind of pattern
/// https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host?view=aspnetcore-3.0#ihostapplicationlifetime
/// </summary>
internal class WebApplicationLifetimeEvents : IHostedService
{
private readonly ILogger _logger;
private readonly IHostApplicationLifetime _appLifetime;
private readonly IWebHostEnvironment _webhostEnvironment;
public WebApplicationLifetimeEvents(
ILogger<WebApplicationLifetimeEvents> logger,
IHostApplicationLifetime appLifetime,
IWebHostEnvironment webhostEnvironment,
)
{
_logger = logger;
_appLifetime = appLifetime;
_webhostEnvironment = webhostEnvironment;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_appLifetime.ApplicationStarted.Register(OnStarted);
_appLifetime.ApplicationStopping.Register(OnStopping);
_appLifetime.ApplicationStopped.Register(OnStopped);
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
private void OnStarted()
{
_logger.LogInformation("OnStarted has been called.");
// Perform post-startup activities here:
try
{
}
catch (Exception ex)
{
_logger.LogError($"Exception in OnStarted {ex}");
throw;
}
}
private void OnStopping()
{
_logger.LogInformation("OnStopping has been called.");
// Perform on-stopping activities here
}
private void OnStopped()
{
_logger.LogInformation("OnStopped has been called.");
// Perform post-stopped activities here
}
}
</pre>
Then to use the service:
<pre class="csharp" name="code">
services.AddSingleton<IHostedService, WebApplicationLifetimeEvents> ();
</pre>R. Bovillhttp://www.blogger.com/profile/13806761971999862684noreply@blogger.com0tag:blogger.com,1999:blog-12176634.post-34040032424098716532021-08-26T12:05:00.013+01:002021-08-26T13:30:07.174+01:00Exposing C# Events using Reactive Extensions<p>There is a way to convert existing C# events to an IObservable<>. There is a good article <a href="https://rehansaeed.com/reactive-extensions-part1-replacing-events/" target="_blank">here</a> on this</p>
<pre class="csharp" name="code">public class SimpleEventSource
{
public event EventHandler<MyEventArgs> SimpleEvent;
// Normally this would be private (event is triggered internally)
// but for test purposes we make it public
public void FireSimpleEvent(MyEvent myevent)
{
EventHandler<MyEventArgs> tmp = SimpleEvent;
if (tmp != null)
{
tmp(this, new MyEventArgs(myEvent));
}
}
<b>
// This returns an IObservable (effectively an event pulisher)
public IObservable<EventPattern<MyEventArgs>> MyEventPublisher
{
get
{ // To consume SimpleEvent as an IObservable:
IObservable<EventPattern<MyEventArgs>> eventAsObservable = Observable.
FromEventPattern<MyEventArgs>(
ev => SimpleEvent += ev,
ev => SimpleEvent -= ev);
return eventAsObservable;
}
}
</b>
}
</pre>
<p>Note that MyEventArgs is something derived from EventArgs that allows access to the "MyEvent" instance, see <a href="#EventClasses">below</a>.
Here is a tester:</p>
<pre class="csharp" name="code">public void TestSimpleEventSource()
{
var ses = new SimpleEventSource();
ses.FireSimpleEvent(new MyEvent("1st event")); // This one is missed
using (var subscription = ses.MyEventPublisher.Subscribe(
ev => Console.WriteLine(ev.EventArgs.MyEvent.Text + " fired")))
{
ses.FireSimpleEvent(new MyEvent("2nd event"));
ses.FireSimpleEvent(new MyEvent("3rd event"));
}
Console.WriteLine("Subscription cancelled");
}
</pre>
and the output
<br />
<pre>2nd event fired
3rd event fired
Subscription cancelled
</pre>
<h4 style="text-align: left;">
Replacing C# Events using Reactive Extensions</h4>
<p>Lets take this one step further, and completely replace the C# event with a reactive style event publisher</p>
<pre class="csharp" name="code">
internal class SimpleEventSource
{
private Subject<MyEvent> _myEventSubject = new Subject<MyEvent>();
public IObservable<MyEvent> MyEventPublisher => this._myEventSubject.AsObservable();
public void FireSimpleEvent(MyEvent myEvent) => this._myEventSubject.OnNext(myEvent);
}
</pre>
And the same test but using this class:<br />
<pre class="csharp" name="code">
public void TestSimpleEventSource2()
{
var ses = new SimpleEventSource2();
ses.FireSimpleEvent(new MyEvent("1st event"));
using (var subscription = ses.MyEventPublisher.Subscribe(
ev => Console.WriteLine(ev.Text + " fired")))
{
ses.FireSimpleEvent(new MyEvent("2nd event"));
ses.FireSimpleEvent(new MyEvent("3rd event"));
}
Console.WriteLine("Subscription cancelled");
}
</pre>
<a name="EventClasses"></a>
<p>Here is the code for the simple event classes if it makes things clearer</p>
<pre class="csharp" name="code">
public class MyEvent
{
public MyEvent(string text)
{
Text = text;
}
public string Text { get; private set; }
}
public class MyEventArgs : EventArgs
{
public MyEventArgs(MyEvent myEvent)
: base()
{
MyEvent = myEvent;
}
public MyEvent MyEvent { get; private set; }
}
</pre>R. Bovillhttp://www.blogger.com/profile/13806761971999862684noreply@blogger.com0tag:blogger.com,1999:blog-12176634.post-75865035835375808552021-08-20T10:10:00.010+01:002023-07-09T07:05:18.650+01:00Stopwatch Extension<p>
Some simple code to use a Stopwatch class to measure how long a piece of code takes to run. With this extension you can forget about the Stopwatch syntax:
</p>
<pre class="csharp" name="code">
namespace Utilities.StopwatchExtensions
{
public static class StopwatchExtender
{
public static TimeSpan MeasureRunTime(this Action codeToRun)
{
var stopWatch = Stopwatch.StartNew();
codeToRun();
stopWatch.Stop();
return stopWatch.Elapsed;
}
}
}
</pre>
<p>
From .NET 7.0 you can use this form:
</p>
<pre class="csharp" name="code">
namespace Utilities.StopwatchExtensions
{
public static class StopwatchExtender
{
public static TimeSpan MeasureRunTime(this Action codeToRun)
{
long startTime = Stopwatch.GetTimestamp();;
codeToRun();
TimeSpan elapsedTime = Stopwatch.GetElapsedTime(startTime);
}
}
}
</pre>
Remember to wrap the extension in a specific isolating namespace otherwise the intelli-editor in Visual Studio will get polluted with unused extension methods.
To use:<br/>
<pre class="csharp" name="code">
Action codeToRun = () => { /* code whose execution time needs measuring */};
var runTimeMs = codeToRun.MeasureRunTime().TotalMilliseconds;
Trace.WriteLine("*** Time to complete task: " + runTimeMs + " ms");
using (var process = Process.GetCurrentProcess())
{
Trace.WriteLine($"*** Execution time: {runTimeMs.ToString()} ms");
Trace.WriteLine($"*** Gen-0: {GC.CollectionCount(0).ToString()}");
Trace.WriteLine($"*** Gen-1: {GC.CollectionCount(1).ToString()}");
Trace.WriteLine($"*** Gen-2: {GC.CollectionCount(2).ToString()}");
Trace.WriteLine($"*** Peak WrkSet: {process.PeakWorkingSet64.ToString("n0")}");
}
</pre>R. Bovillhttp://www.blogger.com/profile/13806761971999862684noreply@blogger.com0tag:blogger.com,1999:blog-12176634.post-10122058872355630742021-08-19T10:36:00.015+01:002023-02-16T07:28:07.458+00:00C# Event Aggregators<p>Did some research into event aggregators. They are a great way to allow events to be fired betwen classes without the classes being tightly coupled, in other words without either class having to know about the other. Standard C# events make the classes tighly coupled, the listener needs to know about the firer. Sometimes with standard c# events, an event will need to be transmitted through an intermediate sequence of objects just to reach the target recipient, this is very cumbersome and laborious. Event aggregators completely decouple the event publishers from the event subscribers as long as both subscriber and publisher objects can access the event aggregator their is no need for intermediate objects to relay the events</p>
<p>
Would make an excellent start for a simulator which has both hardware and software events occuring, an event aggregator could be used to input events to the system. As it is completely decoupled from the system, it would be easy to emulate firing these software events say from a GUI for emulation purposes, and also easy to connect to real hardware to populate those same software events.
</p>
<p>I found several links that use a Reactive Extensions to implement an Event Aggregator. Using the Reactive Extensions the code to implement an aggregator is very small and compact.
<ol>
<li><a href="https://mikebridge.github.io/post/csharp-domain-event-aggregator" target="_blank">https://mikebridge.github.io/post/csharp-domain-event-aggregator</a></li>
</ol>
It is also a great example in the usage of Reactive Extensions.
Currently I have combined them into this form:</p>
<pre class="prettyprint lang-cs">
using System.Reactive.Linq; // You'll need the Reactive nuget package
using System.Reactive.Subjects;
...
public interface IRxEventAggregator : IEventAggregator, IDisposable
{
/// <summary>
/// Use this method to apply RX (LINQ style) expressions on
/// the events before subscribing
/// </summary>
/// <typeparam name="TEvent"></typeparam>
/// <returns></returns>
IObservable<TEvent> GetEventsObservable<TEvent>();
}
public interface IEventAggregator
{
IDisposable Subscribe<T>(Action<T> action);
void Publish<T>(T @event);
}
/// <summary>
/// An EventAggregator that is based on reactive extensions and supports
/// </summary>
public class RxEventAggregator
: IRxEventAggregator
{
readonly Subject<object> _subject = new Subject<object>();
// Use this method to use RX (LINQ style) expressions
// on the events before subscribing
public IObservable<TEvent> GetEventsObservable<TEvent>()
{
return _subject.OfType<TEvent>().
AsObservable();
}
public IDisposable Subscribe<T>(Action<T> action)
{
return GetEventsObservable<T>().
Subscribe(action);
}
public void Publish<TEvent>(TEvent sampleEvent)
{
_subject.OnNext(sampleEvent);
}
#region IDisposable
... // See below
#endregion
}</pre>
<p>Generally an Event Aggregator lives throughout the life of your software so it really does not need disposing of. You could remove the Dispose code completely. For that reason I list it here just for reference:</p>
<pre class="prettyprint lang-cs">
#region IDisposable
bool _disposed;
~RxEventAggregator()
{
Dispose(false);
// Useful for detecting memory leaks
Debug.Assert(false, nameof(RxEventAggregator) +
" was not disposed of");
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed) return;
_subject.Dispose();
_disposed = true;
}
#endregion
</pre>
<p>Here's another event aggregator:</p>
<pre class="prettyprint lang-cs">
// When there are a lot of events with lots of subscribers
public class RxEventAggregator
: IRxEventAggregator
{
private volatile ConcurrentDictionary<Type, object> _subjects
= new ConcurrentDictionary<Type, object>();
// Use this method to use RX (LINQ style) expressions
// on the events before subscribing
public IObservable<TEvent> GetEventsObservable<TEvent>()
{
Trace.Assert(!_disposed, "Trying to access a disposed EventAggregator");
var subject =
(ISubject<TEvent>)_subjects.GetOrAdd(typeof(TEvent),
_ => new Subject<TEvent>());
return subject.AsObservable();
}
public IDisposable Subscribe<T>(Action<T> action)
{
Trace.Assert(!_disposed, "Trying to access a disposed EventAggregator");
return GetEventsObservable<T>().
Subscribe(action);
}
public void Publish<TEvent>(TEvent sampleEvent)
{
Trace.Assert(!_disposed, "Trying to access a disposed EventAggregator");
if (_subjects.TryGetValue(typeof(TEvent), out var subject))
{
((ISubject<TEvent>)subject)
.OnNext(sampleEvent);
}
}
// If your Aggregator is a life (of the application) long one you can simply not bother with disposing of it
#region IDisposable
bool _disposed;
~RxEventAggregator2()
{
Debug.Assert(false, nameof(RxEventAggregator2) + " was not disposed of"); // Useful for detecting memory leaks
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this); // no need to call finalizer now
}
protected virtual void Dispose(bool disposing)
{
if (_disposed) return;
_disposed = true;
// Make them unaccessible
var hiddenSubjects = Interlocked.Exchange(ref _subjects, new ConcurrentDictionary<Type, object>());
// Then dispose of them
foreach (var subject in hiddenSubjects.Values.Cast<IDisposable>())
{
subject.Dispose();
}
}
#endregion
}
</pre>R. Bovillhttp://www.blogger.com/profile/13806761971999862684noreply@blogger.com0tag:blogger.com,1999:blog-12176634.post-85068130672761530362021-08-12T17:07:00.019+01:002021-08-23T15:17:19.787+01:00New Project File Format (VS 2017/2019)<p>There is a new alternative project file format from Visual Stuidio 2017 onwards. Checkout this description <a href="https://natemcmaster.com/blog/2017/03/09/vs2015-to-vs2017-upgrade/" target="_blank">here</a></p>
Here is an example of a WPF project file
<pre><Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net5.0-windows</TargetFramework>
<UseWPF>true</UseWPF>
</PropertyGroup>
</Project>
</pre>
<p>
Note the "TargetFramework" of <b>".net5.0-windows"</b> is required for WinForms, WPF applications. These are supported in .NET 5.0 but only as windows applications.
</p>
<p>
Here is a library project that will build to multiple .Net versions both core and frameworks
</p>
<pre>
<Project Sdk="Microsoft.NET.Sdk">
<!-- Main build info -->
<PropertyGroup>
<TargetFrameworks><span style="color: blue;">net48;net5.0</span></TargetFrameworks>
<PackageId>MyProject</PackageId>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<SignAssembly>false</SignAssembly>
</PropertyGroup>
<!-- Assembly Info -->
<PropertyGroup>
<AssemblyVersion>3.7.2.3</AssemblyVersion>
<Description>An implementation of MyProject.</Description>
<Authors>Joe Blogz</Authors>
<Copyright>2019 Magicsoft Corp.</Copyright>
<Company>Magicsoft Corp.</Company>
<PackageTags>Subject1;Subject2</PackageTags>
<PackageProjectUrl>https://myproject.com/url</PackageProjectUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageReleaseNotes>-</PackageReleaseNotes>
</PropertyGroup>
<!-- Repo Info -->
<PropertyGroup>
<RepositoryUrl>https://github.com/MyProject</RepositoryUrl>
<RepositoryType>git</RepositoryType>
</PropertyGroup>
<!-- Remove AssemblyInfo.cs from build (in Properties dir.) -->
<!-- it is now in the project file -->
<ItemGroup>
<Compile Remove="Properties\**" />
<EmbeddedResource Remove="Properties\**" />
<None Remove="Properties\**" />
</ItemGroup>
<!-- NUnit packages go here -->
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net5.0' ">
<Compile Remove="MyProject (net48)\**" />
</ItemGroup>
</Project>
</pre>
<p>
For multiple frame work targets<span style="color: #ffa400;"> <TargetFramework>..<TargetFramework></span> is replaced with <span style="color: #ffa400;"><TargetFramework<b>s</b>>...<TargetFramework<b>s</b>></span> Note that .Net farmeworks use the style <span style="color: #ffa400;"><b>netnn</b></span> where nn is the version but .Net version 5.0 onwards (which are .NET Core frameworks) use the form <span style="color: #ffa400;"><b>netn.n</b></span> where n.n represesnt the .NET version. See <a href="https://docs.microsoft.com/en-us/dotnet/standard/frameworks" target="_blank">https://docs.microsoft.com/en-us/dotnet/standard/frameworks</a> for all possible entries</p>
<p>
Note that the version info can be removed from the Assembly.cs and placed into the project file now.
Referencing Nuget packages is also simplified.
</p>
<p>
Projects can be migrated with the help of the <a href="https://github.com/hvanbakel/CsprojToVs2017" target="new">CsprojToVs2017 tool</a>. Don't bother downloading the source and building it yourself, instead install it quickly using the dotnet install tool. This is described in the <a href="https://github.com/hvanbakel/CsprojToVs2017#quick-start" target="_blank">Quick Start Section</a>. It will do most of the work of porting projects accross from the old format for you.
</p>
<p>
Some parts of a project file can be conditional, this feature is described <a href="https://markheath.net/post/csproj-conditional-references" target="_blank">here</a>. For example in the project above I had to exclude some files that would only build in .NET Framework 4.8 or less. I placed these files in a subdirectory "CommonCore (net48)" and exclude them from the .NET 5.0 build.
</p>
R. Bovillhttp://www.blogger.com/profile/13806761971999862684noreply@blogger.com0tag:blogger.com,1999:blog-12176634.post-34366090354875847642021-08-12T16:25:00.002+01:002021-08-12T16:25:30.192+01:00Task.IsCompletedSuccessfully Property Extension<p>
Task in all versions of the the .Net framework and .Net Standard 2.0 has a useful property missing. From .Net Standard 2.1 and through all versions of .Net Core the "IsCompletedSuccessfully" property is available. This will tell the use that the task completed without throwing an exeception or because it was cancelled. Well we can use an extension to make up for this missing property.
</p>
<pre class="csharp" name="code">
namespace System.Threading.Tasks
{
public static class TaskIsCompletedSuccessfullyExtension
{
public static <b>bool IsCompletedSuccessfully(this Task task)</b>
{
var value = task.IsCompleted && !(task.IsCanceled || task.IsFaulted);
return value;
}
}
}
</pre>
and to use:
<pre class="csharp" name="code">
using System.Threading.Tasks;
...
Task myTask = ...
// Note it is defined not as a property but as a method
if (myTask.<b>IsCompletedSuccessfully()</b>)
{
...
}
</pre>R. Bovillhttp://www.blogger.com/profile/13806761971999862684noreply@blogger.com0tag:blogger.com,1999:blog-12176634.post-50980709992424595912021-08-12T13:32:00.004+01:002021-08-12T13:33:15.350+01:00Using CallMemberAttribute in an INotifyPropertyChanged Base Class
Here is an sample base clase that makes implementing INotifyPropertyChanged easy. It is also a nice example of using templated methods on a class in C# without making the whole class templated.<br/>
<pre class="csharp" name="code">
using System.Runtime.CompilerServices;
...
public abstract class NotifyProperyChangedBase : INotifyPropertyChanged
{
/// <summary>
/// Multicast event for property change notifications.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Checks if a property already matches a desired value.
/// Sets the property and
/// notifies listeners only when necessary.
/// </summary>
/// <typeparam name="T">Type of the property.</typeparam>
/// <param name="storage">Reference to a property with
/// both getter and setter.</param>
/// <param name="value">Desired value for the property.</param>
/// <param name="propertyName">
/// Name of the property used to notify listeners. This
/// value is optional and can be provided automatically
/// when invoked from compilers that support CallerMemberName.
/// </param>
/// <returns>
/// True if the value was changed, false if the existing
/// value matched the desired value.
/// </returns>
protected bool SetProperty<T>(
ref T storage,
T value,
[CallerMemberName] String propertyName = null)
{
bool valueChanged = !EqualityComparer<T>.Default.Equals(storage, value);
if (valueChanged)
{
storage = value;
OnPropertyChanged(propertyName);
valueChanged = true;
}
return valueChanged;
}
/// <summary>
/// Checks if a property already matches a desired value.
/// Sets the property and notifies listeners only when
/// necessary.
/// </summary>
/// <typeparam name="T">Type of the property.</typeparam>
/// <param name="storage">Reference to a property
/// with both getter and setter.</param>
/// <param name="value">Desired value for the property.</param>
/// <param name="equalityComparer">
/// Specify the equality comparer.
/// For example if it is a non-case sensitive string you could use
/// StringComparer.CurrentCultureIgnoreCase</param>
/// <param name="propertyName">
/// Name of the property used to notify listeners. This
/// value is optional and can be provided automatically
/// when invoked from compilers that support CallerMemberName.
/// </param>
/// <returns>
/// True if the value was changed, false if the existing
/// value matched the desired value.
/// </returns>
protected bool SetProperty<T>(
ref T storage,
T value,
IEqualityComparer<T> equalityComparer,
[CallerMemberName] String propertyName = null)
{
bool valueChanged = !equalityComparer.Equals(storage, value);
if (valueChanged)
{
storage = value;
OnPropertyChanged(propertyName);
valueChanged = true;
}
return valueChanged;
}
/// <summary>
/// Notifies listeners that a property value has changed.
/// </summary>
/// <param name="propertyName">
/// Name of the property used to notify listeners. This
/// value is optional and can be provided automatically
/// when invoked from compilers that support
/// <see cref="CallerMemberNameAttribute" />.
/// </param>
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var eventHandler = this.PropertyChanged;
if (eventHandler != null)
{
eventHandler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
</pre>
and here is an example usage inside a ViewModel class:
<pre class="csharp" name="code">
private string _notifyIconToolTipText = "";
public string NotifyIconToolTipText
{
get
{
return _notifyIconToolTipText;
}
set
{
string newValue = value ?? "";
this.SetProperty(ref _notifyIconToolTipText, newValue);
}
}
</pre>R. Bovillhttp://www.blogger.com/profile/13806761971999862684noreply@blogger.com0tag:blogger.com,1999:blog-12176634.post-55266834431465917292021-07-31T21:07:00.006+01:002021-07-31T21:19:42.321+01:00System Defined Delegates<p>
<b>Action</b> - When you want a delegate for a member/method that may or may not take parameters (up to 16 parameters max, none as the minimum) and does not return anything. The pattern is <b>Action<T1, ..., T16></b>, so a member/method with 0 to 16 parameters that has no return value.
</p>
<p>
<b>
Func</b> - When you want a delegate for a member/method that may or may not take parameters (up to 16 parameters max, none as the minimum) but that always returns a result. The pattern is <b>Func<T1, ..., T16, TResult></b>, so a method with 0 to 16 parameters that returns a TResult.
</p>
<p>
<b>Predicate</b> - Defined as <b>public delegate bool Predicate<in T>(T myObject);</b>. A special delegate used by several methods of the Array and List<T> classes to search for elements in the collection. In other cases use a <b>Func<T, bool></b>
</p>R. Bovillhttp://www.blogger.com/profile/13806761971999862684noreply@blogger.com0