EFCoreSecondLevelCacheInterceptor 5.3.2
dotnet add package EFCoreSecondLevelCacheInterceptor --version 5.3.2
NuGet\Install-Package EFCoreSecondLevelCacheInterceptor -Version 5.3.2
<PackageReference Include="EFCoreSecondLevelCacheInterceptor" Version="5.3.2" />
<PackageVersion Include="EFCoreSecondLevelCacheInterceptor" Version="5.3.2" />
<PackageReference Include="EFCoreSecondLevelCacheInterceptor" />
paket add EFCoreSecondLevelCacheInterceptor --version 5.3.2
#r "nuget: EFCoreSecondLevelCacheInterceptor, 5.3.2"
#:package EFCoreSecondLevelCacheInterceptor@5.3.2
#addin nuget:?package=EFCoreSecondLevelCacheInterceptor&version=5.3.2
#tool nuget:?package=EFCoreSecondLevelCacheInterceptor&version=5.3.2
EF Core Second Level Cache Interceptor
Second-level caching is a query cache. The results of Entity Framework (EF) commands are stored in the cache so that the same EF commands will retrieve their data from the cache rather than executing them against the database again.
How to Use
Using the second-level cache involves three mandatory steps: installing a provider, registering it, and adding the interceptor to your DbContext
.
1. Install a Preferred Cache Provider
First, you need to add the main package:
dotnet add package EFCoreSecondLevelCacheInterceptor
This library supports multiple caching providers, each available as a separate NuGet package. You must install at least one.
- In-Memory (Built-in): A simple in-memory cache provider.
dotnet add package EFCoreSecondLevelCacheInterceptor.MemoryCache
- StackExchange.Redis: Uses Redis with a preconfigured MessagePack serializer.
dotnet add package EFCoreSecondLevelCacheInterceptor.StackExchange.Redis
- FusionCache: Implements FusionCache as a cache provider.
dotnet add package EFCoreSecondLevelCacheInterceptor.FusionCache
- HybridCache: Implements .NET's HybridCache.
dotnet add package EFCoreSecondLevelCacheInterceptor.HybridCache
- EasyCaching.Core: A provider for the EasyCaching.Core library.
dotnet add package EFCoreSecondLevelCacheInterceptor.EasyCaching.Core
- CacheManager.Core: A provider for the CacheManager.Core library.
dotnet add package EFCoreSecondLevelCacheInterceptor.CacheManager.Core
- Custom: You can also implement your own provider.
2. Register the Cache Provider and Interceptor
In your Startup.cs
or Program.cs
, you need to register the EF Core second-level cache services and configure your chosen provider.
Example: Using the Built-in In-Memory Provider
public void ConfigureServices(IServiceCollection services)
{
// 1. Add EF Core Second Level Cache services
services.AddEFSecondLevelCache(options =>
options.UseMemoryCacheProvider().ConfigureLogging(true).UseCacheKeyPrefix("EF_")
// Fallback on db if the caching provider fails.
.UseDbCallsIfCachingProviderIsDown(TimeSpan.FromMinutes(1))
);
// 2. Add your DbContext
var connectionString = Configuration["ConnectionStrings:ApplicationDbContextConnection"];
services.AddConfiguredMsSqlDbContext(connectionString);
services.AddControllersWithViews();
}
(For detailed configuration examples of other providers, see the Available Cache Providers section below.)
3. Add the Interceptor to Your DbContext
Modify your DbContext
registration to add the SecondLevelCacheInterceptor
. This service is automatically registered and available via dependency injection.
public static class MsSqlServiceCollectionExtensions
{
public static IServiceCollection AddConfiguredMsSqlDbContext(this IServiceCollection services, string connectionString)
{
services.AddDbContextPool<ApplicationDbContext>((serviceProvider, optionsBuilder) =>
optionsBuilder
.UseSqlServer(
connectionString,
sqlServerOptionsBuilder =>
{
sqlServerOptionsBuilder
.CommandTimeout((int)TimeSpan.FromMinutes(3).TotalSeconds)
.EnableRetryOnFailure()
.MigrationsAssembly(typeof(MsSqlServiceCollectionExtensions).Assembly.FullName);
})
// Add the interceptor
.AddInterceptors(serviceProvider.GetRequiredService<SecondLevelCacheInterceptor>()));
return services;
}
}
4. Make Queries Cacheable
To cache a query, use the .Cacheable()
extension method. It can be placed anywhere in the LINQ query chain.
var post = context.Posts
.Where(x => x.Id > 0)
.OrderBy(x => x.Id)
.Cacheable() // Mark this query to be cached
.FirstOrDefault(); // Async methods are also supported.
The Cacheable()
method uses global settings by default, but you can override them for a specific query:
var post = context.Posts
.Where(x => x.Id > 0)
.OrderBy(x => x.Id)
// Override global settings for this query
.Cacheable(CacheExpirationMode.Sliding, TimeSpan.FromMinutes(5))
.FirstOrDefault();
How to Verify It's Working
To confirm that the interceptor is working, you should enable logging.
1. Enable Logging in the Configuration
Set ConfigureLogging(true)
during service registration.
services.AddEFSecondLevelCache(options =>
options.UseMemoryCacheProvider().ConfigureLogging(true)
);
2. Set Log Level to Debug
In your appsettings.json
, ensure the log level is set to Debug
.
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Debug",
"Microsoft": "Debug"
}
}
}
When you run a cacheable query for the first time, the data is fetched from the database and cached. On subsequent executions, you should see log messages indicating a cache hit:
Suppressed result with a TableRows[ee20d2d7-ffc7-4ff9-9484-e8d4eecde53e] from the cache[KeyHash: EB153BD4, CacheDependencies: Page.].
Using the TableRows[ee20d2d7-ffc7-4ff9-9484-e8d4eecde53e] from the cache.
Notes:
- The
Suppressed the result with the TableRows
message confirms the caching interceptor is working. - You will still see an
Executed DbCommand
log message from EF Core, but this is expected behavior. - You can also use a database profiler to verify that the query is only executed against the database on the first run.
- For more direct access to library events, you can pass an action to the
ConfigureLogging
method. See the Logging Events section for more details.
Caching Strategies
You can control caching globally or on a per-query basis.
Global Caching
Instead of marking individual queries with .Cacheable()
, you can define a global caching policy.
Caching All Queries
To cache every query in the application, use CacheAllQueries()
.
services.AddEFSecondLevelCache(options =>
{
options.UseMemoryCacheProvider().UseCacheKeyPrefix("EF_");
options.CacheAllQueries(CacheExpirationMode.Absolute, TimeSpan.FromMinutes(30));
});
- If you use
CacheAllQueries()
, you don't need to call.Cacheable()
on individual queries. - A specific
.Cacheable()
call on a query will override the global setting. - To exclude a specific query from global caching, use the
.NotCacheable()
method.
Caching Queries by Type or Table Name
To cache queries that involve specific entity types or database tables, use CacheQueriesContainingTypes
or CacheQueriesContainingTableNames
.
services.AddEFSecondLevelCache(options =>
{
options.UseMemoryCacheProvider()
.CacheQueriesContainingTableNames(
CacheExpirationMode.Absolute, TimeSpan.FromMinutes(30), TableNameComparison.ContainsOnly,
"posts", "products", "users"
);
});
- You can also exclude specific types or table names from caching using
CacheAllQueriesExceptContainingTypes
orCacheAllQueriesExceptContainingTableNames
.
Cache Invalidation
This library automatically invalidates cache entries when it detects CRUD operations (via its interceptor).
Automatic Invalidation
When you use SaveChanges()
or SaveChangesAsync()
, the interceptor identifies which tables have been modified and invalidates all cached queries that depend on those tables. No additional configuration is needed.
Limitation: ExecuteUpdate
and ExecuteDelete
EF Core does not trigger interceptors for bulk operations like ExecuteUpdate
and ExecuteDelete
for performance reasons. These methods execute raw SQL directly, bypassing EF Core's change tracking and related events. Therefore, cache invalidation will not happen automatically for these operations. You must invalidate the cache manually.
Manual Invalidation
You can manually invalidate the cache by injecting the IEFCacheServiceProvider
.
Invalidate the entire cache:
_cacheServiceProvider.ClearAllCachedEntries();
Invalidate cache entries related to specific tables: This is useful if you are using an external tool like
SqlTableDependency
to monitor database changes.// The prefix "EF_" should match your configured UseCacheKeyPrefix. var tableNames = new HashSet<string> { "EF_TableName1", "EF_TableName2" }; _cacheServiceProvider.InvalidateCacheDependencies(new EFCacheKey(tableNames));
Invalidation Notifications
To receive notifications when cache entries are invalidated, use the NotifyCacheInvalidation
method.
services.AddEFSecondLevelCache(options =>
{
options.UseMemoryCacheProvider()
.NotifyCacheInvalidation(invalidationInfo =>
{
var logger = invalidationInfo.ServiceProvider
.GetRequiredService<ILoggerFactory>()
.CreateLogger("NotifyCacheInvalidation");
var message = invalidationInfo.ClearAllCachedEntries
? "Invalidated all cache entries!"
: $"Invalidated dependencies: [{string.Join(", ", invalidationInfo.CacheDependencies)}]";
logger.LogWarning(message);
});
});
Advanced Configuration
Skipping Caching
You can define rules to skip caching for certain queries based on their command text or results.
- Skip by Command Text:
services.AddEFSecondLevelCache(options => { options.SkipCachingCommands(commandText => commandText.Contains("NEWID()", StringComparison.InvariantCultureIgnoreCase)); });
- Skip by Result:
services.AddEFSecondLevelCache(options => { // Don't cache null values or empty result sets. options.SkipCachingResults(result => result.Value == null || (result.Value is EFTableRows rows && rows.RowsCount == 0)); });
Skipping Invalidation
In some cases, you may want to prevent a command from invalidating the cache, such as when updating a view counter.
services.AddEFSecondLevelCache(options =>
{
options.SkipCacheInvalidationCommands(commandText =>
// Assumes a command that only updates a post's view count contains this text.
commandText.Contains("UPDATE [Posts] SET [Views]", StringComparison.InvariantCultureIgnoreCase));
});
Overriding Cache Policy
Use OverrideCachePolicy()
to dynamically change the caching policy for a query at runtime.
services.AddEFSecondLevelCache(options =>
{
options.OverrideCachePolicy(context =>
{
// Don't cache any CRUD commands
if (context.IsCrudCommand)
{
return null;
}
// Use a "never remove" policy for queries on the 'posts' table
if (context.CommandTableNames.Contains("posts"))
{
return new EFCachePolicy().ExpirationMode(CacheExpirationMode.NeverRemove);
}
// Use the default calculated policy for all other queries
return null;
});
});
Logging Events
You can subscribe to caching events directly instead of parsing log files.
.ConfigureLogging(enable: environment.IsDevelopment(), cacheableEvent: args =>
{
switch (args.EventId)
{
case CacheableLogEventId.CacheHit:
break;
case CacheableLogEventId.QueryResultCached:
break;
case CacheableLogEventId.QueryResultInvalidated:
// Example of logging a specific event
args.ServiceProvider.GetRequiredService<ILoggerFactory>()
.CreateLogger(nameof(EFCoreSecondLevelCacheInterceptor))
.LogWarning("{EventId} -> {Message} -> {CommandText}",
args.EventId, args.Message, args.CommandText);
break;
// ... handle other events
}
})
Other Configurations
- Custom Hash Provider: Replace the default
xxHash
algorithm by implementingIEFHashProvider
and registering it withoptions.UseCustomHashProvider<T>()
. - Custom JsonSerializerOptions: Control serialization behavior by passing
JsonSerializerOptions
tooptions.UseJsonSerializerOptions(options)
. - Disable Interceptor: Temporarily disable the interceptor via
options.EnableCachingInterceptor(false)
. - Skip DbContexts: Exclude certain
DbContext
types from caching withoptions.SkipCachingDbContexts()
.
Available Cache Providers
Below are setup examples for the various supported cache providers.
1. EFCoreSecondLevelCacheInterceptor.StackExchange.Redis
This provider uses StackExchange.Redis
and is preconfigured with a MessagePack serializer.
var redisOptions = new ConfigurationOptions
{
EndPoints = { { "127.0.0.1", 6379 } },
AllowAdmin = true,
ConnectTimeout = 10000
};
services.AddEFSecondLevelCache(options =>
options.UseStackExchangeRedisCacheProvider(redisOptions, TimeSpan.FromMinutes(5)));
2. EFCoreSecondLevelCacheInterceptor.FusionCache
This provider uses FusionCache.
// 1. Add FusionCache services with desired options
services.AddFusionCache()
.WithOptions(options =>
{
options.DefaultEntryOptions = new FusionCacheEntryOptions
{
Duration = TimeSpan.FromMinutes(1),
IsFailSafeEnabled = true,
FailSafeMaxDuration = TimeSpan.FromHours(2),
// ... other FusionCache options
};
});
// 2. Add the EF Core Caching provider
services.AddEFSecondLevelCache(options => options.UseFusionCacheProvider());
3. EFCoreSecondLevelCacheInterceptor.HybridCache
This provider uses the new .NET HybridCache.
services.AddEFSecondLevelCache(options => options.UseHybridCacheProvider());
4. Using EasyCaching.Core
This allows you to use EasyCaching.Core
as a highly configurable cache manager.
- In-Memory with EasyCaching.Core:
const string providerName = "InMemory1"; services.AddEFSecondLevelCache(options => options.UseEasyCachingCoreProvider(providerName, isHybridCache: false) .UseCacheKeyPrefix("EF_") ); // Add EasyCaching.Core in-memory provider services.AddEasyCaching(options => { options.UseInMemory(config => { config.DBConfig = new InMemoryCachingOptions { SizeLimit = 10000 }; config.MaxRdSecond = 120; }, providerName); });
- Redis with EasyCaching.Core:
First, install the required packages:
EasyCaching.Redis
andEasyCaching.Serialization.MessagePack
.const string providerName = "Redis1"; services.AddEFSecondLevelCache(options => options.UseEasyCachingCoreProvider(providerName, isHybridCache: false) .UseCacheKeyPrefix("EF_") ); // Add EasyCaching.Core Redis provider services.AddEasyCaching(option => { option.UseRedis(config => { config.DBConfig.Endpoints.Add(new EasyCaching.Core.Configurations.ServerEndPoint("127.0.0.1", 6379)); config.SerializerName = "Pack"; }, providerName) .WithMessagePack("Pack"); // Configure MessagePack serializer });
- Dynamic Provider with EasyCaching.Core for Multi-tenancy:
You can dynamically select a cache provider based on the current context, such as a tenant ID from an HTTP request header.
services.AddEFSecondLevelCache(options => options.UseEasyCachingCoreProvider( (serviceProvider, cacheKey) => "redis-db-" + serviceProvider.GetRequiredService<IHttpContextAccessor>().HttpContext.Request.Headers["tenant-id"], isHybridCache: false) );
5. Using CacheManager.Core
This allows you to use CacheManager.Core
as a cache manager. Note: This library is not actively maintained.
- In-Memory with CacheManager.Core:
First, install:
CacheManager.Core
,CacheManager.Microsoft.Extensions.Caching.Memory
, andCacheManager.Serialization.Json
.services.AddEFSecondLevelCache(options => options.UseCacheManagerCoreProvider()); // Add CacheManager.Core services services.AddSingleton(typeof(ICacheManager<>), typeof(BaseCacheManager<>)); services.AddSingleton(typeof(ICacheManagerConfiguration), new CacheManager.Core.CacheConfigurationBuilder() .WithJsonSerializer() .WithMicrosoftMemoryCacheHandle("MemoryCache1") .Build());
- Redis with CacheManager.Core:
First, install the
CacheManager.StackExchange.Redis
package.services.AddEFSecondLevelCache(options => options.UseCacheManagerCoreProvider()); // Add CacheManager.Core Redis services const string redisConfigurationKey = "redis"; services.AddSingleton(typeof(ICacheManagerConfiguration), new CacheManager.Core.CacheConfigurationBuilder() .WithJsonSerializer() .WithUpdateMode(CacheUpdateMode.Up) .WithRedisConfiguration(redisConfigurationKey, config => { config.WithAllowAdmin() .WithDatabase(0) .WithEndpoint("localhost", 6379) .EnableKeyspaceEvents(); }) .WithRedisCacheHandle(redisConfigurationKey) .Build()); services.AddSingleton(typeof(ICacheManager<>), typeof(BaseCacheManager<>));
6. Using a Custom Cache Provider
If the provided cache providers don't meet your needs, you can implement the IEFCacheServiceProvider
interface and then register it using the options.UseCustomCacheProvider<T>()
method.
Guidance and Best Practices
When to Use
Good candidates for query caching are global site settings and public data, such as infrequently changing articles or comments. It can also be beneficial to cache data specific to a user, so long as the cache expires frequently enough relative to the size of the user base that memory consumption remains acceptable. Small, per-user data that changes frequently is better held in other stores like user claims, which are stored in cookies, than in this cache.
Scope
This cache is scoped to the application, not the current user. It does not use session variables. Accordingly, when retrieving cached per-user data, be sure your queries include a filter for the user ID, such as .Where(x => x.UserId == id)
.
Invalidation
The cache is updated when an entity is changed (inserted, updated, or deleted) via a DbContext
that uses this interceptor. If the database is modified through other means, such as a stored procedure, a trigger, or another application, the cache will become stale.
Transactions
To avoid complications, all queries inside an explicit transaction (context.Database.BeginTransaction()
) will not be cached by default. However, cache invalidation for CRUD operations within the transaction will still occur. You can override this behavior and allow caching within explicit transactions by using the .AllowCachingWithExplicitTransactions(true)
setting.
Database Provider Compatibility
Some database providers do not natively support special data types such as DateTimeOffset
or TimeSpan
. For these scenarios, you will need to configure the appropriate value converters in your DbContext
.
How to Upgrade to Version 5
To support more advanced caching providers, this library was split into multiple assemblies and NuGet packages in version 5.
- Remove the old dependency:
EFCoreSecondLevelCacheInterceptor
. - Add the new main package:
dotnet add package EFCoreSecondLevelCacheInterceptor
- Add a provider package: The main package no longer includes a built-in provider. You must install one of the new provider packages.
- For the previous built-in In-Memory cache, install:
dotnet add package EFCoreSecondLevelCacheInterceptor.MemoryCache
- For the previous EasyCaching.Core provider, install:
dotnet add package EFCoreSecondLevelCacheInterceptor.EasyCaching.Core
- For the previous CacheManager.Core provider, install:
dotnet add package EFCoreSecondLevelCacheInterceptor.CacheManager.Core
- For the previous built-in In-Memory cache, install:
If you were using a custom cache provider via options.UseCustomCacheProvider<T>()
, you do not need to install a new provider package.
Samples
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 is compatible. net5.0-windows was computed. net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 is compatible. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
.NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 is compatible. |
.NET Standard | netstandard2.0 is compatible. netstandard2.1 is compatible. |
.NET Framework | net461 was computed. net462 is compatible. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen40 was computed. tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETCoreApp 3.1
- AsyncKeyedLock (>= 7.1.6)
- Microsoft.EntityFrameworkCore (>= 3.1.0 && < 6.0.0)
- Microsoft.EntityFrameworkCore.Relational (>= 3.1.0 && < 6.0.0)
-
.NETFramework 4.6.2
- AsyncKeyedLock (>= 7.1.6)
- Microsoft.EntityFrameworkCore (>= 3.1.0 && < 4.0.0)
- Microsoft.EntityFrameworkCore.Relational (>= 3.1.0 && < 4.0.0)
-
.NETStandard 2.0
- AsyncKeyedLock (>= 7.1.6)
- Microsoft.EntityFrameworkCore (>= 3.1.0 && < 4.0.0)
- Microsoft.EntityFrameworkCore.Relational (>= 3.1.0 && < 4.0.0)
-
.NETStandard 2.1
- AsyncKeyedLock (>= 7.1.6)
- Microsoft.EntityFrameworkCore (>= 5.0.0)
- Microsoft.EntityFrameworkCore.Relational (>= 5.0.0)
-
net5.0
- AsyncKeyedLock (>= 7.1.6)
- Microsoft.EntityFrameworkCore (>= 5.0.0)
- Microsoft.EntityFrameworkCore.Relational (>= 5.0.0)
-
net6.0
- AsyncKeyedLock (>= 7.1.6)
- Microsoft.EntityFrameworkCore (>= 6.0.0)
- Microsoft.EntityFrameworkCore.Relational (>= 6.0.0)
- Microsoft.Extensions.Caching.Memory (>= 6.0.2)
-
net7.0
- AsyncKeyedLock (>= 7.1.6)
- Microsoft.EntityFrameworkCore (>= 7.0.0)
- Microsoft.EntityFrameworkCore.Relational (>= 7.0.0)
-
net8.0
- AsyncKeyedLock (>= 7.1.6)
- Microsoft.EntityFrameworkCore (>= 8.0.0)
- Microsoft.EntityFrameworkCore.Relational (>= 8.0.0)
- Microsoft.Extensions.Caching.Memory (>= 8.0.1)
- System.IO.Hashing (>= 8.0.0)
-
net9.0
- AsyncKeyedLock (>= 7.1.6)
- Microsoft.EntityFrameworkCore (>= 9.0.0)
- Microsoft.EntityFrameworkCore.Relational (>= 9.0.0)
- System.IO.Hashing (>= 9.0.0)
NuGet packages (20)
Showing the top 5 NuGet packages that depend on EFCoreSecondLevelCacheInterceptor:
Package | Downloads |
---|---|
NanoCore
The project is inspired by years of tedious repetitions, continuously re-writing similar code-snippets and libraries, to handle common functionality, not related to the business domain, such as logging, data persistence, message queuing, documentation, validation and similar. |
|
EaCloud.EntityFrameworkCore
EaCloud 数据访问组件,封装 EntityFrameworkCore 数据访问功能的实现。 |
|
EFCoreSecondLevelCacheInterceptor.MemoryCache
Entity Framework Core Second Level Caching Library. |
|
EFCoreSecondLevelCacheInterceptor.EasyCaching.Core
Entity Framework Core Second Level Caching Library. |
|
R8.EntityFrameworkCore
A predefined pattern for EntityFramework Core to easily store Audits. |
GitHub repositories (5)
Showing the top 5 popular GitHub repositories that depend on EFCoreSecondLevelCacheInterceptor:
Repository | Stars |
---|---|
ldqk/Masuit.MyBlogs
基于C#/.NET8的 masuit.org个人博客站项目源码,https://masuit.org ,供参考、学习、引用、非商业性质的部署。
|
|
rabbal/DNTFrameworkCore
Lightweight and Extensible Infrastructure for Building Web Applications - Web Application Framework
|
|
TheUltimateC0der/listrr
listrr.pro creates and maintains lists on trakt.tv completely automated based on your filters.
|
|
vesoapp/veso
Open source media server.
|
|
Kukks/NNostr
A Nostr Relay and Client written in C#
|
Version | Downloads | Last Updated |
---|---|---|
5.3.2 | 159 | 2 days ago |
5.3.1 | 48,845 | 2 months ago |
5.3.0 | 18,589 | 3 months ago |
5.2.5 | 10,924 | 4 months ago |
5.2.4 | 41,548 | 4 months ago |
5.2.3 | 10,081 | 5 months ago |
5.2.2 | 12,368 | 5 months ago |
5.2.1 | 41,241 | 6 months ago |
5.2.0 | 2,814 | 6 months ago |
5.1.1 | 2,865 | 7 months ago |
5.0.0 | 76,858 | 8 months ago |
4.9.0 | 30,446 | 9 months ago |
4.8.8 | 43,784 | 10 months ago |
4.8.7 | 8,694 | 10 months ago |
4.8.6 | 351 | 10 months ago |
4.8.5 | 867 | 10 months ago |
4.8.4 | 24,785 | 10/25/2024 |
4.8.3 | 32,903 | 10/13/2024 |
4.8.2 | 19,981 | 9/28/2024 |
4.8.1 | 463 | 9/27/2024 |
4.8.0 | 9,869 | 9/21/2024 |
4.7.1 | 97,671 | 8/21/2024 |
4.7.0 | 10,376 | 8/14/2024 |
4.6.0 | 38,719 | 7/21/2024 |
4.5.0 | 139,673 | 5/24/2024 |
4.4.3 | 92,696 | 4/22/2024 |
4.4.2 | 979 | 4/21/2024 |
4.4.1 | 17,457 | 4/5/2024 |
4.4.0 | 578 | 4/5/2024 |
4.3.1 | 16,758 | 3/31/2024 |
4.3.0 | 564 | 3/30/2024 |
4.2.3 | 57,582 | 3/2/2024 |
4.2.2 | 22,497 | 2/20/2024 |
4.2.1 | 10,622 | 2/19/2024 |
4.2.0 | 48,445 | 1/24/2024 |
4.1.2 | 11,962 | 1/19/2024 |
4.1.1 | 56,993 | 12/12/2023 |
4.1.0 | 1,610 | 12/9/2023 |
4.0.1 | 585 | 12/9/2023 |
4.0.0 | 65,971 | 10/31/2023 |
3.9.5 | 17,847 | 10/24/2023 |
3.9.4 | 7,611 | 10/17/2023 |
3.9.3 | 689 | 10/17/2023 |
3.9.2 | 370,164 | 5/27/2023 |
3.9.1 | 53,406 | 5/1/2023 |
3.9.0 | 5,537 | 4/27/2023 |
3.8.8 | 32,185 | 4/15/2023 |
3.8.7 | 374 | 4/15/2023 |
3.8.6 | 112,957 | 3/15/2023 |
3.8.5 | 68,871 | 2/25/2023 |
3.8.3 | 227,166 | 2/3/2023 |
3.8.2 | 72,281 | 1/10/2023 |
3.8.1 | 76,678 | 12/9/2022 |
3.8.0 | 31,219 | 11/26/2022 |
3.7.5 | 21,022 | 11/12/2022 |
3.7.4 | 3,390 | 11/9/2022 |
3.7.3 | 28,057 | 10/14/2022 |
3.7.2 | 1,918 | 10/10/2022 |
3.7.1 | 1,005 | 10/10/2022 |
3.7.0 | 17,649 | 9/30/2022 |
3.6.3 | 85,397 | 8/3/2022 |
3.6.2 | 237,030 | 7/8/2022 |
3.6.1 | 58,010 | 6/15/2022 |
3.6.0 | 1,431 | 6/14/2022 |
3.5.1 | 40,309 | 6/9/2022 |
3.5.0 | 11,459 | 5/25/2022 |
3.4.0 | 161,565 | 3/18/2022 |
3.3.0 | 160,316 | 1/8/2022 |
3.2.5 | 23,428 | 1/6/2022 |
3.2.4 | 41,931 | 12/8/2021 |
3.2.3 | 34,670 | 11/23/2021 |
3.2.2 | 7,236 | 11/18/2021 |
3.2.1 | 13,064 | 11/11/2021 |
3.2.0 | 114,442 | 9/1/2021 |
3.1.2 | 66,081 | 7/12/2021 |
3.1.1 | 95,200 | 6/4/2021 |
3.1.0 | 13,260 | 5/26/2021 |
3.0.0 | 3,871 | 5/20/2021 |
2.9.0 | 4,945 | 5/16/2021 |
2.8.0 | 2,417 | 5/9/2021 |
2.7.0 | 32,311 | 4/30/2021 |
2.6.0 | 1,232 | 4/29/2021 |
2.5.0 | 17,186 | 4/14/2021 |
2.4.1 | 106,848 | 2/9/2021 |
2.4.0 | 4,498 | 2/5/2021 |
2.3.1 | 28,494 | 1/20/2021 |
2.3.0 | 19,518 | 12/28/2020 |
2.2.0 | 4,990 | 12/21/2020 |
2.1.0 | 23,875 | 12/9/2020 |
2.0.1 | 14,578 | 11/11/2020 |
2.0.0 | 12,790 | 10/16/2020 |
1.9.2 | 1,998 | 10/15/2020 |
1.9.1 | 418 | 10/15/2020 |
1.9.0 | 5,804 | 10/14/2020 |
1.8.2 | 265,300 | 7/25/2020 |
1.8.1 | 4,135 | 7/21/2020 |
1.8.0 | 1,942 | 7/17/2020 |
1.7.2 | 28,484 | 5/18/2020 |
1.7.1 | 1,212 | 5/14/2020 |
1.7.0 | 24,985 | 5/1/2020 |
1.6.0 | 4,993 | 4/24/2020 |
1.5.5 | 1,451 | 4/22/2020 |
1.5.4 | 1,618 | 4/19/2020 |
1.5.3 | 1,256 | 4/17/2020 |
1.5.2 | 1,470 | 4/13/2020 |
1.5.1 | 10,278 | 4/3/2020 |
1.5.0 | 1,193 | 4/3/2020 |
1.4.0 | 4,065 | 4/3/2020 |
1.3.4 | 2,294 | 3/31/2020 |
1.3.3 | 1,235 | 3/30/2020 |
1.3.2 | 1,123 | 3/30/2020 |
1.3.1 | 2,017 | 3/22/2020 |
1.3.0 | 1,175 | 3/22/2020 |
1.2.0 | 1,205 | 3/22/2020 |
1.1.2 | 1,276 | 3/20/2020 |
1.1.0 | 2,808 | 3/2/2020 |
1.0.0 | 2,124 | 2/15/2020 |