What's a Good Threadsafe Singleton Generic Template Pattern in C#

How to make a generic singleton base class C#

The problem is your generic constraint where T : class, new(). The new() constraint requires a public, parameterless constructor on T. There is no way around this; you need to provide such a constructor in Permission Controller.

Generic SingletonT

The problem with a generic singleton factory is that since it is generic you do not control the "singleton" type that is instantiated so you can never guarantee that the instance you create will be the only instance in the application.

If a user can provide a type to as a generic type argument then they can also create instances of that type. In other words, you cannot create a generic singleton factory - it undermines the pattern itself.

How to implement a singleton in C#?

If you are just storing some global values and have some methods that don't need state, you don't need singleton. Just make the class and its properties/methods static.

public static class GlobalSomething
{
public static int NumberOfSomething { get; set; }

public static string MangleString( string someValue )
{
}
}

Singleton is most useful when you have a normal class with state, but you only want one of them. The links that others have provided should be useful in exploring the Singleton pattern.

An obvious singleton implementation for .NET?

This is the canonical, thread safe, lazy Singleton pattern in C#:

public sealed class Singleton
{
Singleton(){}
public static Singleton Instance
{
get
{
return Nested.instance;
}
}
class Nested
{
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static Nested() {}
internal static readonly Singleton instance = new Singleton();
}
}

Difference between these two Singleton implementations

I definitely would go with your first implementation...

the second seems questionable to me... if you need/want a lazy implementation you could use Lazy<T> for that - since it is part of the framework it feels much more comfortable..

BTW: There are even more ways to implement the Singleton pattern... here is an excellent article.

Using Singleton in C#

If your Instance method is written correctly, then you should be able to call it again in Form2 and get a reference to the exact same object that was created in Form1.

Implementing Non blocking thread safe singleton logging class

In the end I have managed to achieve a non-blocking, thread safe,singleton logging class with the use of a background worker. I am submitting my solution in case someone find it useful someday.

using System;
using System.Collections.Generic;
using System.IO;
using System.Web;
using System.Threading;
using System.Threading.Tasks;
using System.ComponentModel;
using System.Diagnostics;
using System.Web.Configuration;
/// <summary>
/// Summary description for LogMessage
/// </summary>
public class LogMessage
{
static ReaderWriterLock locker = new ReaderWriterLock();

private static string serviceDirectory = HttpContext.Current != null ?
AppDomain.CurrentDomain.BaseDirectory :
Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
private static string fullpath = serviceDirectory + "\\ActivityLog.log";
private static readonly LogMessage instance = new LogMessage();

public static LogMessage Instance
{
get { return instance; }
}

public void SaveLogMessage(string userName, string message, string stackTrace, bool inOut)
{
bool EnableActivityLogging = false;

if (string.IsNullOrEmpty(WebConfigurationManager.AppSettings["EnableActivityLogging"]))
return;

EnableActivityLogging = Convert.ToBoolean(WebConfigurationManager.AppSettings["EnableActivityLogging"]);

if (!EnableActivityLogging)
return;

BackgroundWorker logbw = new BackgroundWorker();
logbw.DoWork += logbw_DoWork;
logbw.RunWorkerCompleted += logbw_RunWorkerCompleted;

List<string> paramList = new List<string>();
paramList.Add(userName);
paramList.Add(message);
paramList.Add(stackTrace);
paramList.Add(inOut.ToString());

if (!logbw.IsBusy)
{
logbw.RunWorkerAsync(paramList);
}
}

void logbw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Debug.Write("Log Message Background Worker is now free...");
}

void logbw_DoWork(object sender, DoWorkEventArgs e)
{
List<string> paramList = (List<string>)e.Argument;
string userName = paramList[0].ToString();
string message = paramList[1].ToString();
string stackTrace = paramList[2].ToString();
bool inOut = bool.Parse(paramList[3].ToString());

try
{
locker.AcquireWriterLock(int.MaxValue);
using (StreamWriter logWriter =
new StreamWriter(fullpath, true))
{
string logMessage = "";
if (!string.IsNullOrEmpty(stackTrace))
{
if (inOut)//IN
{
logMessage = string.Format("{0} U:{1} IN:{2} E:{3}", DateTime.Now.ToString(), userName, message, stackTrace);
}
else//OUT
{
logMessage = string.Format("{0} U:{1} OUT:{2} E:{3}", DateTime.Now.ToString(), userName, message, stackTrace);
}
}
else
{
if (inOut)//IN
{
logMessage = string.Format("{0} U:{1} IN:{2}", DateTime.Now.ToString(), userName, message);
}
else//OUT
{
logMessage = string.Format("{0} U:{1} OUT:{2}", DateTime.Now.ToString(), userName, message);
}
}

logWriter.WriteLine(logMessage);

}
}
finally
{
locker.ReleaseWriterLock();
}
}

}

Now I can use this singleton object like below to save a log entry

    LogMessage.Instance.SaveLogMessage(Context.User.Identity.Name, "Custom Message" + " M:" + MethodInfo.GetCurrentMethod().Name + "@" + Path.GetFileName(Page.AppRelativeVirtualPath), "", true); 


Related Topics



Leave a reply



Submit