0

I am using SQLCipher for sqlite DB encryption in WPF application. As per knowledge they recommend to use Singleton pattern for my DbContext class ensure a single instance is used throughout my application for better performance. Below is a simplified version of the code:

public class MyDbContext : DbContext
{
    private static MyDbContext _instance;

    private MyDbContext() : base("MyConnectionString") { }

    public static MyDbContext Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new MyDbContext();
            }
            return _instance;
        }
    }
}

For delete, update, and insert operations, I begin a transaction and perform the operations. However, I sometimes perform save and update operations from a separate thread. The problem occurs when one thread is performing an update operation and the main thread attempts to read data from the database at the same time. This causes the following exception:

Error occurred while reading from the store provider's data reader. See the inner exception for details.
---> System.InvalidOperationException: Connection was closed, statement was terminated
at System.Data.SQLite.SQLiteDataReader.CheckClosed()
at System.Data.SQLite.SQLiteDataReader.PrivateRead(Boolean ignoreSingleRow)
at System.Data.SQLite.SQLiteDataReader.Read()

My questions are:

  1. how to avoid such errors gracefully?
  2. Shall the implementation of retry logic when getting the exception with delay be appropriate?
  3. Any other solution suggestion.

Any help or guidance would be appreciated!

6

1 Answer 1

2

As per knowledge they recommend to use Singleton pattern for my DbContext class ensure a single instance is used throughout my application for better performance.

That knowledge is 100% incorrect and is 100% the cause of all of your problems; where-ever you found this advice: maybe re-evaluate how much you trust from there! They might just be wrong on this one topic, or they might be wrong on many topics, or maybe it is just a communication gap and what they are trying to communicate isn't quite how you interpreted it.

Don't do that.

  • a DbContext is intended to be short-lived; you certainly don't want the buckets of known entities building up over time
  • those buckets of known entities are themselves not intended to be used concurrently, and any operations that interact with them concurrently (especially when at least one concurrent operation is a mutation): undefined
  • the underlying connection is probably an ADO.NET connection, which is not a thread-safe API, and if concurrent underlying data operations end up happening concurrently: undefined

In short: use DbContext in a way that is a: scoped to a particular operation (or set of related operations), and b: does not permit concurrent access.

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you very much for your detail description, where they mention that "do not repeatedly open and close connections", that's why we tried to use singleton patterns for minimizing the database connection. Here is the Link. Just to mention that we have data Access classes for database operations, and we are using EF6
@user15539024 the open/close topic is fun; opening once per scope: totally fine, but also: many providers use connection-pooling underneath, so "open" is really "lease an already open connection from the pool, otherwise actually connect" - so "open" is often effectively free (in that it doesn't go out-of-process or off-box); singleton is not an answer to this topic

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.