/
githubmirror
/
aspnetcore
Обзор
Документация
Войти
/
githubmirror
/
aspnetcore
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Caching/SqlServer/test/SqlServerCacheWithDatabaseTest.cs
770 строк
28 KB
William Godbe
Rename Microsoft.Aspnetcore.Testing to Microsoft.AspNetCore.InternalTesting (#51713)
02 ноя 2023, 03:15
Не верифицирован
02 ноя 2023, 03:15
fc8deca
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections.Generic; using System.Data; using System.Globalization; using System.Text; using System.Threading.Tasks; using Microsoft.AspNetCore.InternalTesting; using Microsoft.Data.SqlClient; using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Internal; using Xunit; namespace Microsoft.Extensions.Caching.SqlServer; public class SqlServerCacheWithDatabaseTest { // These tests are disabled by default. To run them, run the "run-db-tests.ps1" script private const string ConnectionStringKey = "ConnectionString"; private const string SchemaNameKey = "SchemaName"; private const string TableNameKey = "TableName"; private const string EnabledEnvVarName = "SQLCACHETESTS_ENABLED"; private readonly string _tableName; private readonly string _schemaName; private readonly string _connectionString; public SqlServerCacheWithDatabaseTest() { // TODO: Figure how to use config.json which requires resolving IApplicationEnvironment which currently // fails. var memoryConfigurationData = new Dictionary<string, string> { // When creating a test database, these values must be used in the parameters to 'dotnet sql-cache create'. // If you have to use other parameters for some reason, make sure to update this! { ConnectionStringKey, @"Server=(localdb)\MSSQLLocalDB;Database=CacheTestDb;Trusted_Connection=True;" }, { SchemaNameKey, "dbo" }, { TableNameKey, "CacheTest" }, }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder .AddInMemoryCollection(memoryConfigurationData) .AddEnvironmentVariables(prefix: "SQLCACHETESTS_"); var configuration = configurationBuilder.Build(); _tableName = configuration[TableNameKey]; _schemaName = configuration[SchemaNameKey]; _connectionString = configuration[ConnectionStringKey]; } [ConditionalFact] [EnvironmentVariableSkipCondition(EnabledEnvVarName, "1")] public async Task ReturnsNullValue_ForNonExistingCacheItem() { // Arrange var cache = GetSqlServerCache(); // Act var value = await cache.GetAsync("NonExisting"); // Assert Assert.Null(value); } [ConditionalFact] [EnvironmentVariableSkipCondition(EnabledEnvVarName, "1")] public async Task SetWithAbsoluteExpirationSetInThePast_Throws() { // Arrange var testClock = new TestClock(); var key = Guid.NewGuid().ToString(); var expectedValue = Encoding.UTF8.GetBytes("Hello, World!"); var cache = GetSqlServerCache(GetCacheOptions(testClock)); // Act & Assert var exception = await Assert.ThrowsAsync<InvalidOperationException>(() => { return cache.SetAsync( key, expectedValue, new DistributedCacheEntryOptions().SetAbsoluteExpiration(testClock.UtcNow.AddHours(-1))); }); Assert.Equal("The absolute expiration value must be in the future.", exception.Message); } [ConditionalFact] [EnvironmentVariableSkipCondition(EnabledEnvVarName, "1")] public async Task SetCacheItem_SucceedsFor_KeyEqualToMaximumSize() { // Arrange // Create a key with the maximum allowed key length. Here a key of length 898 bytes is created. var key = new string('a', SqlParameterCollectionExtensions.CacheItemIdColumnWidth); var testClock = new TestClock(); var expectedValue = Encoding.UTF8.GetBytes("Hello, World!"); var cache = GetSqlServerCache(GetCacheOptions(testClock)); // Act await cache.SetAsync( key, expectedValue, new DistributedCacheEntryOptions().SetSlidingExpiration(TimeSpan.FromMinutes(30))); // Assert var cacheItem = await GetCacheItemFromDatabaseAsync(key); Assert.Equal(expectedValue, cacheItem.Value); // Act await cache.RemoveAsync(key); // Assert var cacheItemInfo = await GetCacheItemFromDatabaseAsync(key); Assert.Null(cacheItemInfo); } [ConditionalFact] [EnvironmentVariableSkipCondition(EnabledEnvVarName, "1")] public async Task SetCacheItem_SucceedsFor_NullAbsoluteAndSlidingExpirationTimes() { // Arrange var key = Guid.NewGuid().ToString(); var testClock = new TestClock(); var expectedValue = Encoding.UTF8.GetBytes("Hello, World!"); var cacheOptions = GetCacheOptions(testClock); var cache = GetSqlServerCache(cacheOptions); var expectedExpirationTime = testClock.UtcNow.Add(cacheOptions.DefaultSlidingExpiration); // Act await cache.SetAsync(key, expectedValue, new DistributedCacheEntryOptions() { AbsoluteExpiration = null, AbsoluteExpirationRelativeToNow = null, SlidingExpiration = null }); // Assert await AssertGetCacheItemFromDatabaseAsync( cache, key, expectedValue, cacheOptions.DefaultSlidingExpiration, absoluteExpiration: null, expectedExpirationTime: expectedExpirationTime); var cacheItem = await GetCacheItemFromDatabaseAsync(key); Assert.Equal(expectedValue, cacheItem.Value); // Act await cache.RemoveAsync(key); // Assert var cacheItemInfo = await GetCacheItemFromDatabaseAsync(key); Assert.Null(cacheItemInfo); } [ConditionalFact] [EnvironmentVariableSkipCondition(EnabledEnvVarName, "1")] public async Task UpdatedDefaultSlidingExpiration_SetCacheItem_SucceedsFor_NullAbsoluteAndSlidingExpirationTimes() { // Arrange var key = Guid.NewGuid().ToString(); var testClock = new TestClock(); var expectedValue = Encoding.UTF8.GetBytes("Hello, World!"); var cacheOptions = GetCacheOptions(testClock); cacheOptions.DefaultSlidingExpiration = cacheOptions.DefaultSlidingExpiration.Add(TimeSpan.FromMinutes(10)); var cache = GetSqlServerCache(cacheOptions); var expectedExpirationTime = testClock.UtcNow.Add(cacheOptions.DefaultSlidingExpiration); // Act await cache.SetAsync(key, expectedValue, new DistributedCacheEntryOptions() { AbsoluteExpiration = null, AbsoluteExpirationRelativeToNow = null, SlidingExpiration = null }); // Assert await AssertGetCacheItemFromDatabaseAsync( cache, key, expectedValue, cacheOptions.DefaultSlidingExpiration, absoluteExpiration: null, expectedExpirationTime: expectedExpirationTime); var cacheItem = await GetCacheItemFromDatabaseAsync(key); Assert.Equal(expectedValue, cacheItem.Value); // Act await cache.RemoveAsync(key); // Assert var cacheItemInfo = await GetCacheItemFromDatabaseAsync(key); Assert.Null(cacheItemInfo); } [ConditionalFact] [EnvironmentVariableSkipCondition(EnabledEnvVarName, "1")] public async Task SetCacheItem_FailsFor_KeyGreaterThanMaximumSize() { // Arrange // Create a key which is greater than the maximum length. var key = new string('b', SqlParameterCollectionExtensions.CacheItemIdColumnWidth + 1); var testClock = new TestClock(); var expectedValue = Encoding.UTF8.GetBytes("Hello, World!"); var cache = GetSqlServerCache(GetCacheOptions(testClock)); // Act await cache.SetAsync( key, expectedValue, new DistributedCacheEntryOptions().SetSlidingExpiration(TimeSpan.FromMinutes(30))); // Assert var cacheItem = await GetCacheItemFromDatabaseAsync(key); Assert.Null(cacheItem); } // Arrange [ConditionalTheory] [InlineData(10, 11)] [InlineData(10, 30)] [EnvironmentVariableSkipCondition(EnabledEnvVarName, "1")] public async Task SetWithSlidingExpiration_ReturnsNullValue_ForExpiredCacheItem( int slidingExpirationWindow, int accessItemAt) { // Arrange var testClock = new TestClock(); var key = Guid.NewGuid().ToString(); var cache = GetSqlServerCache(GetCacheOptions(testClock)); await cache.SetAsync( key, Encoding.UTF8.GetBytes("Hello, World!"), new DistributedCacheEntryOptions().SetSlidingExpiration(TimeSpan.FromSeconds(slidingExpirationWindow))); // set the clock's UtcNow far in future testClock.Add(TimeSpan.FromHours(accessItemAt)); // Act var value = await cache.GetAsync(key); // Assert Assert.Null(value); } [ConditionalTheory] [InlineData(5, 15)] [InlineData(10, 20)] [EnvironmentVariableSkipCondition(EnabledEnvVarName, "1")] public async Task SetWithSlidingExpiration_ExtendsExpirationTime(int accessItemAt, int expected) { // Arrange var testClock = new TestClock(); var slidingExpirationWindow = TimeSpan.FromSeconds(10); var key = Guid.NewGuid().ToString(); var cache = GetSqlServerCache(GetCacheOptions(testClock)); var expectedValue = Encoding.UTF8.GetBytes("Hello, World!"); var expectedExpirationTime = testClock.UtcNow.AddSeconds(expected); await cache.SetAsync( key, expectedValue, new DistributedCacheEntryOptions().SetSlidingExpiration(slidingExpirationWindow)); testClock.Add(TimeSpan.FromSeconds(accessItemAt)); // Act await AssertGetCacheItemFromDatabaseAsync( cache, key, expectedValue, slidingExpirationWindow, absoluteExpiration: null, expectedExpirationTime: expectedExpirationTime); } [ConditionalTheory] [InlineData(8)] [InlineData(50)] [EnvironmentVariableSkipCondition(EnabledEnvVarName, "1")] public async Task SetWithSlidingExpirationAndAbsoluteExpiration_ReturnsNullValue_ForExpiredCacheItem( int accessItemAt) { // Arrange var testClock = new TestClock(); var utcNow = testClock.UtcNow; var slidingExpiration = TimeSpan.FromSeconds(5); var absoluteExpiration = utcNow.Add(TimeSpan.FromSeconds(20)); var key = Guid.NewGuid().ToString(); var cache = GetSqlServerCache(GetCacheOptions(testClock)); var expectedValue = Encoding.UTF8.GetBytes("Hello, World!"); await cache.SetAsync( key, expectedValue, // Set both sliding and absolute expiration new DistributedCacheEntryOptions() .SetSlidingExpiration(slidingExpiration) .SetAbsoluteExpiration(absoluteExpiration)); // Act utcNow = testClock.Add(TimeSpan.FromSeconds(accessItemAt)).UtcNow; var value = await cache.GetAsync(key); // Assert Assert.Null(value); } [ConditionalFact] [EnvironmentVariableSkipCondition(EnabledEnvVarName, "1")] public async Task SetWithAbsoluteExpirationRelativeToNow_ReturnsNullValue_ForExpiredCacheItem() { // Arrange var testClock = new TestClock(); var key = Guid.NewGuid().ToString(); var cache = GetSqlServerCache(GetCacheOptions(testClock)); await cache.SetAsync( key, Encoding.UTF8.GetBytes("Hello, World!"), new DistributedCacheEntryOptions().SetAbsoluteExpiration(relative: TimeSpan.FromSeconds(10))); // set the clock's UtcNow far in future testClock.Add(TimeSpan.FromHours(10)); // Act var value = await cache.GetAsync(key); // Assert Assert.Null(value); } [ConditionalFact] [EnvironmentVariableSkipCondition(EnabledEnvVarName, "1")] public async Task SetWithAbsoluteExpiration_ReturnsNullValue_ForExpiredCacheItem() { // Arrange var testClock = new TestClock(); var key = Guid.NewGuid().ToString(); var cache = GetSqlServerCache(GetCacheOptions(testClock)); await cache.SetAsync( key, Encoding.UTF8.GetBytes("Hello, World!"), new DistributedCacheEntryOptions() .SetAbsoluteExpiration(absolute: testClock.UtcNow.Add(TimeSpan.FromSeconds(30)))); // set the clock's UtcNow far in future testClock.Add(TimeSpan.FromHours(10)); // Act var value = await cache.GetAsync(key); // Assert Assert.Null(value); } [ConditionalFact] [EnvironmentVariableSkipCondition(EnabledEnvVarName, "1")] public async Task DoesNotThrowException_WhenOnlyAbsoluteExpirationSupplied_AbsoluteExpirationRelativeToNow() { // Arrange var testClock = new TestClock(); var absoluteExpirationRelativeToUtcNow = TimeSpan.FromSeconds(10); var key = Guid.NewGuid().ToString(); var cache = GetSqlServerCache(GetCacheOptions(testClock)); var expectedValue = Encoding.UTF8.GetBytes("Hello, World!"); var expectedAbsoluteExpiration = testClock.UtcNow.Add(absoluteExpirationRelativeToUtcNow); // Act await cache.SetAsync( key, expectedValue, new DistributedCacheEntryOptions() .SetAbsoluteExpiration(relative: absoluteExpirationRelativeToUtcNow)); // Assert await AssertGetCacheItemFromDatabaseAsync( cache, key, expectedValue, slidingExpiration: null, absoluteExpiration: expectedAbsoluteExpiration, expectedExpirationTime: expectedAbsoluteExpiration); } [ConditionalFact] [EnvironmentVariableSkipCondition(EnabledEnvVarName, "1")] public async Task DoesNotThrowException_WhenOnlyAbsoluteExpirationSupplied_AbsoluteExpiration() { // Arrange var testClock = new TestClock(); var expectedAbsoluteExpiration = new DateTimeOffset(2025, 1, 1, 1, 0, 0, TimeSpan.Zero); var key = Guid.NewGuid().ToString(); var cache = GetSqlServerCache(); var expectedValue = Encoding.UTF8.GetBytes("Hello, World!"); // Act await cache.SetAsync( key, expectedValue, new DistributedCacheEntryOptions() .SetAbsoluteExpiration(absolute: expectedAbsoluteExpiration)); // Assert await AssertGetCacheItemFromDatabaseAsync( cache, key, expectedValue, slidingExpiration: null, absoluteExpiration: expectedAbsoluteExpiration, expectedExpirationTime: expectedAbsoluteExpiration); } [ConditionalFact] [EnvironmentVariableSkipCondition(EnabledEnvVarName, "1")] public async Task SetCacheItem_UpdatesAbsoluteExpirationTime() { // Arrange var testClock = new TestClock(); var key = Guid.NewGuid().ToString(); var cache = GetSqlServerCache(GetCacheOptions(testClock)); var expectedValue = Encoding.UTF8.GetBytes("Hello, World!"); var absoluteExpiration = testClock.UtcNow.Add(TimeSpan.FromSeconds(10)); // Act & Assert // Creates a new item await cache.SetAsync( key, expectedValue, new DistributedCacheEntryOptions().SetAbsoluteExpiration(absoluteExpiration)); await AssertGetCacheItemFromDatabaseAsync( cache, key, expectedValue, slidingExpiration: null, absoluteExpiration: absoluteExpiration, expectedExpirationTime: absoluteExpiration); // Updates an existing item with new absolute expiration time absoluteExpiration = testClock.UtcNow.Add(TimeSpan.FromMinutes(30)); await cache.SetAsync( key, expectedValue, new DistributedCacheEntryOptions().SetAbsoluteExpiration(absoluteExpiration)); await AssertGetCacheItemFromDatabaseAsync( cache, key, expectedValue, slidingExpiration: null, absoluteExpiration: absoluteExpiration, expectedExpirationTime: absoluteExpiration); } [ConditionalFact] [EnvironmentVariableSkipCondition(EnabledEnvVarName, "1")] public async Task SetCacheItem_WithValueLargerThan_DefaultColumnWidth() { // Arrange var testClock = new TestClock(); var key = Guid.NewGuid().ToString(); var cache = GetSqlServerCache(GetCacheOptions(testClock)); var expectedValue = new byte[SqlParameterCollectionExtensions.DefaultValueColumnWidth + 100]; var absoluteExpiration = testClock.UtcNow.Add(TimeSpan.FromSeconds(10)); // Act // Creates a new item await cache.SetAsync( key, expectedValue, new DistributedCacheEntryOptions().SetAbsoluteExpiration(absoluteExpiration)); // Assert await AssertGetCacheItemFromDatabaseAsync( cache, key, expectedValue, slidingExpiration: null, absoluteExpiration: absoluteExpiration, expectedExpirationTime: absoluteExpiration); } [ConditionalFact] [EnvironmentVariableSkipCondition(EnabledEnvVarName, "1")] public async Task ExtendsExpirationTime_ForSlidingExpiration() { // Arrange var testClock = new TestClock(); var slidingExpiration = TimeSpan.FromSeconds(10); var key = Guid.NewGuid().ToString(); var cache = GetSqlServerCache(GetCacheOptions(testClock)); var expectedValue = Encoding.UTF8.GetBytes("Hello, World!"); // The operations Set and Refresh here extend the sliding expiration 2 times. var expectedExpiresAtTime = testClock.UtcNow.AddSeconds(15); await cache.SetAsync( key, expectedValue, new DistributedCacheEntryOptions().SetSlidingExpiration(slidingExpiration)); // Act testClock.Add(TimeSpan.FromSeconds(5)); await cache.RefreshAsync(key); // Assert // verify if the expiration time in database is set as expected var cacheItemInfo = await GetCacheItemFromDatabaseAsync(key); Assert.NotNull(cacheItemInfo); Assert.Equal(slidingExpiration, cacheItemInfo.SlidingExpirationInSeconds); Assert.Null(cacheItemInfo.AbsoluteExpiration); Assert.Equal(expectedExpiresAtTime, cacheItemInfo.ExpiresAtTime); } [ConditionalFact] [EnvironmentVariableSkipCondition(EnabledEnvVarName, "1")] public async Task GetItem_SlidingExpirationDoesNot_ExceedAbsoluteExpirationIfSet() { // Arrange var testClock = new TestClock(); var utcNow = testClock.UtcNow; var slidingExpiration = TimeSpan.FromSeconds(5); var absoluteExpiration = utcNow.Add(TimeSpan.FromSeconds(20)); var key = Guid.NewGuid().ToString(); var cache = GetSqlServerCache(GetCacheOptions(testClock)); var expectedValue = Encoding.UTF8.GetBytes("Hello, World!"); await cache.SetAsync( key, expectedValue, // Set both sliding and absolute expiration new DistributedCacheEntryOptions() .SetSlidingExpiration(slidingExpiration) .SetAbsoluteExpiration(absoluteExpiration)); // Act && Assert var cacheItemInfo = await GetCacheItemFromDatabaseAsync(key); Assert.NotNull(cacheItemInfo); Assert.Equal(utcNow.AddSeconds(5), cacheItemInfo.ExpiresAtTime); // Accessing item at time... utcNow = testClock.Add(TimeSpan.FromSeconds(5)).UtcNow; await AssertGetCacheItemFromDatabaseAsync( cache, key, expectedValue, slidingExpiration, absoluteExpiration, expectedExpirationTime: utcNow.AddSeconds(5)); // Accessing item at time... utcNow = testClock.Add(TimeSpan.FromSeconds(5)).UtcNow; await AssertGetCacheItemFromDatabaseAsync( cache, key, expectedValue, slidingExpiration, absoluteExpiration, expectedExpirationTime: utcNow.AddSeconds(5)); // Accessing item at time... utcNow = testClock.Add(TimeSpan.FromSeconds(5)).UtcNow; // The expiration extension must not exceed the absolute expiration await AssertGetCacheItemFromDatabaseAsync( cache, key, expectedValue, slidingExpiration, absoluteExpiration, expectedExpirationTime: absoluteExpiration); } [ConditionalFact] [EnvironmentVariableSkipCondition(EnabledEnvVarName, "1")] public async Task DoestNotExtendsExpirationTime_ForAbsoluteExpiration() { // Arrange var testClock = new TestClock(); var absoluteExpirationRelativeToNow = TimeSpan.FromSeconds(30); var expectedExpiresAtTime = testClock.UtcNow.Add(absoluteExpirationRelativeToNow); var key = Guid.NewGuid().ToString(); var cache = GetSqlServerCache(GetCacheOptions(testClock)); var expectedValue = Encoding.UTF8.GetBytes("Hello, World!"); await cache.SetAsync( key, expectedValue, new DistributedCacheEntryOptions().SetAbsoluteExpiration(absoluteExpirationRelativeToNow)); testClock.Add(TimeSpan.FromSeconds(25)); // Act var value = await cache.GetAsync(key); // Assert Assert.NotNull(value); Assert.Equal(expectedValue, value); // verify if the expiration time in database is set as expected var cacheItemInfo = await GetCacheItemFromDatabaseAsync(key); Assert.NotNull(cacheItemInfo); Assert.Equal(expectedExpiresAtTime, cacheItemInfo.ExpiresAtTime); } [ConditionalFact] [EnvironmentVariableSkipCondition(EnabledEnvVarName, "1")] public async Task RefreshItem_ExtendsExpirationTime_ForSlidingExpiration() { // Arrange var testClock = new TestClock(); var slidingExpiration = TimeSpan.FromSeconds(10); var key = Guid.NewGuid().ToString(); var cache = GetSqlServerCache(GetCacheOptions(testClock)); var expectedValue = Encoding.UTF8.GetBytes("Hello, World!"); // The operations Set and Refresh here extend the sliding expiration 2 times. var expectedExpiresAtTime = testClock.UtcNow.AddSeconds(15); await cache.SetAsync( key, expectedValue, new DistributedCacheEntryOptions().SetSlidingExpiration(slidingExpiration)); // Act testClock.Add(TimeSpan.FromSeconds(5)); await cache.RefreshAsync(key); // Assert // verify if the expiration time in database is set as expected var cacheItemInfo = await GetCacheItemFromDatabaseAsync(key); Assert.NotNull(cacheItemInfo); Assert.Equal(slidingExpiration, cacheItemInfo.SlidingExpirationInSeconds); Assert.Null(cacheItemInfo.AbsoluteExpiration); Assert.Equal(expectedExpiresAtTime, cacheItemInfo.ExpiresAtTime); } [ConditionalFact] [EnvironmentVariableSkipCondition(EnabledEnvVarName, "1")] public async Task GetCacheItem_IsCaseSensitive() { // Arrange var key = Guid.NewGuid().ToString().ToLower(CultureInfo.InvariantCulture); // lower case var cache = GetSqlServerCache(); await cache.SetAsync( key, Encoding.UTF8.GetBytes("Hello, World!"), new DistributedCacheEntryOptions().SetAbsoluteExpiration(relative: TimeSpan.FromHours(1))); // Act var value = await cache.GetAsync(key.ToUpper(CultureInfo.InvariantCulture)); // key made upper case // Assert Assert.Null(value); } [ConditionalFact] [EnvironmentVariableSkipCondition(EnabledEnvVarName, "1")] public async Task GetCacheItem_DoesNotTrimTrailingSpaces() { // Arrange var key = string.Format(CultureInfo.InvariantCulture, " {0} ", Guid.NewGuid()); // with trailing spaces var cache = GetSqlServerCache(); var expectedValue = Encoding.UTF8.GetBytes("Hello, World!"); await cache.SetAsync( key, expectedValue, new DistributedCacheEntryOptions().SetAbsoluteExpiration(relative: TimeSpan.FromHours(1))); // Act var value = await cache.GetAsync(key); // Assert Assert.NotNull(value); Assert.Equal(expectedValue, value); } [ConditionalFact] [EnvironmentVariableSkipCondition(EnabledEnvVarName, "1")] public async Task DeletesCacheItem_OnExplicitlyCalled() { // Arrange var key = Guid.NewGuid().ToString(); var cache = GetSqlServerCache(); await cache.SetAsync( key, Encoding.UTF8.GetBytes("Hello, World!"), new DistributedCacheEntryOptions().SetSlidingExpiration(TimeSpan.FromSeconds(10))); // Act await cache.RemoveAsync(key); // Assert var cacheItemInfo = await GetCacheItemFromDatabaseAsync(key); Assert.Null(cacheItemInfo); } private IDistributedCache GetSqlServerCache(SqlServerCacheOptions options = null) { if (options == null) { options = GetCacheOptions(); } return new SqlServerCache(options); } private SqlServerCacheOptions GetCacheOptions(ISystemClock testClock = null) { return new SqlServerCacheOptions() { ConnectionString = _connectionString, SchemaName = _schemaName, TableName = _tableName, SystemClock = testClock ?? new TestClock(), ExpiredItemsDeletionInterval = TimeSpan.FromHours(2) }; } private async Task AssertGetCacheItemFromDatabaseAsync( IDistributedCache cache, string key, byte[] expectedValue, TimeSpan? slidingExpiration, DateTimeOffset? absoluteExpiration, DateTimeOffset expectedExpirationTime) { var value = await cache.GetAsync(key); Assert.NotNull(value); Assert.Equal(expectedValue, value); var cacheItemInfo = await GetCacheItemFromDatabaseAsync(key); Assert.NotNull(cacheItemInfo); Assert.Equal(slidingExpiration, cacheItemInfo.SlidingExpirationInSeconds); Assert.Equal(absoluteExpiration, cacheItemInfo.AbsoluteExpiration); Assert.Equal(expectedExpirationTime, cacheItemInfo.ExpiresAtTime); } private async Task<CacheItemInfo> GetCacheItemFromDatabaseAsync(string key) { using (var connection = new SqlConnection(_connectionString)) { var command = new SqlCommand( $"SELECT Id, Value, ExpiresAtTime, SlidingExpirationInSeconds, AbsoluteExpiration " + $"FROM {_tableName} WHERE Id = @Id", connection); command.Parameters.AddWithValue("Id", key); await connection.OpenAsync(); var reader = await command.ExecuteReaderAsync(CommandBehavior.SingleRow); // NOTE: The following code is made to run on Mono as well because of which // we cannot use GetFieldValueAsync etc. if (await reader.ReadAsync()) { var cacheItemInfo = new CacheItemInfo { Id = key, Value = (byte[])reader[1], ExpiresAtTime = DateTimeOffset.Parse(reader[2].ToString(), CultureInfo.InvariantCulture) }; if (!await reader.IsDBNullAsync(3)) { cacheItemInfo.SlidingExpirationInSeconds = TimeSpan.FromSeconds(reader.GetInt64(3)); } if (!await reader.IsDBNullAsync(4)) { cacheItemInfo.AbsoluteExpiration = DateTimeOffset.Parse(reader[4].ToString(), CultureInfo.InvariantCulture); } return cacheItemInfo; } else { return null; } } } }