Thursday, May 13, 2021

Convert string to title case

There is a possibility to convert any string to title case, taking care of culture in .NET. However this functionality is quite hidden and has an exception rule for words which are written entirely in uppercase. So I decided to create some extension methods on type string for easier access an with the possibility to bypass the "only-uppercase" exception rule.

using System;
using System.Globalization;
 
namespace Luegisdorf
{
    
/// <summary>
    
/// Contains extensions for type string
    
/// </summary>
    
public static class StringExtensions
    {
        
/// <summary>
        
/// Converts the specified string to title case.
        
/// </summary>
        
/// <param name="subject">
        
/// The specified string converted to title case.
        
/// </param>
        
/// <param name="keepOnlyUppercase">
        
/// If <c>true</c>, only-uppercase <c>subject</c> 
        
/// will be retained as uppercase, otherwise only-uppercase 
        
/// <c>subject</c> will title cased as well.
        
/// </param>
        
/// <param name="cultureInfo">
        
/// The culture information used for conversion.
        
/// </param>
        
/// <returns>
        
/// The title cased <c>subject</c>
        
/// </returns>
        
public static string ToTitleCase(this string subjectbool keepOnlyUppercase
            
CultureInfo cultureInfo
        {
            
var textToConvert = subject 
                ?? 
throw new ArgumentNullException(nameof(subject));
            
var cultureInformation = cultureInfo 
                ?? 
throw new ArgumentNullException(nameof(cultureInfo));
 
            
if (!keepOnlyUppercase)
            {
                
/* since TextInfo.ToTitleCase keeps only uppercase words, 
                 * we need to ensure the subject does not contain entirely
                 * uppercase words
                 */

                textToConvert = cultureInformation.TextInfo.ToLower(textToConvert);
            }
 
            
return cultureInfo.TextInfo.ToTitleCase(textToConvert);
        }
 
        
/// <summary>
        
/// Converts the specified string to title case.
        
/// </summary>
        
/// <param name="subject">
        
/// The specified string converted to title case.
        
/// </param>
        
/// <param name="keepOnlyUppercase">
        
/// If <c>true</c>, only-uppercase <c>subject</c> 
        
/// will be retained as uppercase, otherwise only-uppercase 
        
/// <c>subject</c> will title cased as well.
        
/// </param>
        
/// <returns>
        
/// The title cased <c>subject</c>
        
/// </returns>
        
public static string ToTitleCase(this string subjectbool keepOnlyUppercase)
            => ToTitleCase(subject, keepOnlyUppercase, CultureInfo.CurrentCulture);
 
 
        
/// <summary>
        
/// Converts the specified string to title case 
        
/// (including for words that are entirely in uppercase).
        
/// </summary>
        
/// <param name="subject">
        
/// The specified string converted to title case.
        
/// </param>
        
/// <param name="cultureInfo">
        
/// The culture information used for conversion.
        
/// </param>
        
/// <returns>
        
/// The title cased <c>subject</c>
        
/// </returns>
        
public static string ToTitleCase(this string subject, CultureInfo cultureInfo)
            => ToTitleCase(subject, 
false, cultureInfo);
 
 
        
/// <summary>
        
/// Converts the specified string to title case 
        
/// (including for words that are entirely in uppercase).
        
/// </summary>
        
/// <param name="subject">
        
/// The specified string converted to title case.
        
/// </param>
        
/// <returns>
        
/// The title cased <c>subject</c>
        
/// </returns>
        
public static string ToTitleCase(this string subject)
            
=> ToTitleCase(subject, CultureInfo.CurrentCulture);
 
    }
}



 


Wednesday, March 3, 2021

Beware from compiler optimizations

Recently I had the situation, that I wanted to write something into a log file and in the same time the message should be displayed as a debug message as well.

I had a file log method looked like this:

string AddMessage(string message)

So I worte the following code:

Debug.WriteLine(AddMessage("message"))

Well, when I shipped the code to the productive environment, my message never got into the log file. So first, I checked if the newest code was installed. Version number seemed to be Ok. Strange.

Then I made some considerations:

