Skip to content

Commit

Permalink
Add a way to specify no-tracking to reduce caching on demand (#524)
Browse files Browse the repository at this point in the history
Fixes #522
Fixes #523
  • Loading branch information
MikeAlhayek authored Jan 26, 2024
1 parent 6c139c1 commit 11a51bf
Show file tree
Hide file tree
Showing 8 changed files with 132 additions and 58 deletions.
18 changes: 9 additions & 9 deletions src/YesSql.Abstractions/IQuery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,13 @@ public interface IQuery<T> where T : class
/// </summary>
/// <typeparam name="TIndex">The index to filter on.</typeparam>
IQuery<T, TIndex> With<TIndex>() where TIndex : class, IIndex;

/// <summary>
/// Filters the documents with a constraint on the specified index.
/// </summary>
/// <typeparam name="TIndex">The index to filter on.</typeparam>
IQuery<T, TIndex> With<TIndex>(Expression<Func<TIndex, bool>> predicate) where TIndex : class, IIndex;

/// <summary>
/// Skips the specified number of document.
/// </summary>
Expand Down Expand Up @@ -128,7 +128,7 @@ public interface IQueryIndex<T> where T : IIndex
/// Joins the document table with an index, and filter it with a predicate.
/// </summary>
IQueryIndex<TIndex> With<TIndex>(Expression<Func<TIndex, bool>> predicate) where TIndex : class, IIndex;

/// <summary>
/// Adds a custom Where clause to the query.
/// </summary>
Expand Down Expand Up @@ -163,7 +163,7 @@ public interface IQueryIndex<T> where T : IIndex
/// Adds an OrderBy clause using a custom lambda expression.
/// </summary>
IQueryIndex<T> ThenBy(Expression<Func<T, object>> keySelector);

/// <summary>
/// Adds a descending OrderBy clause using a custom lambda expression.
/// </summary>
Expand Down Expand Up @@ -221,7 +221,7 @@ public interface IQuery<T, TIndex> : IQuery<T>
/// Adds a custom Where clause to the query using a specific dialect.
/// </summary>
IQuery<T, TIndex> Where(Func<ISqlDialect, string> sql);

/// <summary>
/// Adds a named parameter to the query.
/// </summary>
Expand All @@ -236,19 +236,19 @@ public interface IQuery<T, TIndex> : IQuery<T>
/// Sets an OrderBy clause using a custom lambda expression.
/// </summary>
IQuery<T, TIndex> OrderBy(Expression<Func<TIndex, object>> keySelector);

/// <summary>
/// Sets an OrderBy clause using a custom SQL statement.
/// </summary>
IQuery<T, TIndex> OrderBy(string sql);

IQuery<T, TIndex> OrderByDescending(Expression<Func<TIndex, object>> keySelector);

/// <summary>
/// Sets a descending OrderBy clause using a custom SQL statement.
/// </summary>
IQuery<T, TIndex> OrderByDescending(string sql);

/// <summary>
/// Sets a random OrderBy clause.
/// </summary>
Expand Down
10 changes: 10 additions & 0 deletions src/YesSql.Abstractions/ISession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,16 @@ public interface ISession : IDisposable, IAsyncDisposable
/// </remarks>
void Detach(object item, string collection = null);

/// <summary>
/// Removes multiple items from the identity map.
/// </summary>
/// <remarks>
/// This method can be used to remove multiple items that should not be served again from the cache.
/// For instance when its state as changed and any subsequent query should not return the
/// modified instance but a fresh one.
/// </remarks>
void Detach(IEnumerable<object> entries, string collection = null);

/// <summary>
/// Loads objects by id.
/// </summary>
Expand Down
2 changes: 1 addition & 1 deletion src/YesSql.Abstractions/IStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public interface IStore : IDisposable
/// <summary>
/// Creates a new <see cref="ISession"/> to communicate with the <see cref="IStore"/>.
/// </summary>
ISession CreateSession();
ISession CreateSession(bool withTracking = true);

/// <summary>
/// Registers index providers.
Expand Down
25 changes: 10 additions & 15 deletions src/YesSql.Abstractions/SessionExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
Expand All @@ -13,16 +13,17 @@ public static class SessionExtensions
/// Loads an object by its id.
/// </summary>
/// <returns>The object or <c>null</c>.</returns>
public async static Task<T> GetAsync<T>(this ISession session, long id, string collection = null) where T : class
{
return (await session.GetAsync<T>(new[] { id }, collection)).FirstOrDefault();
}
public async static Task<T> GetAsync<T>(this ISession session, long id, string collection = null)
where T : class
=> (await session.GetAsync<T>([id], collection)).FirstOrDefault();

/// <summary>
/// Loads objects by id.
/// </summary>
/// <returns>A collection of objects in the same order they were defined.</returns>
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);
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);

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

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

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

