diff --git a/Orm/Xtensive.Orm.Tests/Storage/Prefetch/PrefetchManagerBasicTest.cs b/Orm/Xtensive.Orm.Tests/Storage/Prefetch/PrefetchManagerBasicTest.cs
index d03bdda37e..ca375845f8 100644
--- a/Orm/Xtensive.Orm.Tests/Storage/Prefetch/PrefetchManagerBasicTest.cs
+++ b/Orm/Xtensive.Orm.Tests/Storage/Prefetch/PrefetchManagerBasicTest.cs
@@ -7,6 +7,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Runtime.CompilerServices;
using System.Threading;
using NUnit.Framework;
using Xtensive.Core;
@@ -27,16 +28,14 @@ namespace Xtensive.Orm.Tests.Storage.Prefetch
[TestFixture]
public class PrefetchManagerBasicTest : PrefetchManagerTestBase
{
+ private const int Iterations = 10;
private volatile static int instanceCount;
#region Nested class
public class MemoryLeakTester
{
- ~MemoryLeakTester()
- {
- instanceCount--;
- }
+ ~MemoryLeakTester() => Interlocked.Decrement(ref instanceCount);
}
#endregion
@@ -932,17 +931,18 @@ public void RemoveTest()
[Test]
public void ReferenceToSessionIsNotPreservedInCacheTest()
{
- // Use separate method for session related processing
+ // Use separate method with [MethodImpl(MethodImplOptions.NoInlining)] attribute for session related processing
// to make sure we don't hold session reference somewhere on stack
OpenSessionsAndRunPrefetches();
TestHelper.CollectGarbage(true);
Assert.That(instanceCount, Is.EqualTo(0));
}
+ [MethodImpl(MethodImplOptions.NoInlining)]
private void OpenSessionsAndRunPrefetches()
{
- instanceCount = 10;
- for (int i = 0; i < instanceCount; i++) {
+ instanceCount = Iterations;
+ for (int i = 0; i < Iterations; i++) {
using (var session = Domain.OpenSession())
using (var t = session.OpenTransaction()) {
session.Extensions.Set(new MemoryLeakTester());
diff --git a/Orm/Xtensive.Orm/Collections/BindingCollection.cs b/Orm/Xtensive.Orm/Collections/BindingCollection.cs
index 0d8efef136..de594dbe0e 100644
--- a/Orm/Xtensive.Orm/Collections/BindingCollection.cs
+++ b/Orm/Xtensive.Orm/Collections/BindingCollection.cs
@@ -112,9 +112,7 @@ public virtual BindingScope Add(TKey key, TValue value)
public virtual void PermanentAdd(TKey key, TValue value)
{
bindings[key] = value;
- if (!permanentBindings.Contains(key)) {
- permanentBindings.Add(key);
- }
+ permanentBindings.Add(key);
}
///
diff --git a/Orm/Xtensive.Orm/Collections/Graphs/Graph.cs b/Orm/Xtensive.Orm/Collections/Graphs/Graph.cs
index 97799678e3..4d476f797e 100644
--- a/Orm/Xtensive.Orm/Collections/Graphs/Graph.cs
+++ b/Orm/Xtensive.Orm/Collections/Graphs/Graph.cs
@@ -56,9 +56,8 @@ public Graph, Edge> CreateMutableCopy()
foreach (var rNode in copy.Nodes) {
var node = rNode.Value;
foreach (var edge in node.Edges) {
- if (!processedEdges.Contains(edge)) {
+ if (processedEdges.Add(edge)) {
var rEdge = new Edge(nodeMap[edge.Source], nodeMap[edge.Target], (TEdge) edge);
- processedEdges.Add(edge);
}
}
}
diff --git a/Orm/Xtensive.Orm/Orm/Entity.cs b/Orm/Xtensive.Orm/Orm/Entity.cs
index acfb714124..b9d5103351 100644
--- a/Orm/Xtensive.Orm/Orm/Entity.cs
+++ b/Orm/Xtensive.Orm/Orm/Entity.cs
@@ -138,8 +138,7 @@ public VersionInfo VersionInfo {
List columnsToPrefetch = null;
foreach (var columnInfo in versionColumns) {
if (!tuple.GetFieldState(columnInfo.Field.MappingInfo.Offset).IsAvailable()) {
- if (columnsToPrefetch==null)
- columnsToPrefetch = new List();
+ columnsToPrefetch ??= new List(1);
columnsToPrefetch.Add(new PrefetchFieldDescriptor(columnInfo.Field));
}
}
diff --git a/Orm/Xtensive.Orm/Orm/Internals/Pinner.cs b/Orm/Xtensive.Orm/Orm/Internals/Pinner.cs
index b18c2bc902..fc73f3f9fb 100644
--- a/Orm/Xtensive.Orm/Orm/Internals/Pinner.cs
+++ b/Orm/Xtensive.Orm/Orm/Internals/Pinner.cs
@@ -14,6 +14,14 @@ namespace Xtensive.Orm.Internals
{
internal sealed class Pinner : SessionBound
{
+ private class DisposableRemover : IDisposable
+ {
+ public Pinner Pinner { get; init; }
+ public EntityState State { get; init; }
+
+ public void Dispose() => Pinner.roots.Remove(State);
+ }
+
private readonly HashSet roots = new HashSet();
private EntityChangeRegistry activeRegistry;
@@ -24,15 +32,8 @@ internal sealed class Pinner : SessionBound
public EntityChangeRegistry PinnedItems { get; private set; }
public EntityChangeRegistry PersistableItems { get; private set; }
- public IDisposable RegisterRoot(EntityState state)
- {
- if (roots.Contains(state))
- return null;
- roots.Add(state);
- return new Disposable(
- this, state,
- (disposing, _this, _state) => _this.roots.Remove(_state));
- }
+ public IDisposable RegisterRoot(EntityState state) =>
+ roots.Add(state) ? new DisposableRemover { Pinner = this, State = state } : null;
public void ClearRoots()
{
diff --git a/Orm/Xtensive.Orm/Orm/Model/FullTextIndexInfoCollection.cs b/Orm/Xtensive.Orm/Orm/Model/FullTextIndexInfoCollection.cs
index 97af6944e8..c2c3894938 100644
--- a/Orm/Xtensive.Orm/Orm/Model/FullTextIndexInfoCollection.cs
+++ b/Orm/Xtensive.Orm/Orm/Model/FullTextIndexInfoCollection.cs
@@ -54,8 +54,7 @@ public bool TryGetValue(TypeInfo typeInfo, out FullTextIndexInfo fullTextIndexIn
public void Add(TypeInfo typeInfo, FullTextIndexInfo fullTextIndexInfo)
{
EnsureNotLocked();
- if (!container.Contains(fullTextIndexInfo))
- container.Add(fullTextIndexInfo);
+ container.Add(fullTextIndexInfo);
indexMap.Add(typeInfo, fullTextIndexInfo);
}
diff --git a/Orm/Xtensive.Orm/Orm/OperationLog.cs b/Orm/Xtensive.Orm/Orm/OperationLog.cs
index eea570e86b..333a83a430 100644
--- a/Orm/Xtensive.Orm/Orm/OperationLog.cs
+++ b/Orm/Xtensive.Orm/Orm/OperationLog.cs
@@ -61,7 +61,7 @@ public KeyMapping Replay(Session session)
KeyMapping keyMapping;
using (session.Activate()) {
- using (isSystemOperationLog ? session.OpenSystemLogicOnlyRegion() : null)
+ using (isSystemOperationLog ? (IDisposable) session.OpenSystemLogicOnlyRegion() : null)
using (var tx = session.OpenTransaction(TransactionOpenMode.New)) {
foreach (var operation in operations)
diff --git a/Orm/Xtensive.Orm/Orm/Operations/OperationRegistry.cs b/Orm/Xtensive.Orm/Orm/Operations/OperationRegistry.cs
index dfd867091a..61fb2b8b88 100644
--- a/Orm/Xtensive.Orm/Orm/Operations/OperationRegistry.cs
+++ b/Orm/Xtensive.Orm/Orm/Operations/OperationRegistry.cs
@@ -16,10 +16,24 @@ namespace Xtensive.Orm.Operations
///
public sealed class OperationRegistry
{
+ public readonly struct SystemOperationRegistrationScope : IDisposable
+ {
+ private readonly OperationRegistry registry;
+ private readonly bool prevIsSystemOperationRegistrationEnabled;
+
+ public SystemOperationRegistrationScope(OperationRegistry registry, bool enable)
+ {
+ this.registry = registry;
+ prevIsSystemOperationRegistrationEnabled = registry.IsSystemOperationRegistrationEnabled;
+ registry.IsSystemOperationRegistrationEnabled = enable;
+ }
+
+ public void Dispose() => registry.IsSystemOperationRegistrationEnabled = prevIsSystemOperationRegistrationEnabled;
+ }
+
private readonly ICompletableScope blockingScope;
private bool isOperationRegistrationEnabled = true;
private bool isUndoOperationRegistrationEnabled = true;
- private bool isSystemOperationRegistrationEnabled = true;
private Collections.Deque scopes = new Collections.Deque();
///
@@ -41,10 +55,7 @@ public bool IsRegistrationEnabled {
///
/// Gets a value indicating whether system operation registration is enabled.
///
- public bool IsSystemOperationRegistrationEnabled {
- get { return isSystemOperationRegistrationEnabled; }
- internal set { isSystemOperationRegistrationEnabled = value; }
- }
+ public bool IsSystemOperationRegistrationEnabled { get; internal set; } = true;
///
/// Gets a value indicating whether this instance can register operation
@@ -219,29 +230,13 @@ public IDisposable DisableUndoOperationRegistration()
/// Temporarily disables system operation logging.
///
/// An object enabling the logging back on its disposal.
- public IDisposable DisableSystemOperationRegistration()
- {
- if (!isSystemOperationRegistrationEnabled)
- return null;
- var result = new Disposable(this, isSystemOperationRegistrationEnabled,
- (disposing, _this, previousState) => _this.isSystemOperationRegistrationEnabled = previousState);
- isSystemOperationRegistrationEnabled = false;
- return result;
- }
+ public SystemOperationRegistrationScope DisableSystemOperationRegistration() => new(this, false);
///
/// Temporarily enables system operation logging.
///
/// An object disabling the logging back on its disposal.
- public IDisposable EnableSystemOperationRegistration()
- {
- if (isSystemOperationRegistrationEnabled)
- return null;
- var result = new Disposable(this, isSystemOperationRegistrationEnabled,
- (disposing, _this, previousState) => _this.isSystemOperationRegistrationEnabled = previousState);
- isSystemOperationRegistrationEnabled = true;
- return result;
- }
+ public SystemOperationRegistrationScope EnableSystemOperationRegistration() => new(this, true);
///
/// Registers the operation.
diff --git a/Orm/Xtensive.Orm/Orm/Services/DirectSessionAccessor.cs b/Orm/Xtensive.Orm/Orm/Services/DirectSessionAccessor.cs
index 93fa845709..0d5e115273 100644
--- a/Orm/Xtensive.Orm/Orm/Services/DirectSessionAccessor.cs
+++ b/Orm/Xtensive.Orm/Orm/Services/DirectSessionAccessor.cs
@@ -28,10 +28,7 @@ public sealed class DirectSessionAccessor : SessionBound,
/// disposal will restore previous state of
/// property.
///
- public IDisposable OpenSystemLogicOnlyRegion()
- {
- return Session.OpenSystemLogicOnlyRegion();
- }
+ public Session.SystemLogicOnlyRegionScope OpenSystemLogicOnlyRegion() => Session.OpenSystemLogicOnlyRegion();
///
/// Changes the value of .
diff --git a/Orm/Xtensive.Orm/Orm/Session.Persist.cs b/Orm/Xtensive.Orm/Orm/Session.Persist.cs
index e963a0dd80..07fcd69c95 100644
--- a/Orm/Xtensive.Orm/Orm/Session.Persist.cs
+++ b/Orm/Xtensive.Orm/Orm/Session.Persist.cs
@@ -18,6 +18,8 @@ namespace Xtensive.Orm
{
public partial class Session
{
+ private static readonly IDisposable EmptyDisposable = new Disposable(b => { return; });
+
private bool disableAutoSaveChanges;
private KeyRemapper remapper;
private bool persistingIsFailed;
@@ -245,9 +247,9 @@ public IDisposable DisableSaveChanges(IEntity target)
ArgumentValidator.EnsureArgumentNotNull(target, "target");
var targetEntity = (Entity) target;
targetEntity.EnsureNotRemoved();
- if (!Configuration.Supports(SessionOptions.AutoSaveChanges))
- return new Disposable(b => {return;}); // No need to pin in this case
- return pinner.RegisterRoot(targetEntity.State);
+ return Configuration.Supports(SessionOptions.AutoSaveChanges)
+ ? pinner.RegisterRoot(targetEntity.State)
+ : EmptyDisposable; // No need to pin in this case
}
///
@@ -260,8 +262,9 @@ public IDisposable DisableSaveChanges(IEntity target)
///
public IDisposable DisableSaveChanges()
{
- if (!Configuration.Supports(SessionOptions.AutoSaveChanges))
- return new Disposable(b => { return; }); // No need to pin in this case
+ if (!Configuration.Supports(SessionOptions.AutoSaveChanges)) {
+ return EmptyDisposable; // No need to pin in this case
+ }
if (disableAutoSaveChanges)
return null;
diff --git a/Orm/Xtensive.Orm/Orm/Session.SystemLogic.cs b/Orm/Xtensive.Orm/Orm/Session.SystemLogic.cs
index c14a77535f..5b31a1b9da 100644
--- a/Orm/Xtensive.Orm/Orm/Session.SystemLogic.cs
+++ b/Orm/Xtensive.Orm/Orm/Session.SystemLogic.cs
@@ -11,17 +11,26 @@ namespace Xtensive.Orm
{
public partial class Session
{
+ public readonly struct SystemLogicOnlyRegionScope : IDisposable
+ {
+ private readonly Session session;
+ private readonly bool prevIsSystemLogicOnly;
+
+ public SystemLogicOnlyRegionScope(Session session)
+ {
+ this.session = session;
+ prevIsSystemLogicOnly = session.IsSystemLogicOnly;
+ session.IsSystemLogicOnly = true;
+ }
+
+ public void Dispose() => session.IsSystemLogicOnly = prevIsSystemLogicOnly;
+ }
+
///
/// Gets a value indicating whether only a system logic is enabled.
///
internal bool IsSystemLogicOnly { get; set; }
- internal IDisposable OpenSystemLogicOnlyRegion()
- {
- var result = new Disposable(this, IsSystemLogicOnly,
- (disposing, session, previousState) => session.IsSystemLogicOnly = previousState);
- IsSystemLogicOnly = true;
- return result;
- }
+ internal SystemLogicOnlyRegionScope OpenSystemLogicOnlyRegion() => new(this);
}
}
\ No newline at end of file