  • The code for productive environment was compiled in release mode: Should not affect my code - I haven't a debug directive and if WriteLine(...) has one, so what.
  • In productive environment, there is no debugger: No problem for me, with release build I won't see debug messages anyway.
So I thought, probably I had not saved my code properly or the check-in failed or something like that and I built it again. Same result.

No I took a look into the shipped assembly with IL-Spy (a nice assembly reflection tool) and the whole code line for Debug.WriteLine(...) was missing. Only explanation: The compiler optimization stripped that line away and did not take consideration that AddMessage(...) still has the right to exist.

So I dissect AddMessage(...) from Debug.WriteLine(...) and all went fine. This article is just to remind you, that in circumstances you need to be aware of compiler optimization and that it not always works as you expect to do so.

Thursday, October 29, 2020

Cascaded ambient scopes: How to create your own

There are such situations when you want to provide something in background, but only as long as it's needed. This could be a resource or anything you want to keep togheter. The goal can be to ensure, that it is only alive as long as you need it and/or to have a maximum one single instance to provide over complete program live cycle.

Explained in other words: an outer method call could be the first place in code when the resource or connector is used, but cascaded called methods in that outer method want's also access to that resource.

One solution to solve such problem is the use of an ambient scope. For example, the TransactionScope is a good example for ambient scopes. But it does not give you possibility to open one scope over the other with the goal to access the same transaction, if a transaction scope was opened before. For transaction scope might this have its reasons, but in this article I want to show you how you can create your action scope which supports cascaded calls.

You might argue, that you can pass such a resource from one method to the other. Yes, that is true, but there are situations where you place your code where you cannot or don't want to change the method profile (in methods such as in interfaces). And that specific ressource might become that central, that you don't want to reply code to check and eventually re-fabric this resource over and over again.

Below you find an example how such a cascaded ambient scope type could look like. As an example you could imagine, that we want to share a DB connection through some program scopes. As you see, the DB connection construction is quite flawly, it's only to give you some idea about possiblities. We will see later on, how this cascaded ambient scope could be used with another example code.

using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CascadedAmbientScopes
{
   
public sealed class DbConnectionScope : IDisposable
    {
       
#region Internal diagnosys helpers
       
public static event EventHandler<DbConnectionScope> ScopeStarted;
       
public static event EventHandler<DbConnectionScope> ScopeEnded;

       
public static event EventHandler<DbConnectionScope> CascadeStarted;
        
public static event EventHandler<DbConnectionScope> CascadeEnded;

       
public static int CascadeLevel => stack.Count - 1;

       
public int StackLevel { get; private set; }

       
#endregion
       
static DbConnectionScope Current => stack.Any() ? stack.Peek() : null;


       
#region Cascade lifetime stuff
        [ThreadStatic]
       
static DbConnection dbConnection;

       
public DbConnection DbConnection => dbConnection;

       
static void CascadeStart()
        {
           
/*
             * Build the DbConnection
             */

            dbConnection = DbProviderFactories.GetFactory(
"f.ex. retrieved from configuration or use dependency injection or whatever").CreateConnection();
           
        }

       
static void CascadeEnd()
        {
           
/*
             * Destroy the DbConnection
             */

            dbConnection.Dispose();
        }


       
#endregion

       
#region Scope stack

        [ThreadStatic]
       
static readonly Stack<DbConnectionScope> stack = new Stack<DbConnectionScope>();

       
static void Add(DbConnectionScope scope)
        {
           
if (!stack.Any())
            {
                CascadeStart();
                CascadeStarted?.Invoke(
null, scope);
            }

            stack.Push(scope);

            ScopeStarted?.Invoke(
null, scope);

        }

       
static void Remove(DbConnectionScope scope)
        {

           
#region Optional extra check for lousy programmers
           
if (Current != scope)
            {
               
throw new Exception("How you dare? It's not your turn: probably you have forgotten to dispose a previous scope or you might dispose an object in a different thread than the thread when it was created.");
            }
           
#endregion

            ScopeEnded?.Invoke(
null, scope);

            _ = stack.Pop();

           
if (!stack.Any())
            {
                CascadeEnd();
                CascadeEnded?.Invoke(
null, scope);
            }
        }

       
#endregion

       
public DbConnectionScope()
        {
            StackLevel = stack.Count;

            Add(
this);
        }


       
bool disposedValue = false;
       
public void Dispose()
        {
           
if (!disposedValue)
            {
                Remove(
this);
           
}

            disposedValue =
true;
        }
    }
}

Some take-outs for this type:

