Skip to content

Commit 591b67d

Browse files
committed
Applies SessionScope/Scope changes from master
1 parent 5448708 commit 591b67d

File tree

7 files changed

+142
-53
lines changed

7 files changed

+142
-53
lines changed

Orm/Xtensive.Orm/Core/Scope.cs

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -143,36 +143,37 @@ protected virtual void Dispose(bool disposing)
143143
/// <exception cref="InvalidOperationException">Current scope differs from this one.</exception>
144144
public void Dispose()
145145
{
146-
bool bStop = false;
147146
Exception error = null;
148-
while (!bStop) {
149-
try {
150-
if (currentScopeAsync==null) {
151-
bStop = true;
152-
throw new InvalidOperationException(Strings.ExScopeCantBeDisposed);
153-
}
154-
else if (currentScopeAsync.Value==this) {
155-
bStop = true;
156-
currentScopeAsync.Value.Dispose(true);
157-
}
158-
else {
159-
currentScopeAsync.Value.Dispose();
147+
try {
148+
while (true) {
149+
try {
150+
var currentScope = currentScopeAsync.Value;
151+
if (currentScope == null) {
152+
throw new InvalidOperationException(Strings.ExScopeCantBeDisposed);
153+
}
154+
155+
if (currentScope == this) {
156+
currentScope.Dispose(true);
157+
break;
158+
}
159+
160+
currentScope.Dispose();
160161
}
161-
}
162-
catch (Exception e) {
163-
if (error==null)
162+
catch (Exception e) {
164163
error = e;
165-
try {
166164
CoreLog.Error(e, Strings.LogScopeDisposeError);
165+
break;
167166
}
168-
catch {}
169167
}
170168
}
171-
currentScopeAsync.Value = outerScope;
172-
context = null;
173-
outerScope = null;
174-
if (error!=null)
175-
throw error;
169+
finally {
170+
currentScopeAsync.Value = outerScope;
171+
context = null;
172+
outerScope = null;
173+
if (error != null) {
174+
throw error;
175+
}
176+
}
176177
}
177178
}
178179
}

Orm/Xtensive.Orm/Orm/Domain.cs

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -326,11 +326,22 @@ public Task<Session> OpenSessionAsync(SessionType type, CancellationToken cancel
326326
/// <seealso cref="Session"/>
327327
public Task<Session> OpenSessionAsync(SessionConfiguration configuration, CancellationToken cancellationToken = default)
328328
{
329-
return OpenSessionInternalAsync(configuration, null,
330-
configuration.Supports(SessionOptions.AutoActivation), cancellationToken);
329+
ArgumentValidator.EnsureArgumentNotNull(configuration, nameof(configuration));
330+
331+
SessionScope sessionScope = null;
332+
try {
333+
if (configuration.Supports(SessionOptions.AutoActivation)) {
334+
sessionScope = new SessionScope();
335+
}
336+
return OpenSessionInternalAsync(configuration, null, sessionScope, cancellationToken);
337+
}
338+
catch {
339+
sessionScope?.Dispose();
340+
throw;
341+
}
331342
}
332343

333-
internal async Task<Session> OpenSessionInternalAsync(SessionConfiguration configuration, StorageNode storageNode, bool activate, CancellationToken cancellationToken)
344+
internal async Task<Session> OpenSessionInternalAsync(SessionConfiguration configuration, StorageNode storageNode, SessionScope sessionScope, CancellationToken cancellationToken)
334345
{
335346
ArgumentValidator.EnsureArgumentNotNull(configuration, nameof(configuration));
336347

@@ -358,10 +369,13 @@ internal async Task<Session> OpenSessionInternalAsync(SessionConfiguration confi
358369
// connection become opened.
359370
session = new Session(this, storageNode, configuration, false);
360371
try {
361-
await ((SqlSessionHandler)session.Handler).OpenConnectionAsync(cancellationToken).ContinueWith((t) => {
362-
if (activate)
363-
session.ActivateInternally();
364-
}, TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.ExecuteSynchronously).ConfigureAwait(false);
372+
await ((SqlSessionHandler) session.Handler).OpenConnectionAsync(cancellationToken)
373+
.ContinueWith(t => {
374+
if (sessionScope != null) {
375+
session.AttachToScope(sessionScope);
376+
}
377+
}, TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.ExecuteSynchronously)
378+
.ConfigureAwait(false);
365379
}
366380
catch (OperationCanceledException) {
367381
session.DisposeSafely();

Orm/Xtensive.Orm/Orm/Session.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -234,9 +234,10 @@ internal void EnsureNotDisposed()
234234
throw new ObjectDisposedException(Strings.ExSessionIsAlreadyDisposed);
235235
}
236236

237-
internal void ActivateInternally()
237+
internal void AttachToScope(SessionScope sessionScope)
238238
{
239-
disposableSet.Add(new SessionScope(this));
239+
sessionScope.Session = this;
240+
_ = disposableSet.Add(sessionScope);
240241
}
241242

242243
internal EnumerationContext CreateEnumerationContext()
@@ -582,8 +583,9 @@ internal Session(Domain domain, StorageNode selectedStorageNode, SessionConfigur
582583
disableAutoSaveChanges = !configuration.Supports(SessionOptions.AutoSaveChanges);
583584

584585
// Perform activation
585-
if (activate)
586-
ActivateInternally();
586+
if (activate) {
587+
AttachToScope(new SessionScope());
588+
}
587589

588590
// Query endpoint
589591
SystemQuery = Query = new QueryEndpoint(new QueryProvider(this));

Orm/Xtensive.Orm/Orm/SessionScope.cs

Lines changed: 64 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,41 +4,93 @@
44
// Created by: Dmitri Maximov
55
// Created: 2007.08.29
66

7+
using System;
8+
using System.Threading;
79
using Xtensive.Core;
810

911
namespace Xtensive.Orm
1012
{
1113
/// <summary>
1214
/// <see cref="Session"/> activation scope.
1315
/// </summary>
14-
public sealed class SessionScope : Scope<Session>
16+
public sealed class SessionScope : IDisposable // : Scope<Session>
1517
{
18+
private static readonly AsyncLocal<SessionScope> currentScopeAsync = new AsyncLocal<SessionScope>();
19+
20+
private enum State
21+
{
22+
New,
23+
Active,
24+
Disposed
25+
}
26+
27+
private readonly SessionScope outerScope;
28+
private Session session;
29+
private State state;
30+
1631
/// <summary>
1732
/// Gets the current <see cref="Session"/>.
1833
/// </summary>
19-
public static Session CurrentSession
20-
{
21-
get { return CurrentContext; }
22-
}
34+
public static Session CurrentSession => currentScopeAsync.Value?.Session;
2335

2436
/// <summary>
2537
/// Gets the context of this scope.
2638
/// </summary>
2739
public Session Session
2840
{
29-
get { return Context; }
41+
get => state == State.Active ? session : outerScope?.Session;
42+
internal set {
43+
if (state != State.New) {
44+
throw new InvalidOperationException(Strings.ExCantModifyActiveOrDisposedScope);
45+
}
46+
47+
state = State.Active;
48+
session = value;
49+
}
3050
}
3151

52+
/// <inheritdoc/>
53+
public void Dispose()
54+
{
55+
if (state == State.Disposed) {
56+
return;
57+
}
58+
59+
var currentScope = currentScopeAsync.Value;
60+
61+
while (currentScope != null) {
62+
if (currentScope == this) {
63+
currentScopeAsync.Value = outerScope;
64+
state = State.Disposed;
65+
session = null;
66+
return;
67+
}
68+
69+
currentScope = currentScope.outerScope;
70+
}
71+
72+
throw new InvalidOperationException(Strings.ExScopeCantBeDisposed);
73+
}
3274

3375
// Constructors
3476

35-
/// <summary>
36-
/// Initializes a new instance of this class.
37-
/// </summary>
38-
/// <param name="session">The session to activate.</param>
39-
public SessionScope(Session session)
40-
: base(session)
77+
internal SessionScope()
78+
{
79+
state = State.New;
80+
81+
outerScope = currentScopeAsync.Value;
82+
while (outerScope != null && outerScope.state != State.Active) {
83+
outerScope = outerScope.outerScope;
84+
}
85+
86+
currentScopeAsync.Value = this;
87+
}
88+
89+
internal SessionScope(Session session)
90+
: this()
4191
{
92+
this.session = session;
93+
state = State.Active;
4294
}
4395
}
4496
}

Orm/Xtensive.Orm/Orm/StorageNode.cs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,17 @@ public Task<Session> OpenSessionAsync(SessionConfiguration configuration, Cancel
9696
{
9797
ArgumentValidator.EnsureArgumentNotNull(configuration, nameof(configuration));
9898

99-
return domain.OpenSessionInternalAsync(configuration,
100-
this,
101-
configuration.Supports(SessionOptions.AllowSwitching),
102-
cancellationToken);
99+
SessionScope sessionScope = null;
100+
try {
101+
if (configuration.Supports(SessionOptions.AutoActivation)) {
102+
sessionScope = new SessionScope();
103+
}
104+
return domain.OpenSessionInternalAsync(configuration, this, sessionScope, cancellationToken);
105+
}
106+
catch {
107+
sessionScope?.Dispose();
108+
throw;
109+
}
103110
}
104111

105112
// Constructors

Orm/Xtensive.Orm/Strings.Designer.cs

Lines changed: 12 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Orm/Xtensive.Orm/Strings.resx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3464,4 +3464,7 @@ Error: {1}</value>
34643464
<data name="ExUnableToPrepareCommandNoPartsRegistered" xml:space="preserve">
34653465
<value>Unable to prepare command: no parts registered.</value>
34663466
</data>
3467-
</root>
3467+
<data name="ExCantModifyActiveOrDisposedScope" xml:space="preserve">
3468+
<value>Can't modify Active or Disposed scope.</value>
3469+
</data>
3470+
</root>

0 commit comments

Comments
 (0)