Skip to content
DUONG Phu-Hiep edited this page Aug 4, 2019 · 16 revisions

My collection of many small useful code-snippet .NET, wrapping in reusable library with unobtrusive dependencies. Some of the most-used library are published on nuget

This sample demonstrates How usefull ToolsPack.Net is.

Here is some brief introduction code. Navigate to the code and unit test samples for more information.

ToolsPack.Displayer

  • No dependencies
  • Use to display some C# object.

ArrayDisplayer

  • Know to convert a IEnummerable to string in order do display in a log message
var arr = new string[1000] {"item1".."item1000"};
arr.Display().SeparatedBy("; ").MaxItems(4)

gives

{ item1; item2; item3; item4; ..and 996 (of 1000) more }
  • If some items in the array are very long. We should limit the length of individual item with MaxItemLength() so that long items will be displayed with ... ellipsis
var arr = new string[1000] {"Lorem ipsum kidda foom", "item2".."item1000"};
arr.Display().MaxItems(4).MaxItemLength(10)

gives

{ [[Lorem...]], item2, item3, item4, ..and 996 (of 1000) more }
  • Fast performance it only iterate neccessary items once (complexity O(N))
  • see more functionalities in code and test

StopwatchDisplayer

convert Stopwatch to string

Stopwatch sw;
Console.WriteLine(sw.DisplayMili()); //get the display string in mili seconds "103 ms"
Console.WriteLine(sw.DisplayMicro()); //get the display string in micro seconds "103,000 mcs"
Console.WriteLine(sw.Display()); //automaticly choose a time unit (day, hour, minute, seconde..) to display

Tips

For some serializable object which don't implement ToString(). Newtonsoft.Json can convert them to json. DO NOT use this technique on production. The reflection is bad for Perf..

Newtonsoft.Json.JsonConvert.SerializeObject(someObject);

Ellipsis

ArrayDisplayer.DefaultEllipsis("1234567890", 4, "..."); //gives "1234..."
ArrayDisplayer.WordEllipsis("123 567 90", 5, "..."); //gives "123 567..."

ToolsPack.Log4net

Log4NetQuickSetup

In a Unit test project, or a temporary console application, you donnot have to configure the log4net.config any more.

Call

Log4NetQuickSetup.SetUpConsole();

or

Log4NetQuickSetup.SetUpFile("my_small_app.log");

it will setup a typical log4net appender so that you can use them in your test application. Example:

[TestClass]
public class ArrayDisplayerTests
{
	private static readonly ILog Log = LogManager.GetLogger(typeof (ArrayDisplayerTests));

	[ClassInitialize]
	public static void SetUp(TestContext testContext)
	{
		Log4NetQuickSetup.SetUpConsole();
		//or Log4NetQuickSetup.SetUpFile("mytest.log");
	}

	[TestMethod]
	public void DisplayTest()
	{
		Log.Info("it will display to the Console");
		//or to the file if you use SetUpFile()
	}
}

See also code-snippet to quickly configure log4net in a C# project

ConfigReader

use it to read app.config

Example app.config of your application

<?xml version="1.0" encoding="utf-8"?>
<configuration>
	<appSettings>
		<add key="connectionString" value="Server=localhost;Database=foo"/>
		<add key="activePingService" value="true"/>
		<add key="pollIteration" value="100"/>
	</appSettings>
</configuration>

You can read these value in your C# application

ConfigReader.Read<string>("connectionString", "a default value if config not found");
ConfigReader.Read<bool>("activePingService", false);
ConfigReader.Read<int>("pollIteration", -1);

ElapsedTimeWatcher

Micro-benchmark a part of code to investigate on performance

class MyCalculator 
{
	private static readonly ILog Log = LogManager.GetLogger(typeof(MyCalculator));

	public void Process()
	{
		using (var etw = ElapsedTimeWatcher.Create(Log, "blockCodeName"))
		{
		    ...
		    etw.Info("step 1");
		    ...
		    etw.Debug("step 2");
			...
		    etw.Info("Step 3)");
		    ...
		} //"sum up log" is displayed here 
	}
}
  • The etw wrap the usual logger Log, we use etw to log message instead of the usual Log
  • the blockCodeName is repeated in the start of each log message, so that we can filter log message by "blockCodeName"
  • Each log message will display the elapsed time (in micro-second) since the last log message.
  • A sum up log will display the total elapsed time (in micro-second) when the etw object is disposed.