  • It must be disposed correctly, because the disposing of a scope level is the trigger to give back control to parent level. Otherwise the most parent level is never disposed - and the resource is not freed.
  • The cascaded ambient scope works isolated in it's thread. That's a limitation because otherwise, we would not be able to find out, when a scope really ends since different threads will dispose and create the ambient type concurrent.

 

Now the example, how we can use the ambient type. Let's say we have a program which run's a report on sales revenue. It can either run for a complete week or for single day. We have two separate methods, to fulfill our functional requirements: DailyReport and WeeklyReport. As part of functionality, WeeklyReport is calling DailyReport, but DailyReport can be called isolated as well (yes, I know, you would not design a program like that, neither would I - but it's only for demonstration). Both methods needs same DB connection and we want to provide the DB connection as a single resource in order to increase performance.


using System;

namespace CascadedAmbientScopes
{
   
class Program
    {
       
static void Main(string[] args)
        {
           
if (args[0] == "FullAction")
            {
                WeeklyReport();
            }
           
else
            {
                DailyReport(DateTime.Now.DayOfWeek);
            }
        }

       
static void DailyReport(DayOfWeek dayOfWeek)
        {
            
using(var dbConnectinScope = new DbConnectionScope())
            {
               
/*
                 * do something with the dbConnectionScope
                 */

            }
        }

       
static void WeeklyReport()
        {
           
using (var dbConnectinScope = new DbConnectionScope())
            {
               
for (var day = DayOfWeek.Sunday; day <= DayOfWeek.Sunday; day++)
                {
                    DailyReport(day);
                }

               
/*
                 * do something additional with the dbConnectionScope
                
*/
            }
        }
    }
}

Look now the using directives with DbConnectionScope: In case of "FullAction", it's called in WeeklyReport once, and seven times implicitly with the call of DailyReport. However: The DB connection is exactly created once, namely in the second line of WeeklyReport (using (... new DbConnectionScope())) and the DB connection will be still the same for all code in DailyReport because there will be no new DB connection created as long as the using directive in WeeklyReport is open.

See the idea behind? Again, the business case with DB connection is just an example to demonstrate the mechanics and the benefits of an cascaded ambient scope.

An other Idea is the use for the possibility of prefixed logging: Each scope could add a prefix in background, so wen something should be logged, the prefixes are included.
When we take again our sales revenue example, our code could look like this:

using System;
using System.Diagnostics;

namespace CascadedAmbientScopes
{
   
class Program
    {
       
static void Main(string[] args)
        {
           
if (args[0] == "FullAction")
            {
                WeeklyReport();
            }
           
else
            {
                DailyReport(DateTime.Now.DayOfWeek);
            }
        }

       
static decimal DailyReport(DayOfWeek dayOfWeek)
        {
           
var moneyIndicator = default(decimal);
           
using(var logging = new PrefixedLoggingScope(dayOfWeek.ToString()))
            {
               
/*
                 * calculate the money indicator
                 */

                logging.Log(
$"Turnover at {moneyIndicator}");
            }

           
return moneyIndicator;
        }

       
static void WeeklyReport()
        {
           
using (var logging = new PrefixedLoggingScope("Weekly report"))
            {
               
var moneyIndicator = default(decimal);
               
for (var day = DayOfWeek.Sunday; day <= DayOfWeek.Sunday; day++)
                {
                    moneyIndicator += DailyReport(day);
                }
                logging.Log(
$"Turnover at {moneyIndicator}");
           
}
        }
    }
}

In case of "FullAction" the output could look like this:

Weekly Report/Monday/Turnover at 6000
Weekly Report/Tuesday/Turnover at 5400
Weekly Report/Wednesday/Turnover at 4400
....
Weekly Report/Turnover at 48800

 

And in case of not "FullAction" like this:

Monday/Turnover at 6000


The token "Weekly Report" would be taken from most outer scope, created with the using directive in WeeklyReport method. The Monday, Tuesday, a.s.o. is then taken from the level created by the DailyReport method (either as most outer scope or as sub level of its parent scope created by WeeklyReport).