/// <summary>
/// Saves a new or existing object to the store, and updates
Expand Down
9 changes: 4 additions & 5 deletions src/YesSql.Core/Services/DefaultQuery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public QueryState(ISqlBuilder sqlBuilder, IStore store, string collection)
public List<Action<object, ISqlBuilder>> _parameterBindings;
public string _collection;
public IStore _store;
internal CompositeNode _predicate; // the defaut root predicate is an AND expression
internal CompositeNode _predicate; // the default root predicate is an AND expression
internal CompositeNode _currentPredicate; // the current predicate when Any() or All() is called
public bool _processed = false;
public bool _deduplicate = true;
Expand Down Expand Up @@ -131,8 +131,7 @@ public class DefaultQuery : IQuery
private readonly object _compiledQuery = null;
private readonly string _collection;

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

static DefaultQuery()
{
Expand Down Expand Up @@ -564,7 +563,7 @@ private ConstantExpression Evaluate(Expression expression)
obj = null;
}

_queryState._parameterBindings = _queryState._parameterBindings ?? new List<Action<object, ISqlBuilder>>();
_queryState._parameterBindings ??= new List<Action<object, ISqlBuilder>>();

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

private string GetBinaryOperator(Expression expression)
private static string GetBinaryOperator(Expression expression)
{
switch (expression.NodeType)
{
Expand Down
60 changes: 36 additions & 24 deletions src/YesSql.Core/Session.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,19 @@ public class Session : ISession
protected string _tablePrefix;
private readonly ISqlDialect _dialect;
private readonly ILogger _logger;
private readonly bool _withTracking;

public Session(Store store)
public Session(Store store, bool withTracking = true)
{
_store = store;
_tablePrefix = _store.Configuration.TablePrefix;
_dialect = store.Dialect;
_logger = store.Configuration.Logger;

_withTracking = withTracking;
_defaultState = new SessionState();
_collectionStates = new Dictionary<string, SessionState>()
{
[""] = _defaultState
[string.Empty] = _defaultState
};
}

Expand All @@ -58,7 +59,7 @@ public ISession RegisterIndexes(IIndexProvider[] indexProviders, string collecti
}
}

_indexes ??= new List<IIndexProvider>();
_indexes ??= [];

_indexes.AddRange(indexProviders);

Expand All @@ -81,7 +82,6 @@ private SessionState GetState(string collection)
return state;
}

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

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

// Then assign a new identifier if it has one
if (accessor != null)
{
accessor.Set(entity, id);
}
accessor?.Set(entity, id);

state.Saved.Add(entity);
}
Expand Down Expand Up @@ -222,6 +219,23 @@ public void Detach(object entity, string collection)

var state = GetState(collection);

DetachInternal(entity, state);
}

public void Detach(IEnumerable<object> entries, string collection)
{
CheckDisposed();

var state = GetState(collection);

foreach (var entry in entries)
{
DetachInternal(entry, state);
}
}

private static void DetachInternal(object entity, SessionState state)
{
state.Saved.Remove(entity);
state.Updated.Remove(entity);
state.Tracked.Remove(entity);
Expand Down Expand Up @@ -277,14 +291,11 @@ private async Task SaveEntityAsync(object entity, string collection)
doc.Version = 1;
}

if (versionAccessor != null)
{
versionAccessor.Set(entity, doc.Version);
}
versionAccessor?.Set(entity, doc.Version);

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

_commands ??= new List<IIndexCommand>();
_commands ??= [];

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

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

var index = entity as IIndex;

if (entity is Document)
{
throw new ArgumentException("A document should not be saved explicitly");
}

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

oldDoc.Content = newContent;

_commands ??= new List<IIndexCommand>();
_commands ??= [];

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

_commands ??= new List<IIndexCommand>();
_commands ??= [];

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

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

// track the loaded object
state.IdentityMap.AddEntity(d.Id, item);
state.IdentityMap.AddDocument(d);
if (_withTracking)
{
// track the loaded object.
state.IdentityMap.AddEntity(d.Id, item);
state.IdentityMap.AddDocument(d);
}

result.Add(item);
}
Expand Down Expand Up @@ -657,7 +669,7 @@ public async Task FlushAsync()
}

// prevent recursive calls in FlushAsync,
// when autoflush is triggered from an IndexProvider
// when auto-flush is triggered from an IndexProvider
// for instance.

if (_flushing)
Expand Down
8 changes: 4 additions & 4 deletions src/YesSql.Core/Store.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ static Store()

private Store()
{
Indexes = new List<IIndexProvider>();
ScopedIndexes = new List<Type>();
Indexes = [];
ScopedIndexes = [];
}

/// <summary>
Expand Down Expand Up @@ -204,8 +204,8 @@ private void ValidateConfiguration()
}
}

public ISession CreateSession()
=> new Session(this);
public ISession CreateSession(bool withTracking = true)
=> new Session(this, withTracking);

public void Dispose()
{
Expand Down
Loading

0 comments on commit 11a51bf

Please sign in to comment.