22:56:59,866 [DEBUG] Begin blockCodeName
22:56:59,970 [INFO ] blockCodeName - 102350 mcs - step 1
22:57:00,144 [DEBUG] blockCodeName - 173295 mcs - step 2
22:57:00,259 [INFO ] blockCodeName - 114036 mcs - Step 3)
22:57:00,452 [INFO ] End blockCodeName : Total elapsed 585436 mcs

Auto Jump Log Level

var etw = ElapsedTimeWatcher.Create(Log, "checkIntraday").InfoEnd().AutoJump(150, 250).AutoJumpLastLog(500, 1000)
  • The log level will auto jump to INFO if the elapsed time exceeds 150 ms
  • The log level will auto jump to WARN if the elapsed time exceeds 250 ms
  • The above sum up log will switch to INFO if the total elapsed time exceeds 500 ms
  • The above sum up log will switch to WARN if the total elapsed time exceeds 1 sec

Customize Start Context and End context

var etw = ElapsedTimeWatcher.Create(Log, "foo", "Start_context", "End_context");

will give

22:56:59,866 [DEBUG] Begin Start_context
22:56:59,970 [INFO ] foo - 102350 mcs - step 1
22:57:00,144 [DEBUG] foo - 173295 mcs - step 2
22:57:00,259 [INFO ] foo - 114036 mcs - Step 3)
22:57:00,452 [INFO ] End End_context : Total elapsed 585436 mcs

We often display the parameter of the functions in the "Start context". Example:

public void process(string val, bool useCache)
{
	var context = string.Format("process(val={0}, useCache={1})", val, useCache);
	using (var etw = ElapsedTimeWatcher.Create(Log, "process", context))
	{
	    ...
	    etw.Info("step 1");
	    ...
	    etw.DebugFormat("step 2");
		...
	    etw.Info("Step 3)");
	    ...
	} //"sum up log" is displayed here 
}

will give

22:56:59,866 [DEBUG] Begin process(val=Lorem ipsum, useCache=true)
22:56:59,970 [INFO ] process - 102350 mcs - step 1
22:57:00,144 [DEBUG] process - 173295 mcs - step 2
22:57:00,259 [INFO ] process - 114036 mcs - Step 3)
22:57:00,452 [INFO ] End process(val=Lorem ipsum, useCache=true) : Total elapsed 585436 mcs

ToolsPack.Sql

Avoid redundancy of pure ADO.NET code

string qry = "SELECT.. FROM.. WHERE ArtApproved = @Approved AND ArtUpdated > @Updated AND name <> @Foo";
using (AdoHelper db = new AdoHelper(connectionString))
{
	using (SqlDataReader rdr = db.ExecDataReader(qry, 
	    "@Approved", true,
	    "@Foo", "Bazz", 50 //50 is the parameter length to optimize query cache in some case
	    "@Fuu", "Beuh", //also a varchar parameter, but I do not declare the length (not recommended)
	    "@Holly", null, //will be replaced by DBNull value
	    "@Updated", new DateTime(2011, 3, 1)))
	{
	    while (rdr.Read())
	    {
	        rdr.GetValue<int?>("views"); //no need to check null value anymore
	        rdr.GetValue<DateTime?>("lastModified");
	    }
	}
}

ToolsPack.Thread

TimedLock

https://github.com/Haacked/TimedLock

using(TimedLock.Lock(obj, TimeSpan.FromSeconds(10)))
{
    //Thread safe operations
}
  • The "synchronized code" will wait for other lock on obj free.
  • TimeOutException if the lock acquiring is longer than 10 sec

NamedLocker

static readonly NamedLocker<string> CustomerLocker = new NamedLocker<string>();
customerLocker.RunWithLock("Peter.Buy", () =>
{
	//synchronized code
}
  • The "synchronized code" will wait for other "Peter.Buy"` key free.

MultiNamedTimedLocker

static readonly MultiNamedTimedLocker<string> CustomerLocker = new MultiNamedTimedLocker<string>();

using (customerLocker.Lock(new[] {"peter", "david"}, 100))
{
	//synchronized code
}
  • The "synchronized code" will wait until the "peter" and "david" key of the CustomerLocker object are free.
  • After 100 mili-second of waiting: TimeOutException