HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpModerate

Threadsafe DBContext in singleton

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
singletondbcontextthreadsafe

Problem

I found out the hardway that access to DbContext in .NET is not threadsafe. I have a singleton for logging things using a dbcontext. The original version uses something like

public Logger{
  private MyContext context;
  private static Logger instance = new Logger();

  private Logger(){
    //init stuff, including context
  }

  public static Logger Instance {
      return this.instance;
  }

  public void someMethod(){
    //do something with this.context
  }
}


I'm thinking of a few solutions:

one would be is not making this a singleton at all, but instantiate a logger each time I need one.

I can think of added overhead as a disadvantage for this way, and simplicity as an advantage.

another one is locking (on the context or type) for each public method:

public void someMethod(){
  lock(this.context){
    //do something with this.context
  }
}


This adds extra maintenance complexity.

a third one could be one context per thread in a form of

private ConditionalWeakTable contexts = new ConditionalWeakTable();

private MyContext Context{
  get {
    return contexts.GetValue(Thread.CurrentThread, createContext());
  }
}

private MyContext createContext(){
  //instantiate a context
}


  • Pro: fairly consice, complexity is isolated



  • Con: batshit insane? Using System.Runtime.CompilerServices for something fairly mondaine, which also isn't what it's meant for.



Am I overlooking any arguments? What would you do?

Solution

General rules of thumb:

-
In order to take advantage of connection pooling (and you should), database connections should be as short lived as possible. Create, use, then immediately destroy.

-
Single instance objects should always be agile (defined as not holding system resources e.g. db connections, file handles, etc.).

-
If you need a single instance object to handle a system resource, it should be done in a protected manner using a named mutex (naming makes a mutex available to all threads across the computer) or Monitor. Within the protected block of code, if the resource is a db connection, apply rule 1.

-
When using object locking, you should create your locks around an arbitrary object, not the current object's instance e.g.:

private static readonly object _lockObject = new object();


...

try {
    Monitor.Enter(_lockObject);
    // Do protected stuff...
}
finally {
    Monitor.Exit(_lockObject);
}

Code Snippets

private static readonly object _lockObject = new object();
try {
    Monitor.Enter(_lockObject);
    // Do protected stuff...
}
finally {
    Monitor.Exit(_lockObject);
}

Context

StackExchange Code Review Q#15703, answer score: 12

Revisions (0)

No revisions yet.