patterncsharpModerate
Threadsafe DBContext in singleton
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
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:
This adds extra maintenance complexity.
a third one could be one context per thread in a form of
Am I overlooking any arguments? What would you do?
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.CompilerServicesfor 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.:
...
-
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.