Cache control
Command lifetime
QueryCommand is reusable and can be shared when its lifetime is the application. Dispose a command that owns a shorter-lived cache when that scope ends.
const string sql = "SELECT AlbumId AS Id, Title FROM albums ORDER BY AlbumId";
using var command = new QueryCommand(sql);
List<Album> albums = command.Query<List<Album>>(cnn);
Disposal invalidates the command's parsers and parameter accessors, releases its mapper, and makes the command unusable. CachedTypeParser<T> and the non-generic CachedTypeParser are disposable as well, so scope them in the same way when they are not application-lifetime caches.
QueryCommand parsers
static readonly QueryCommand GetAlbums = new("SELECT AlbumId AS Id, Title FROM albums ORDER BY AlbumId");
int removed = GetAlbums.InvalidateParsers();
The overload without a scope removes the command references and removes a parser globally only when no other command still uses it.
GetAlbums.InvalidateParsers(QueryParserInvalidationScope.Local);
GetAlbums.InvalidateParsers(QueryParserInvalidationScope.GlobalIfUnused);
GetAlbums.InvalidateParsers(QueryParserInvalidationScope.Global);
Local remove this command reference
GlobalIfUnused also remove the global parser when no other command uses it
Global remove the parser globally and clear command references to it
One exact parser can be invalidated through the same scopes.
GetAlbums.InvalidateParser(parser, QueryParserInvalidationScope.GlobalIfUnused);
Parameter accessors
public sealed class AlbumFilter
{
public int ArtistId { get; init; }
}
static readonly QueryCommand GetAlbum = new("SELECT AlbumId AS Id, Title FROM albums WHERE ArtistId = @ArtistId");
GetAlbum.Query<List<Album>>(cnn, new AlbumFilter { ArtistId = 7 });
(Type ParameterType, ParameterAccessorKinds Accessors)[] accessors = GetAlbum.GetCachedParameterAccessors();
GetAlbum.InvalidateParameterAccessor(typeof(AlbumFilter), ParameterAccessorKinds.Direct);
GetAlbum.InvalidateParameterAccessor(typeof(AlbumFilter), ParameterAccessorKinds.UseWith);
GetAlbum.InvalidateParameterAccessor(typeof(AlbumFilter), ParameterAccessorKinds.Both);
These calls affect parameter source accessors. They do not invalidate row parsers.
SQL string command cache
const string sql = "SELECT AlbumId AS Id, Title FROM albums ORDER BY AlbumId";
QueryCommand command = ConnectionQueryExtensions.GetOrCreateCommand(sql);
bool removed = ConnectionQueryExtensions.CommandCache.TryRemove(sql, out QueryCommand? cached);
The exact SQL string is the cache key. Removing the key does not dispose the removed command.
Generic CachedTypeParser
static readonly CachedTypeParser<List<Album>> AlbumParser = new();
using DbCommand command = cnn.CreateCommand();
command.CommandText = "SELECT AlbumId AS Id, Title FROM albums ORDER BY AlbumId";
List<Album> first = AlbumParser.Query(command, disposeCommand: false);
AlbumParser.Invalidate();
List<Album> second = AlbumParser.Query(command, disposeCommand: false);
Non generic CachedTypeParser
static readonly CachedTypeParser SchemaParser = new();
using DbCommand command = cnn.CreateCommand();
command.CommandText = "SELECT AlbumId AS Id, Title FROM albums WHERE AlbumId = 12";
Album album = SchemaParser.Query<Album>(command, disposeCommand: false);
DynaObject row = SchemaParser.Query<DynaObject>(command, disposeCommand: false);
SchemaParser.Invalidate<Album>();
// The DynaObject parser remains cached.
Global parser cache
int removed = TypeParser.Invalidate(columns, ParserInvalidationMode.CheckUsage);
CheckUsage leaves an exact parser alive when another cache still reports that it retains it.
bool removed = TypeParser.Invalidate(parser, ParserInvalidationMode.InvalidateReferences);
InvalidateReferences asks subscribed caches to release that exact parser instance.
int removed = TypeParser.InvalidateAll(ParserInvalidationMode.InvalidateReferences);
All globally cached parsers can be removed with the same invalidation mode.
bool disposed = TypeParser.Release(parser);
Release disposes the parser only when neither the global cache nor another subscribed cache still retains it.