Skip to content

Commit 11a51bf

Browse files
authored
Add a way to specify no-tracking to reduce caching on demand (#524)
Fixes #522 Fixes #523
1 parent 6c139c1 commit 11a51bf

File tree

8 files changed

+132
-58
lines changed

8 files changed

+132
-58
lines changed

src/YesSql.Abstractions/IQuery.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -63,13 +63,13 @@ public interface IQuery<T> where T : class
6363
/// </summary>
6464
/// <typeparam name="TIndex">The index to filter on.</typeparam>
6565
IQuery<T, TIndex> With<TIndex>() where TIndex : class, IIndex;
66-
66+
6767
/// <summary>
6868
/// Filters the documents with a constraint on the specified index.
6969
/// </summary>
7070
/// <typeparam name="TIndex">The index to filter on.</typeparam>
7171
IQuery<T, TIndex> With<TIndex>(Expression<Func<TIndex, bool>> predicate) where TIndex : class, IIndex;
72-
72+
7373
/// <summary>
7474
/// Skips the specified number of document.
7575
/// </summary>
@@ -128,7 +128,7 @@ public interface IQueryIndex<T> where T : IIndex
128128
/// Joins the document table with an index, and filter it with a predicate.
129129
/// </summary>
130130
IQueryIndex<TIndex> With<TIndex>(Expression<Func<TIndex, bool>> predicate) where TIndex : class, IIndex;
131-
131+
132132
/// <summary>
133133
/// Adds a custom Where clause to the query.
134134
/// </summary>
@@ -163,7 +163,7 @@ public interface IQueryIndex<T> where T : IIndex
163163
/// Adds an OrderBy clause using a custom lambda expression.
164164
/// </summary>
165165
IQueryIndex<T> ThenBy(Expression<Func<T, object>> keySelector);
166-
166+
167167
/// <summary>
168168
/// Adds a descending OrderBy clause using a custom lambda expression.
169169
/// </summary>
@@ -221,7 +221,7 @@ public interface IQuery<T, TIndex> : IQuery<T>
221221
/// Adds a custom Where clause to the query using a specific dialect.
222222
/// </summary>
223223
IQuery<T, TIndex> Where(Func<ISqlDialect, string> sql);
224-
224+
225225
/// <summary>
226226
/// Adds a named parameter to the query.
227227
/// </summary>
@@ -236,19 +236,19 @@ public interface IQuery<T, TIndex> : IQuery<T>
236236
/// Sets an OrderBy clause using a custom lambda expression.
237237
/// </summary>
238238
IQuery<T, TIndex> OrderBy(Expression<Func<TIndex, object>> keySelector);
239-
239+
240240
/// <summary>
241241
/// Sets an OrderBy clause using a custom SQL statement.
242242
/// </summary>
243243
IQuery<T, TIndex> OrderBy(string sql);
244-
244+
245245
IQuery<T, TIndex> OrderByDescending(Expression<Func<TIndex, object>> keySelector);
246-
246+
247247
/// <summary>
248248
/// Sets a descending OrderBy clause using a custom SQL statement.
249249
/// </summary>
250250
IQuery<T, TIndex> OrderByDescending(string sql);
251-
251+
252252
/// <summary>
253253
/// Sets a random OrderBy clause.
254254
/// </summary>

src/YesSql.Abstractions/ISession.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,16 @@ public interface ISession : IDisposable, IAsyncDisposable
6161
/// </remarks>
6262
void Detach(object item, string collection = null);
6363

64+
/// <summary>
65+
/// Removes multiple items from the identity map.
66+
/// </summary>
67+
/// <remarks>
68+
/// This method can be used to remove multiple items that should not be served again from the cache.
69+
/// For instance when its state as changed and any subsequent query should not return the
70+
/// modified instance but a fresh one.
71+
/// </remarks>
72+
void Detach(IEnumerable<object> entries, string collection = null);
73+
6474
/// <summary>
6575
/// Loads objects by id.
6676
/// </summary>

src/YesSql.Abstractions/IStore.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ public interface IStore : IDisposable
1010
/// <summary>
1111
/// Creates a new <see cref="ISession"/> to communicate with the <see cref="IStore"/>.
1212
/// </summary>
13-
ISession CreateSession();
13+
ISession CreateSession(bool withTracking = true);
1414

1515
/// <summary>
1616
/// Registers index providers.

src/YesSql.Abstractions/SessionExtensions.cs

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System;
1+
using System;
22
using System.Collections.Generic;
33
using System.Data;
44
using System.Linq;
@@ -13,16 +13,17 @@ public static class SessionExtensions
1313
/// Loads an object by its id.
1414
/// </summary>
1515
/// <returns>The object or <c>null</c>.</returns>
16-
public async static Task<T> GetAsync<T>(this ISession session, long id, string collection = null) where T : class
17-
{
18-
return (await session.GetAsync<T>(new[] { id }, collection)).FirstOrDefault();
19-
}
16+
public async static Task<T> GetAsync<T>(this ISession session, long id, string collection = null)
17+
where T : class
18+
=> (await session.GetAsync<T>([id], collection)).FirstOrDefault();
2019

2120
/// <summary>
2221
/// Loads objects by id.
2322
/// </summary>
2423
/// <returns>A collection of objects in the same order they were defined.</returns>
25-
public static Task<IEnumerable<T>> GetAsync<T>(this ISession session, int[] ids, string collection = null) where T : class => session.GetAsync<T>(ids.Select(x => (long)x).ToArray(), collection);
24+
public static Task<IEnumerable<T>> GetAsync<T>(this ISession session, int[] ids, string collection = null)
25+
where T : class
26+
=> session.GetAsync<T>(ids.Select(x => (long)x).ToArray(), collection);
2627

2728
/// <summary>
2829
/// Imports an object in the local identity map.
@@ -37,9 +38,7 @@ public async static Task<T> GetAsync<T>(this ISession session, long id, string c
3738
/// <c>true</c> if the object was imported, <c>false</c> otherwise.
3839
/// </returns>
3940
public static bool Import(this ISession session, object item, string collection = null)
40-
{
41-
return session.Import(item, 0, 0, collection);
42-
}
41+
=> session.Import(item, 0, 0, collection);
4342

4443
/// <summary>
4544
/// Registers index providers that are used only during the lifetime of this session.
@@ -48,9 +47,7 @@ public static bool Import(this ISession session, object item, string collection
4847
/// <param name="indexProviders">The index providers to register.</param>
4948
/// <returns>The <see cref="ISession"/> instance.</returns>
5049
public static ISession RegisterIndexes(this ISession session, params IIndexProvider[] indexProviders)
51-
{
52-
return session.RegisterIndexes(indexProviders, null);
53-
}
50+
=> session.RegisterIndexes(indexProviders, null);
5451

5552
/// <summary>
5653
/// Registers index providers that are used only during the lifetime of this session.
@@ -60,9 +57,7 @@ public static ISession RegisterIndexes(this ISession session, params IIndexProvi
6057
/// <param name="collection">The name of the collection.</param>
6158
/// <returns>The <see cref="ISession"/> instance.</returns>
6259
public static ISession RegisterIndexes(this ISession session, IIndexProvider indexProvider, string collection = null)
63-
{
64-
return session.RegisterIndexes(new[] { indexProvider }, collection);
65-
}
60+
=> session.RegisterIndexes([indexProvider], collection);
6661

6762
/// <summary>
6863
/// Saves a new or existing object to the store, and updates

src/YesSql.Core/Services/DefaultQuery.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public QueryState(ISqlBuilder sqlBuilder, IStore store, string collection)
3636
public List<Action<object, ISqlBuilder>> _parameterBindings;
3737
public string _collection;
3838
public IStore _store;
39-
internal CompositeNode _predicate; // the defaut root predicate is an AND expression
39+
internal CompositeNode _predicate; // the default root predicate is an AND expression
4040
internal CompositeNode _currentPredicate; // the current predicate when Any() or All() is called
4141
public bool _processed = false;
4242
public bool _deduplicate = true;
@@ -131,8 +131,7 @@ public class DefaultQuery : IQuery
131131
private readonly object _compiledQuery = null;
132132
private readonly string _collection;
133133

134-
public static Dictionary<MethodInfo, Action<DefaultQuery, IStringBuilder, ISqlDialect, MethodCallExpression>> MethodMappings =
135-
new();
134+
public static Dictionary<MethodInfo, Action<DefaultQuery, IStringBuilder, ISqlDialect, MethodCallExpression>> MethodMappings = [];
136135

137136
static DefaultQuery()
138137
{
@@ -564,7 +563,7 @@ private ConstantExpression Evaluate(Expression expression)
564563
obj = null;
565564
}
566565

567-
_queryState._parameterBindings = _queryState._parameterBindings ?? new List<Action<object, ISqlBuilder>>();
566+
_queryState._parameterBindings ??= new List<Action<object, ISqlBuilder>>();
568567

569568
// Create a delegate that will be invoked every time a compiled query is reused,
570569
// which will re-evaluate the current node, for the current parameter.
@@ -624,7 +623,7 @@ private ConstantExpression Evaluate(Expression expression)
624623
return Expression.Constant(Expression.Lambda(expression).Compile().DynamicInvoke());
625624
}
626625

627-
private string GetBinaryOperator(Expression expression)
626+
private static string GetBinaryOperator(Expression expression)
628627
{
629628
switch (expression.NodeType)
630629
{

src/YesSql.Core/Session.cs

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,19 @@ public class Session : ISession
3333
protected string _tablePrefix;
3434
private readonly ISqlDialect _dialect;
3535
private readonly ILogger _logger;
36+
private readonly bool _withTracking;
3637

37-
public Session(Store store)
38+
public Session(Store store, bool withTracking = true)
3839
{
3940
_store = store;
4041
_tablePrefix = _store.Configuration.TablePrefix;
4142
_dialect = store.Dialect;
4243
_logger = store.Configuration.Logger;
43-
44+
_withTracking = withTracking;
4445
_defaultState = new SessionState();
4546
_collectionStates = new Dictionary<string, SessionState>()
4647
{
47-
[""] = _defaultState
48+
[string.Empty] = _defaultState
4849
};
4950
}
5051

@@ -58,7 +59,7 @@ public ISession RegisterIndexes(IIndexProvider[] indexProviders, string collecti
5859
}
5960
}
6061

61-
_indexes ??= new List<IIndexProvider>();
62+
_indexes ??= [];
6263

6364
_indexes.AddRange(indexProviders);
6465

@@ -81,7 +82,6 @@ private SessionState GetState(string collection)
8182
return state;
8283
}
8384

84-
[Obsolete]
8585
public void Save(object entity, bool checkConcurrency = false, string collection = null)
8686
=> SaveAsync(entity, checkConcurrency, collection).GetAwaiter().GetResult();
8787

@@ -140,10 +140,7 @@ public async Task SaveAsync(object entity, bool checkConcurrency = false, string
140140
state.IdentityMap.AddEntity(id, entity);
141141

142142
// Then assign a new identifier if it has one
143-
if (accessor != null)
144-
{
145-
accessor.Set(entity, id);
146-
}
143+
accessor?.Set(entity, id);
147144

148145
state.Saved.Add(entity);
149146
}
@@ -222,6 +219,23 @@ public void Detach(object entity, string collection)
222219

223220
var state = GetState(collection);
224221

222+
DetachInternal(entity, state);
223+
}
224+
225+
public void Detach(IEnumerable<object> entries, string collection)
226+
{
227+
CheckDisposed();
228+
229+
var state = GetState(collection);
230+
231+
foreach (var entry in entries)
232+
{
233+
DetachInternal(entry, state);
234+
}
235+
}
236+
237+
private static void DetachInternal(object entity, SessionState state)
238+
{
225239
state.Saved.Remove(entity);
226240
state.Updated.Remove(entity);
227241
state.Tracked.Remove(entity);
@@ -277,14 +291,11 @@ private async Task SaveEntityAsync(object entity, string collection)
277291
doc.Version = 1;
278292
}
279293

280-
if (versionAccessor != null)
281-
{
282-
versionAccessor.Set(entity, doc.Version);
283-
}
294+
versionAccessor?.Set(entity, doc.Version);
284295

285296
doc.Content = Store.Configuration.ContentSerializer.Serialize(entity);
286297

287-
_commands ??= new List<IIndexCommand>();
298+
_commands ??= [];
288299

289300
_commands.Add(new CreateDocumentCommand(doc, Store, collection));
290301

@@ -300,14 +311,12 @@ private async Task UpdateEntityAsync(object entity, bool tracked, string collect
300311
throw new ArgumentNullException(nameof(entity));
301312
}
302313

303-
var index = entity as IIndex;
304-
305314
if (entity is Document)
306315
{
307316
throw new ArgumentException("A document should not be saved explicitly");
308317
}
309318

310-
if (index != null)
319+
if (entity is IIndex index)
311320
{
312321
throw new ArgumentException("An index should not be saved explicitly");
313322
}
@@ -379,7 +388,7 @@ private async Task UpdateEntityAsync(object entity, bool tracked, string collect
379388

380389
oldDoc.Content = newContent;
381390

382-
_commands ??= new List<IIndexCommand>();
391+
_commands ??= [];
383392

384393
_commands.Add(new UpdateDocumentCommand(oldDoc, Store, version, collection));
385394
}
@@ -463,7 +472,7 @@ private async Task DeleteEntityAsync(object obj, string collection)
463472
// Update impacted indexes
464473
await MapDeleted(doc, obj, collection);
465474

466-
_commands ??= new List<IIndexCommand>();
475+
_commands ??= [];
467476

468477
// The command needs to come after any index deletion because of the database constraints
469478
_commands.Add(new DeleteDocumentCommand(doc, Store, collection));
@@ -535,7 +544,7 @@ public IEnumerable<T> Get<T>(IList<Document> documents, string collection) where
535544
// Are all the objects already in cache?
536545
foreach (var d in documents)
537546
{
538-
if (state.IdentityMap.TryGetEntityById(d.Id, out var entity))
547+
if (_withTracking && state.IdentityMap.TryGetEntityById(d.Id, out var entity))
539548
{
540549
result.Add((T)entity);
541550
}
@@ -568,9 +577,12 @@ public IEnumerable<T> Get<T>(IList<Document> documents, string collection) where
568577

569578
accessor?.Set(item, d.Id);
570579

571-
// track the loaded object
572-
state.IdentityMap.AddEntity(d.Id, item);
573-
state.IdentityMap.AddDocument(d);
580+
if (_withTracking)
581+
{
582+
// track the loaded object.
583+
state.IdentityMap.AddEntity(d.Id, item);
584+
state.IdentityMap.AddDocument(d);
585+
}
574586

575587
result.Add(item);
576588
}
@@ -657,7 +669,7 @@ public async Task FlushAsync()
657669
}
658670

659671
// prevent recursive calls in FlushAsync,
660-
// when autoflush is triggered from an IndexProvider
672+
// when auto-flush is triggered from an IndexProvider
661673
// for instance.
662674

663675
if (_flushing)

src/YesSql.Core/Store.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@ static Store()
5757

5858
private Store()
5959
{
60-
Indexes = new List<IIndexProvider>();
61-
ScopedIndexes = new List<Type>();
60+
Indexes = [];
61+
ScopedIndexes = [];
6262
}
6363

6464
/// <summary>
@@ -204,8 +204,8 @@ private void ValidateConfiguration()
204204
}
205205
}
206206

207-
public ISession CreateSession()
208-
=> new Session(this);
207+
public ISession CreateSession(bool withTracking = true)
208+
=> new Session(this, withTracking);
209209

210210
public void Dispose()
211211
{

0 commit comments

Comments
 (0)