4

I've been searching for some time now in here and other places and can't find a good answer to why Linq-TO-SQL with NOLOCK is not possible..

Every time I search for how to apply the with(NOLOCK) hint to a Linq-To-SQL context (applied to 1 sql statement) people often answer to force a transaction (TransactionScope) with IsolationLevel set to ReadUncommitted. Well - they rarely tell this causes the connection to open an transaction (that I've also read somewhere must be ensured closed manually).

Using ReadUncommitted in my application as is, is really not that good. Right now I've got using context statements for the same connection within each other. Like:

using( var ctx1 = new Context()) {
    ... some code here ...
    using( var ctx2 = new Context()) {
        ... some code here ...
        using( var ctx3 = new Context()) {
            ... some code here ...
        }
        ... some code here ...
    }
    ... some code here ...
}

With a total execution time of 1 sec and many users on the same time, changing the isolation level will cause the contexts to wait for each other to release a connection because all the connections in the connection pool is being used.

So one (of many reasons) for changing to "nolock" is to avoid deadlocks (right now we have 1 customer deadlock per day). The consequence of above is just another kind of deadlock and really doesn't solve my issue.

So what I know I could do is:

  1. Avoid nested usage of same connection
  2. Increase the connection pool size at the server

But my problem is:

  1. This is not possible within near future because of many lines of code re-factoring and it will conflict with the architecture (without even starting to comment whether this is good or bad)
  2. Even though this of course will work, this is what I would call "symptomatic treatment" - as I don't know how much the application will grow and if this is a reliable solution for the future (and then I might end up with a even worse situation with a lot more users being affected)

My thoughts are:

  1. Can it really be true that NoLock is not possible (for each statement without starting transactions)?
  2. If 1 is true - can it really be true no one other got this problem and solved it in a generic linq to sql modification?
  3. If 2 is true - why is this not a issue for others?
    1. Is there another workaround I havn't looked at maybe?
    2. Is the using of the same connection (nested) many times so bad practice that no-one has this issue?
Nick Niebling
  • 317
  • 3
  • 12
  • 1
    is it *all* operations that you want to be nolocked? or *some*? for *all* you can use the connection-level isolation level via `SET`, however **IMPORTANT**; in a move of "brilliance", the isolation-level **IS NOT** reset between different usage of the same underlying pooled connection, so if you go that route you need to explicitly `SET` appropriately after opening the connection – Marc Gravell Dec 13 '11 at 10:59
  • 1
    Also: worth a watch: http://www.brentozar.com/archive/2011/11/theres-something-about-nolock-webcast-video/ – Marc Gravell Dec 13 '11 at 11:01

1 Answers1

2

1: LINQ-to-SQL does indeed not allow you to indicate hints like NOLOCK; it is possible to write your own TSQL, though, and use ExecuteQuery<T> etc

2: to solve in an elegant way would be pretty complicated, frankly; and there's a strong chance that you would be using it inappropriately. For example, in the "deadlock" scenario, I would wager that actually it is UPDLOCK that you should be using (during the first read), to ensure that the first read takes a write lock; this prevents a second later query getting a read lock, so you generally get blocking instead of deadlock

3: using the connection isn't necessarily a big problem (although note that new Context() won't generally share a connection; to share a connection you would use new Context(connection)). If seeing this issue, there are three likely solutions (if we exclude "use an ORM with hint support"):

  • using an explicit transaction (which doesn't have to be TransactionScope - it can be a connection level transaction) to specify the isolation level
  • write your own TSQL with hints
  • use a connection-level isolation level (noting the caveat I added as a comment)

IIRC there is also a way to subclass the data-context and override some of the transaction-creation code to control the isolation-level for the transactions that it creates internally.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Right now I've been testing with transactionscope and this.ExecuteCommand("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;"); (and of course made my own clientcontext class to handle creation and IDisposable generic) We do use new Context(connection) to get the benefit of shared connection.. Using the connection-level isolation level, will this affect the sharing of connections? Sounds like this might be the solution for me? – Nick Niebling Dec 13 '11 at 11:25
  • 1
    @Nick - it is connection wide; any usage of that connection will use the last isolation-level set, which *might* mean the isolation-level from a `SET`, or *might` mean the isolation-level from a transaction. Crazy stuff. And: as noted it **does not** get reset automatically, so if you monkey with this, then **all** your `connection.Open()` must issue a `SET` to ensure the **appropriate* isolation-level is used for that connection. But - as long as you understand **what** you are doing (and **why**), it can be effective, yes. – Marc Gravell Dec 13 '11 at 11:30
  • Isn't Disposing the connection when I use Open() enough? – Nick Niebling Dec 13 '11 at 11:32
  • 1
    @Nick no, it is not. As I was trying to say: that (typically) just releases the underlying connection to the pool; when you re-fetch from the pool, a *reset* operation is performed to cleanse it, but the isolation-level **is not included** in this, and will retain the level from whatever the underlying connection was doing last. – Marc Gravell Dec 13 '11 at 11:34
  • Well thanks.. I guess my approach will be to reset the connection on dispose rather than on init.. Like the "mess up" and "clean up" to on init and dispose.. I'm also using different "modes", so different cleanup might be required.. Thanks a lot for your info in depth :-) – Nick Niebling Dec 13 '11 at 12:01