From d086284a13f34cf35090495ea8aa994bd2be5c48 Mon Sep 17 00:00:00 2001 From: Jinhuafei Date: Tue, 21 Aug 2018 17:01:09 -0700 Subject: [PATCH] Adding Azure Table storage and ComosDB table provider (#5) * v1 * mend * fix sqlprovider issues * update build script * update build for signing * update build * update CosmosDBTable nupkg version * add iconUrl in nupkg * fix deadlock issue and format code * update module version * update package ref for table provider --- .gitignore | 1 + Microsoft.AspNet.OutputCache.sln | 14 ++ MicrosoftAspNetOutputCache.msbuild | 4 +- .../CacheEntity.cs | 81 +++++++ .../CosmosDBTableAsyncOutputCacheProvider.cs | 67 ++++++ .../CosmosDBTableOutputCacheRepository.cs | 155 ++++++++++++++ .../ITableOutputCacheRepository.cs | 25 +++ ...smosDBTableAsyncOutputCacheProvider.csproj | 91 ++++++++ .../Properties/AssemblyInfo.cs | 26 +++ .../Resource/SR.Designer.cs | 108 ++++++++++ .../Resource/SR.resx | 135 ++++++++++++ .../TableOperationHelper.cs | 26 +++ ....OutputCache.OutputCacheModuleAsync.csproj | 2 +- ...utCache.SQLAsyncOutputCacheProvider.csproj | 2 +- .../SqlOutputCacheRepository.cs | 197 +++++++++++++----- ...smosDBTableAsyncOutputCacheProvider.nuproj | 29 +++ ...smosDBTableAsyncOutputCacheProvider.nuspec | 23 ++ .../content/Net462/web.config.install.xdt | 30 +++ .../content/Net462/web.config.uninstall.xdt | 16 ++ ....OutputCache.OutputCacheModuleAsync.nuproj | 3 +- ....OutputCache.OutputCacheModuleAsync.nuspec | 1 + src/packages/Packages.csproj | 1 + ...utCache.SQLAsyncOutputCacheProvider.nuproj | 3 +- ...utCache.SQLAsyncOutputCacheProvider.nuspec | 1 + .../CacheEntityTest.cs | 57 +++++ ...smosDBTableAsyncOutputCacheProviderTest.cs | 126 +++++++++++ ...BTableAsyncOutputCacheProvider.Test.csproj | 84 ++++++++ .../Properties/AssemblyInfo.cs | 36 ++++ .../35MSSharedLib1024.snk | Bin 160 -> 0 bytes ...utCache.OutputCacheModuleAsync.Test.csproj | 9 +- .../35MSSharedLib1024.snk | Bin 160 -> 0 bytes ...he.SQLAsyncOutputCacheProvider.Test.csproj | 9 +- .../SQLAsyncOutputCacheProviderTest.cs | 6 +- ...eAsyncOutputCacheProvider.settings.targets | 25 +++ ...rosoftAspNetOutputCache.Extensions.targets | 5 +- ...icrosoftAspNetOutputCache.settings.targets | 44 ++-- tools/MicrosoftAspNetOutputCache.targets | 20 +- tools/NuGetProj.targets | 3 +- tools/OutputCacheModuleAsync.settings.targets | 18 ++ ...LAsyncOutputCacheProvider.settings.targets | 23 ++ tools/version.targets | 1 + 41 files changed, 1397 insertions(+), 110 deletions(-) create mode 100644 src/CosmosDBTableAsyncOutputCacheProvider/CacheEntity.cs create mode 100644 src/CosmosDBTableAsyncOutputCacheProvider/CosmosDBTableAsyncOutputCacheProvider.cs create mode 100644 src/CosmosDBTableAsyncOutputCacheProvider/CosmosDBTableOutputCacheRepository.cs create mode 100644 src/CosmosDBTableAsyncOutputCacheProvider/ITableOutputCacheRepository.cs create mode 100644 src/CosmosDBTableAsyncOutputCacheProvider/Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.csproj create mode 100644 src/CosmosDBTableAsyncOutputCacheProvider/Properties/AssemblyInfo.cs create mode 100644 src/CosmosDBTableAsyncOutputCacheProvider/Resource/SR.Designer.cs create mode 100644 src/CosmosDBTableAsyncOutputCacheProvider/Resource/SR.resx create mode 100644 src/CosmosDBTableAsyncOutputCacheProvider/TableOperationHelper.cs create mode 100644 src/packages/CosmosDBTableAsyncOutputCacheProvider.nupkg/Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.nuproj create mode 100644 src/packages/CosmosDBTableAsyncOutputCacheProvider.nupkg/Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.nuspec create mode 100644 src/packages/CosmosDBTableAsyncOutputCacheProvider.nupkg/content/Net462/web.config.install.xdt create mode 100644 src/packages/CosmosDBTableAsyncOutputCacheProvider.nupkg/content/Net462/web.config.uninstall.xdt create mode 100644 test/Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.Test/CacheEntityTest.cs create mode 100644 test/Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.Test/CosmosDBTableAsyncOutputCacheProviderTest.cs create mode 100644 test/Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.Test/Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.Test.csproj create mode 100644 test/Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.Test/Properties/AssemblyInfo.cs delete mode 100644 test/Microsoft.AspNet.OutputCache.OutputCacheModuleAsync.Test/35MSSharedLib1024.snk delete mode 100644 test/Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.Test/35MSSharedLib1024.snk create mode 100644 tools/CosmosDBTableAsyncOutputCacheProvider.settings.targets create mode 100644 tools/OutputCacheModuleAsync.settings.targets create mode 100644 tools/SQLAsyncOutputCacheProvider.settings.targets diff --git a/.gitignore b/.gitignore index 67e8f26..baf229d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ .vs/ msbuild.* packages/ +*.log diff --git a/Microsoft.AspNet.OutputCache.sln b/Microsoft.AspNet.OutputCache.sln index 93eadf9..ff1f68b 100644 --- a/Microsoft.AspNet.OutputCache.sln +++ b/Microsoft.AspNet.OutputCache.sln @@ -15,6 +15,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.OutputCach EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.Test", "test\Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.Test\Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.Test.csproj", "{B2C127BD-077B-4B9A-8163-F77DF76CE6A8}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider", "src\CosmosDBTableAsyncOutputCacheProvider\Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.csproj", "{9FDBB781-C469-4D9E-B687-4087CD422B13}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.Test", "test\Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.Test\Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.Test.csproj", "{107EF28E-05AA-485E-A5C1-9A20A6DFE877}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -37,6 +41,14 @@ Global {B2C127BD-077B-4B9A-8163-F77DF76CE6A8}.Debug|Any CPU.Build.0 = Debug|Any CPU {B2C127BD-077B-4B9A-8163-F77DF76CE6A8}.Release|Any CPU.ActiveCfg = Release|Any CPU {B2C127BD-077B-4B9A-8163-F77DF76CE6A8}.Release|Any CPU.Build.0 = Release|Any CPU + {9FDBB781-C469-4D9E-B687-4087CD422B13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9FDBB781-C469-4D9E-B687-4087CD422B13}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9FDBB781-C469-4D9E-B687-4087CD422B13}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9FDBB781-C469-4D9E-B687-4087CD422B13}.Release|Any CPU.Build.0 = Release|Any CPU + {107EF28E-05AA-485E-A5C1-9A20A6DFE877}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {107EF28E-05AA-485E-A5C1-9A20A6DFE877}.Debug|Any CPU.Build.0 = Debug|Any CPU + {107EF28E-05AA-485E-A5C1-9A20A6DFE877}.Release|Any CPU.ActiveCfg = Release|Any CPU + {107EF28E-05AA-485E-A5C1-9A20A6DFE877}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -46,6 +58,8 @@ Global {062FD141-4E51-4943-8C69-385DDE3D2792} = {D256C480-BB19-4239-AD53-DF634A794F41} {89636B89-D392-47CA-9D81-BEB4C5252D75} = {BCF9496E-D71E-4F4A-814B-B37830A87FF3} {B2C127BD-077B-4B9A-8163-F77DF76CE6A8} = {BCF9496E-D71E-4F4A-814B-B37830A87FF3} + {9FDBB781-C469-4D9E-B687-4087CD422B13} = {D256C480-BB19-4239-AD53-DF634A794F41} + {107EF28E-05AA-485E-A5C1-9A20A6DFE877} = {BCF9496E-D71E-4F4A-814B-B37830A87FF3} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {51897B01-673A-47FD-87BC-8FD7C01950E6} diff --git a/MicrosoftAspNetOutputCache.msbuild b/MicrosoftAspNetOutputCache.msbuild index cbd9148..0bd90da 100644 --- a/MicrosoftAspNetOutputCache.msbuild +++ b/MicrosoftAspNetOutputCache.msbuild @@ -1,13 +1,13 @@ - - + + diff --git a/src/CosmosDBTableAsyncOutputCacheProvider/CacheEntity.cs b/src/CosmosDBTableAsyncOutputCacheProvider/CacheEntity.cs new file mode 100644 index 0000000..9b19911 --- /dev/null +++ b/src/CosmosDBTableAsyncOutputCacheProvider/CacheEntity.cs @@ -0,0 +1,81 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See the License.txt file in the project root for full license information. + +namespace Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider { + using Microsoft.Azure.CosmosDB.Table; + using Microsoft.Azure.Storage; + using System; + using System.Collections.Generic; + using System.IO; + using System.Runtime.Serialization.Formatters.Binary; + using System.Text; + + class CacheEntity : TableEntity { + private static readonly char[] InvalidCharsInResource = { '/', '\\', '?', '#' }; + private const char ReplacementOfInvalidChars = '_'; + + // This is required by TableQuery + public CacheEntity() { } + + public CacheEntity(string cacheKey, object cacheItem, DateTime utcExpiry) { + RowKey = SanitizeKey(cacheKey); + PartitionKey = GeneratePartitionKey(cacheKey); + CacheItem = cacheItem; + UtcExpiry = utcExpiry; + } + + public object CacheItem { get; set; } + + public DateTime UtcExpiry { get; set; } + + public override void ReadEntity(IDictionary properties, OperationContext operationContext) { + base.ReadEntity(properties, operationContext); + CacheItem = Deserialize(properties[nameof(CacheItem)].BinaryValue); + } + + public override IDictionary WriteEntity(OperationContext operationContext) { + var result = base.WriteEntity(operationContext); + var cacheItemProperty = new EntityProperty(Serialize(CacheItem)); + result.Add(nameof(CacheItem), cacheItemProperty); + return result; + } + + public static string GeneratePartitionKey(string cacheKey) { + return (cacheKey.Length % 10).ToString(); + } + + public static string SanitizeKey(string cacheKey) { + // some chars are not allowed in rowkey + // https://docs.microsoft.com/en-us/rest/api/storageservices/Understanding-the-Table-Service-Data-Model + var sbKey = new StringBuilder(cacheKey); + + foreach (var c in InvalidCharsInResource) { + sbKey.Replace(c, ReplacementOfInvalidChars); + } + return sbKey.ToString(); + } + + private static byte[] Serialize(object data) { + if (data == null) { + data = new object(); + } + + using (var memoryStream = new MemoryStream()) { + var binaryFormatter = new BinaryFormatter(); + binaryFormatter.Serialize(memoryStream, data); + return memoryStream.ToArray(); + } + } + + private static object Deserialize(byte[] data) { + if (data == null) { + return null; + } + + using (var memoryStream = new MemoryStream(data, 0, data.Length)) { + var binaryFormatter = new BinaryFormatter(); + return binaryFormatter.Deserialize(memoryStream); + } + } + } +} diff --git a/src/CosmosDBTableAsyncOutputCacheProvider/CosmosDBTableAsyncOutputCacheProvider.cs b/src/CosmosDBTableAsyncOutputCacheProvider/CosmosDBTableAsyncOutputCacheProvider.cs new file mode 100644 index 0000000..eca9e1b --- /dev/null +++ b/src/CosmosDBTableAsyncOutputCacheProvider/CosmosDBTableAsyncOutputCacheProvider.cs @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See the License.txt file in the project root for full license information. + +namespace Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider { + using System; + using System.Collections.Specialized; + using System.Threading.Tasks; + using System.Web.Caching; + using System.Web.Configuration; + + /// + /// Async CosmosDB table OutputCache provider + /// + public class CosmosDBTableAsyncOutputCacheProvider : OutputCacheProviderAsync { + private ITableOutputCacheRepository _tableRepo; + + /// + public override void Initialize(string name, NameValueCollection config) { + Initialize(name, config, new CosmosDBTableOutputCacheRepository(config, WebConfigurationManager.AppSettings)); + } + + internal void Initialize(string name, NameValueCollection providerConfig, ITableOutputCacheRepository _repo) { + _tableRepo = _repo; + base.Initialize(name, providerConfig); + } + + /// + public override object Add(string key, object entry, DateTime utcExpiry) { + return _tableRepo.Add(key, entry, utcExpiry); + } + + /// + public override Task AddAsync(string key, object entry, DateTime utcExpiry) { + return _tableRepo.AddAsync(key, entry, utcExpiry); + } + + /// + public override object Get(string key) { + return _tableRepo.Get(key); + } + + /// + public override Task GetAsync(string key) { + return _tableRepo.GetAsync(key); + } + + /// + public override void Remove(string key) { + _tableRepo.Remove(key); + } + + /// + public override Task RemoveAsync(string key) { + return _tableRepo.RemoveAsync(key); + } + + /// + public override void Set(string key, object entry, DateTime utcExpiry) { + _tableRepo.Set(key, entry, utcExpiry); + } + + /// + public override Task SetAsync(string key, object entry, DateTime utcExpiry) { + return _tableRepo.SetAsync(key, entry, utcExpiry); + } + } +} diff --git a/src/CosmosDBTableAsyncOutputCacheProvider/CosmosDBTableOutputCacheRepository.cs b/src/CosmosDBTableAsyncOutputCacheProvider/CosmosDBTableOutputCacheRepository.cs new file mode 100644 index 0000000..44cb492 --- /dev/null +++ b/src/CosmosDBTableAsyncOutputCacheProvider/CosmosDBTableOutputCacheRepository.cs @@ -0,0 +1,155 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See the License.txt file in the project root for full license information. + +namespace Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider { + using System; + using System.Collections.Specialized; + using System.Configuration; + using System.Threading.Tasks; + using System.Web; + using Microsoft.Azure.CosmosDB.Table; + using Microsoft.Azure.Storage; + using Resource; + + class CosmosDBTableOutputCacheRepository : ITableOutputCacheRepository { + private const string TableNameKey = "tableName"; + private const string ConnectionStringKey = "connectionStringName"; + private const string FixedPartitionKey = "P"; + + private CloudTable _table; + private string _connectionString; + private string _tableName; + private object _lock = new object(); + + public CosmosDBTableOutputCacheRepository(NameValueCollection providerConfig, NameValueCollection appSettings) { + var connectionStringName = providerConfig[ConnectionStringKey]; + if (string.IsNullOrEmpty(connectionStringName)) { + throw new ConfigurationErrorsException(SR.Cant_find_connectionStringName); + } + + _connectionString = appSettings[connectionStringName]; + if (string.IsNullOrEmpty(_connectionString)) { + throw new ConfigurationErrorsException(string.Format(SR.Cant_find_connectionString, connectionStringName)); + } + + _tableName = providerConfig[TableNameKey]; + if (string.IsNullOrEmpty(_tableName)) { + throw new ConfigurationErrorsException(SR.TableName_cant_be_empty); + } + } + + public object Add(string key, object entry, DateTime utcExpiry) { + var retrieveOp = TableOperationHelper.Retrieve(key); + var retrieveResult = _table.Execute(retrieveOp); + var existingCacheEntry = retrieveResult.Result as CacheEntity; + + if (existingCacheEntry != null && existingCacheEntry.UtcExpiry > DateTime.UtcNow) { + return existingCacheEntry.CacheItem; + } else { + Set(key, entry, utcExpiry); + return entry; + } + } + + public async Task AddAsync(string key, object entry, DateTime utcExpiry) { + // If there is already a value in the cache for the specified key, the provider must return that value if not expired + // and must not store the data passed by using the Add method parameters. + var retrieveOp = TableOperationHelper.Retrieve(key); + var retrieveResult = await _table.ExecuteAsync(retrieveOp); + var existingCacheEntry = retrieveResult.Result as CacheEntity; + + if (existingCacheEntry != null && existingCacheEntry.UtcExpiry > DateTime.UtcNow) { + return existingCacheEntry.CacheItem; + } else { + await SetAsync(key, entry, utcExpiry); + return entry; + } + } + + public object Get(string key) { + var retrieveOp = TableOperationHelper.Retrieve(key); + var retrieveResult = _table.Execute(retrieveOp); + var existingCacheEntry = retrieveResult.Result as CacheEntity; + + if (existingCacheEntry != null && existingCacheEntry.UtcExpiry < DateTime.UtcNow) { + Remove(key); + return null; + } else { + return existingCacheEntry?.CacheItem; + } + } + + public async Task GetAsync(string key) { + // Outputcache module will always first call GetAsync + // so only calling EnsureTableInitializedAsync here is good enough + await EnsureTableInitializedAsync(); + + var retrieveOp = TableOperationHelper.Retrieve(key); + var retrieveResult = await _table.ExecuteAsync(retrieveOp); + var existingCacheEntry = retrieveResult.Result as CacheEntity; + + if (existingCacheEntry != null && existingCacheEntry.UtcExpiry < DateTime.UtcNow) { + await RemoveAsync(key); + return null; + } else { + return existingCacheEntry?.CacheItem; + } + } + + public void Remove(string key) { + var removeOp = TableOperationHelper.Delete(key); + _table.Execute(removeOp); + } + + public async Task RemoveAsync(string key) { + var removeOp = TableOperationHelper.Delete(key); + await _table.ExecuteAsync(removeOp); + } + + public void Set(string key, object entry, DateTime utcExpiry) { + var insertOp = TableOperationHelper.InsertOrReplace(key, entry, utcExpiry); + _table.Execute(insertOp); + } + + public async Task SetAsync(string key, object entry, DateTime utcExpiry) { + //Check if the key is already in database + //If there is already a value in the cache for the specified key, the Set method will update it. + //Otherwise it will insert the entry. + var insertOp = TableOperationHelper.InsertOrReplace(key, entry, utcExpiry); + await _table.ExecuteAsync(insertOp); + } + + private async Task EnsureTableInitializedAsync() { + if (_table != null) { + return; + } + + try { + lock (_lock) { + if (_table != null) { + return; + } + + var storageAccount = CreateStorageAccount(); + var tableClient = storageAccount.CreateCloudTableClient(); + _table = tableClient.GetTableReference(_tableName); + } + + // The sync version API causes deadlock when using CosmosDB table. + await _table.CreateIfNotExistsAsync(); + } catch (StorageException ex) { + throw new HttpException(SR.Fail_to_create_table, ex); + } + } + + private CloudStorageAccount CreateStorageAccount() { + try { + return CloudStorageAccount.Parse(_connectionString); + } catch (FormatException) { + throw new HttpException(SR.Invalid_storage_account_information); + } catch (ArgumentException) { + throw new HttpException(SR.Invalid_storage_account_information); + } + } + } +} diff --git a/src/CosmosDBTableAsyncOutputCacheProvider/ITableOutputCacheRepository.cs b/src/CosmosDBTableAsyncOutputCacheProvider/ITableOutputCacheRepository.cs new file mode 100644 index 0000000..f6d7bf3 --- /dev/null +++ b/src/CosmosDBTableAsyncOutputCacheProvider/ITableOutputCacheRepository.cs @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See the License.txt file in the project root for full license information. + +namespace Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider { + using System; + using System.Threading.Tasks; + + interface ITableOutputCacheRepository { + Task AddAsync(string key, object entry, DateTime utcExpiry); + + Task GetAsync(string key); + + Task RemoveAsync(string key); + + Task SetAsync(string key, object entry, DateTime utcExpiry); + + object Add(string key, object entry, DateTime utcExpiry); + + object Get(string key); + + void Remove(string key); + + void Set(string key, object entry, DateTime utcExpiry); + } +} diff --git a/src/CosmosDBTableAsyncOutputCacheProvider/Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.csproj b/src/CosmosDBTableAsyncOutputCacheProvider/Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.csproj new file mode 100644 index 0000000..1b3096e --- /dev/null +++ b/src/CosmosDBTableAsyncOutputCacheProvider/Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.csproj @@ -0,0 +1,91 @@ + + + + + + Debug + AnyCPU + {9FDBB781-C469-4D9E-B687-4087CD422B13} + Library + Properties + Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider + Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider + v4.6.2 + 512 + SAK + SAK + SAK + SAK + $(OutputPath)$(AssemblyName).xml + ..\..\ + + + + true + full + false + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + TRACE + prompt + 4 + + + true + + + $(RepositoryRoot)tools\35MSSharedLib1024.snk + + + true + + + + + + + + + + + + + + + + + {3b446e33-7b1c-4a32-aeb8-92dc6ce94f77} + Microsoft.AspNet.OutputCache.OutputCacheModuleAsync + + + + + + + + + + True + True + SR.resx + + + + + + 1.1.0 + + + + + ResXFileCodeGenerator + SR.Designer.cs + + + + \ No newline at end of file diff --git a/src/CosmosDBTableAsyncOutputCacheProvider/Properties/AssemblyInfo.cs b/src/CosmosDBTableAsyncOutputCacheProvider/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..cec2da6 --- /dev/null +++ b/src/CosmosDBTableAsyncOutputCacheProvider/Properties/AssemblyInfo.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider")] +[assembly: AssemblyDescription("Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.dll")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft Corporation")] +[assembly: AssemblyProduct("Microsoft AspNet OutputCache CosmosDBTableAsyncOutputCacheProvider")] +[assembly: AssemblyCopyright("\x00a9 Microsoft Corporation. All rights reserved.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("9fdbb781-c469-4d9e-b687-4087cd422b13")] + +[assembly: InternalsVisibleTo("Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] +[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] \ No newline at end of file diff --git a/src/CosmosDBTableAsyncOutputCacheProvider/Resource/SR.Designer.cs b/src/CosmosDBTableAsyncOutputCacheProvider/Resource/SR.Designer.cs new file mode 100644 index 0000000..a9c53a9 --- /dev/null +++ b/src/CosmosDBTableAsyncOutputCacheProvider/Resource/SR.Designer.cs @@ -0,0 +1,108 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.Resource { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class SR { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal SR() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.Resource.SR", typeof(SR).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Can't find the connection string by connection string name '{0}'.. + /// + internal static string Cant_find_connectionString { + get { + return ResourceManager.GetString("Cant_find_connectionString", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Connection string name can't be empty.. + /// + internal static string Cant_find_connectionStringName { + get { + return ResourceManager.GetString("Cant_find_connectionStringName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to create table.. + /// + internal static string Fail_to_create_table { + get { + return ResourceManager.GetString("Fail_to_create_table", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invalid storage account information provided. Please confirm the AccountName and AccountKey are valid in the web.config file - then restart the application.. + /// + internal static string Invalid_storage_account_information { + get { + return ResourceManager.GetString("Invalid_storage_account_information", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Table name can't be empty.. + /// + internal static string TableName_cant_be_empty { + get { + return ResourceManager.GetString("TableName_cant_be_empty", resourceCulture); + } + } + } +} diff --git a/src/CosmosDBTableAsyncOutputCacheProvider/Resource/SR.resx b/src/CosmosDBTableAsyncOutputCacheProvider/Resource/SR.resx new file mode 100644 index 0000000..a65ac10 --- /dev/null +++ b/src/CosmosDBTableAsyncOutputCacheProvider/Resource/SR.resx @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Can't find the connection string by connection string name '{0}'. + + + Connection string name can't be empty. + + + Failed to create table. + + + Invalid storage account information provided. Please confirm the AccountName and AccountKey are valid in the web.config file - then restart the application. + + + Table name can't be empty. + + \ No newline at end of file diff --git a/src/CosmosDBTableAsyncOutputCacheProvider/TableOperationHelper.cs b/src/CosmosDBTableAsyncOutputCacheProvider/TableOperationHelper.cs new file mode 100644 index 0000000..1adcb3e --- /dev/null +++ b/src/CosmosDBTableAsyncOutputCacheProvider/TableOperationHelper.cs @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See the License.txt file in the project root for full license information. + +namespace Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider { + using Microsoft.Azure.CosmosDB.Table; + using System; + + static class TableOperationHelper { + public static TableOperation Retrieve(string rowkey) { + return TableOperation.Retrieve(CacheEntity.GeneratePartitionKey(rowkey), CacheEntity.SanitizeKey(rowkey)); + } + + public static TableOperation Delete(string rowkey) { + var entryToDel = new CacheEntity(rowkey, null, DateTime.UtcNow) { + ETag = "*" + }; + + return TableOperation.Delete(entryToDel); + } + + public static TableOperation InsertOrReplace(string key, object cacheItem, DateTime utcExpiry) { + var cacheEntry = new CacheEntity(key, cacheItem, utcExpiry); + return TableOperation.InsertOrReplace(cacheEntry); + } + } +} diff --git a/src/OutputCacheModuleAsync/Microsoft.AspNet.OutputCache.OutputCacheModuleAsync.csproj b/src/OutputCacheModuleAsync/Microsoft.AspNet.OutputCache.OutputCacheModuleAsync.csproj index c9445c4..f757bff 100644 --- a/src/OutputCacheModuleAsync/Microsoft.AspNet.OutputCache.OutputCacheModuleAsync.csproj +++ b/src/OutputCacheModuleAsync/Microsoft.AspNet.OutputCache.OutputCacheModuleAsync.csproj @@ -1,7 +1,7 @@  - + Debug AnyCPU diff --git a/src/SQLAsyncOutputCacheProvider/Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.csproj b/src/SQLAsyncOutputCacheProvider/Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.csproj index ebe8ae0..94a7020 100644 --- a/src/SQLAsyncOutputCacheProvider/Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.csproj +++ b/src/SQLAsyncOutputCacheProvider/Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.csproj @@ -1,7 +1,7 @@  - + Debug AnyCPU diff --git a/src/SQLAsyncOutputCacheProvider/SqlOutputCacheRepository.cs b/src/SQLAsyncOutputCacheProvider/SqlOutputCacheRepository.cs index 06db06d..b44c427 100644 --- a/src/SQLAsyncOutputCacheProvider/SqlOutputCacheRepository.cs +++ b/src/SQLAsyncOutputCacheProvider/SqlOutputCacheRepository.cs @@ -83,7 +83,7 @@ internal SqlOutputCacheRepository(NameValueCollection config, bool createDb) { internal bool IsUsingInMemoryTable { get; private set; } - internal static Func GetConnectString = + internal static Func GetConnectString = (connectionStrName) => ConfigurationManager.ConnectionStrings[connectionStrName]?.ConnectionString; #endregion @@ -121,8 +121,7 @@ public async Task SetAsync(string key, object entry, DateTime utcExpiry) { //Otherwise it will insert the entry. if (await DoesKeyExistAsync(key)) { await UpdateEntryAsync(key, entry, utcExpiry); - } - else { + } else { await InsertEntryAsync(key, entry, utcExpiry); } } @@ -170,18 +169,15 @@ public void Set(string key, object entry, DateTime utcExpiry) { //Otherwise it will insert the entry. if (DoesKeyExist(key)) { UpdateEntry(key, entry, utcExpiry); - } - else { + } else { InsertEntry(key, entry, utcExpiry); } } #endregion - + #region private Async Methods private async Task RemoveEntryAsync(string key) { - using (var cmd = new SqlCommand()) { - cmd.CommandText = $@"DELETE FROM {TableName} WHERE [Key] = @key"; - cmd.Parameters.AddWithValue("key", key); + using (var cmd = CreateRemoveEntryCommand(key)) { using (var connection = new SqlConnection(ConnectionString)) { await SqlExecuteNonQueryAsync(connection, cmd); } @@ -189,9 +185,7 @@ private async Task RemoveEntryAsync(string key) { } private async Task DoesKeyExistAsync(string key) { - using (var cmd = new SqlCommand()) { - cmd.CommandText = $@"SELECT [Key] FROM {TableName} WHERE [Key] = @key"; - cmd.Parameters.AddWithValue("key", key); + using (var cmd = CreateDoesKeyExistCommand(key)) { using (var connection = new SqlConnection(ConnectionString)) { using (var reader = await SqlExecuteReaderAsync(connection, cmd)) { if (await reader.ReadAsync()) { @@ -204,11 +198,7 @@ private async Task DoesKeyExistAsync(string key) { } private async Task UpdateEntryAsync(string key, object entry, DateTime utcExpiry) { - using (var cmd = new SqlCommand()) { - cmd.CommandText = $@"UPDATE {TableName} SET [Value] = @value,[UtcExpiry]=@utcExpiry WHERE [Key] = @key"; - cmd.Parameters.AddWithValue("key", key); - cmd.Parameters.AddWithValue("value", BinarySerializer.Serialize(entry)); - cmd.Parameters.AddWithValue("utcExpiry", utcExpiry.ToUniversalTime()); + using (var cmd = CreateUpdateEntryCommand(key, entry, utcExpiry)) { using (var connection = new SqlConnection(ConnectionString)) { await SqlExecuteNonQueryAsync(connection, cmd); } @@ -216,11 +206,7 @@ private async Task UpdateEntryAsync(string key, object entry, DateTime utcExpiry } private async Task InsertEntryAsync(string key, object entry, DateTime utcExpiry) { - using (var cmd = new SqlCommand()) { - cmd.CommandText = $@"INSERT INTO {TableName} ([Key], [Value], [UtcExpiry]) VALUES (@key, @value, @utcExpiry)"; - cmd.Parameters.AddWithValue("key", key); - cmd.Parameters.AddWithValue("value", BinarySerializer.Serialize(entry)); - cmd.Parameters.AddWithValue("utcExpiry", utcExpiry.ToUniversalTime()); + using (var cmd = CreateInsertEntryCommand(key, entry, utcExpiry)) { using (var connection = new SqlConnection(ConnectionString)) { await SqlExecuteNonQueryAsync(connection, cmd); } @@ -229,9 +215,7 @@ private async Task InsertEntryAsync(string key, object entry, DateTime u } private async Task GetNonExpiredEntryAsync(string key) { - using (var cmd = new SqlCommand()) { - cmd.CommandText = $@"SELECT * FROM {TableName} WHERE [Key] = @key"; - cmd.Parameters.AddWithValue("key", key); + using (var cmd = CreateGetNonExpiredEntryCommand(key)) { using (var connection = new SqlConnection(ConnectionString)) { using (var reader = await SqlExecuteReaderAsync(connection, cmd)) { if (await reader.ReadAsync()) { @@ -248,76 +232,187 @@ private async Task GetNonExpiredEntryAsync(string key) { private static async Task OpenConnectionAsync(SqlConnection sqlConnection) { try { if (sqlConnection.State != ConnectionState.Open) { - await sqlConnection.OpenAsync().ConfigureAwait(false); + await sqlConnection.OpenAsync(); } } catch (SqlException e) { - if (e != null && + HandleOpenConnectionException(e, sqlConnection); + } catch (Exception e) { + // just throw, we have a different Exception + throw new HttpException(SR.Cant_connect_sql_session_database, e); + } + } + + private static void HandleOpenConnectionException(SqlException e, SqlConnection sqlConnection) { + if (e != null && (e.Number == SQL_LOGIN_FAILED || e.Number == SQL_LOGIN_FAILED_2 || e.Number == SQL_LOGIN_FAILED_3)) { - string user; - - SqlConnectionStringBuilder scsb = new SqlConnectionStringBuilder(sqlConnection.ConnectionString); - if (scsb.IntegratedSecurity) { - user = WindowsIdentity.GetCurrent().Name; - } else { - user = scsb.UserID; - } + string user; - throw new HttpException(string.Format(SR.Login_failed_sql_session_database, user), e); + SqlConnectionStringBuilder scsb = new SqlConnectionStringBuilder(sqlConnection.ConnectionString); + if (scsb.IntegratedSecurity) { + user = WindowsIdentity.GetCurrent().Name; + } else { + user = scsb.UserID; } - } catch (Exception e) { - // just throw, we have a different Exception - throw new HttpException(SR.Cant_connect_sql_session_database, e); + + throw new HttpException(string.Format(SR.Login_failed_sql_session_database, user), e); } } private static async Task SqlExecuteReaderAsync(SqlConnection connection, SqlCommand sqlCmd) { sqlCmd.Connection = connection; - await OpenConnectionAsync(connection).ConfigureAwait(false); - return await sqlCmd.ExecuteReaderAsync().ConfigureAwait(false); + await OpenConnectionAsync(connection); + return await sqlCmd.ExecuteReaderAsync(); } private static async Task SqlExecuteNonQueryAsync(SqlConnection connection, SqlCommand sqlCommand) { sqlCommand.Connection = connection; - await OpenConnectionAsync(connection).ConfigureAwait(false); - await sqlCommand.ExecuteNonQueryAsync().ConfigureAwait(false); + await OpenConnectionAsync(connection); + await sqlCommand.ExecuteNonQueryAsync(); } #endregion - - + #region private Sync Methods private object GetNonExpiredEntry(string key) { - return GetNonExpiredEntryAsync(key).GetAwaiter().GetResult(); + using (var cmd = CreateGetNonExpiredEntryCommand(key)) { + using (var connection = new SqlConnection(ConnectionString)) { + using (var reader = SqlExecuteReader(connection, cmd)) { + if (reader.Read()) { + if ((DateTime)reader["UtcExpiry"] > DateTime.Now.ToUniversalTime()) { + return BinarySerializer.Deserialize((byte[])reader["Value"]); + } + } + } + } + } + return null; } private object InsertEntry(string key, object entry, DateTime utcExpiry) { - return InsertEntryAsync(key, entry, utcExpiry).GetAwaiter().GetResult(); + using (var cmd = CreateInsertEntryCommand(key, entry, utcExpiry)) { + using (var connection = new SqlConnection(ConnectionString)) { + SqlExecuteNonQuery(connection, cmd); + } + return entry; + } } private bool DoesKeyExist(string key) { - return DoesKeyExistAsync(key).GetAwaiter().GetResult(); + using (var cmd = CreateDoesKeyExistCommand(key)) { + using (var connection = new SqlConnection(ConnectionString)) { + using (var reader = SqlExecuteReader(connection, cmd)) { + if (reader.Read()) { + return true; + } + return false; + } + } + } } private void RemoveEntry(string key) { - RemoveEntryAsync(key).GetAwaiter().GetResult(); + using (var cmd = CreateRemoveEntryCommand(key)) { + using (var connection = new SqlConnection(ConnectionString)) { + SqlExecuteNonQuery(connection, cmd); + } + } } private void UpdateEntry(string key, object entry, DateTime utcExpiry) { - UpdateEntryAsync(key, entry, utcExpiry).GetAwaiter().GetResult(); + using (var cmd = CreateUpdateEntryCommand(key, entry, utcExpiry)) { + using (var connection = new SqlConnection(ConnectionString)) { + SqlExecuteNonQuery(connection, cmd); + } + } } private void CreateTableIfNotExists(string createTableSql) { using (var cmd = new SqlCommand()) { cmd.CommandText = createTableSql; using (var connection = new SqlConnection(ConnectionString)) { - SqlExecuteNonQueryAsync(connection, cmd).GetAwaiter().GetResult(); + SqlExecuteNonQuery(connection, cmd); } } } + + private static SqlDataReader SqlExecuteReader(SqlConnection connection, SqlCommand sqlCmd) { + sqlCmd.Connection = connection; + + OpenConnection(connection); + return sqlCmd.ExecuteReader(); + } + + private static void SqlExecuteNonQuery(SqlConnection connection, SqlCommand sqlCommand) { + sqlCommand.Connection = connection; + + OpenConnection(connection); + sqlCommand.ExecuteNonQuery(); + } + + private static void OpenConnection(SqlConnection sqlConnection) { + try { + if (sqlConnection.State != ConnectionState.Open) { + sqlConnection.Open(); + } + } catch (SqlException e) { + HandleOpenConnectionException(e, sqlConnection); + } catch (Exception e) { + // just throw, we have a different Exception + throw new HttpException(SR.Cant_connect_sql_session_database, e); + } + } + #endregion + + #region private SqlCommand creating methods + private SqlCommand CreateRemoveEntryCommand(string key) { + var cmd = new SqlCommand(); + cmd.CommandText = $@"DELETE FROM {TableName} WHERE [Key] = @key"; + cmd.Parameters.AddWithValue("key", key); + + return cmd; + } + + private SqlCommand CreateDoesKeyExistCommand(string key) { + var cmd = new SqlCommand(); + cmd.CommandText = $@"SELECT [Key] FROM {TableName} WHERE [Key] = @key"; + cmd.Parameters.AddWithValue("key", key); + + return cmd; + } + + private SqlCommand CreateUpdateEntryCommand(string key, object entry, DateTime utcExpiry) { + var cmd = new SqlCommand(); + cmd.CommandText = $@"UPDATE {TableName} SET [Value] = @value,[UtcExpiry]=@utcExpiry WHERE [Key] = @key"; + cmd.Parameters.AddWithValue("key", key); + cmd.Parameters.AddWithValue("value", BinarySerializer.Serialize(entry)); + cmd.Parameters.AddWithValue("utcExpiry", utcExpiry.ToUniversalTime()); + + return cmd; + } + + + private SqlCommand CreateInsertEntryCommand(string key, object entry, DateTime utcExpiry) { + var cmd = new SqlCommand(); + cmd.CommandText = $@"INSERT INTO {TableName} ([Key], [Value], [UtcExpiry]) VALUES (@key, @value, @utcExpiry)"; + cmd.Parameters.AddWithValue("key", key); + cmd.Parameters.AddWithValue("value", BinarySerializer.Serialize(entry)); + cmd.Parameters.AddWithValue("utcExpiry", utcExpiry.ToUniversalTime()); + + return cmd; + } + + private SqlCommand CreateGetNonExpiredEntryCommand(string key) { + var cmd = new SqlCommand(); + cmd.CommandText = $@"SELECT * FROM {TableName} WHERE [Key] = @key"; + cmd.Parameters.AddWithValue("key", key); + + return cmd; + } + #endregion } } \ No newline at end of file diff --git a/src/packages/CosmosDBTableAsyncOutputCacheProvider.nupkg/Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.nuproj b/src/packages/CosmosDBTableAsyncOutputCacheProvider.nupkg/Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.nuproj new file mode 100644 index 0000000..af90d52 --- /dev/null +++ b/src/packages/CosmosDBTableAsyncOutputCacheProvider.nupkg/Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.nuproj @@ -0,0 +1,29 @@ + + + + + $(MSBuildProjectName) + $(MSBuildProjectName) + Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.nuspec + true + + + + $(AssemblyPath) + lib\Net462 + + + $(OutputPath) + lib\Net462 + + + $(OutputPath) + lib\Net462 + + + + content\Net462 + + + + \ No newline at end of file diff --git a/src/packages/CosmosDBTableAsyncOutputCacheProvider.nupkg/Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.nuspec b/src/packages/CosmosDBTableAsyncOutputCacheProvider.nupkg/Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.nuspec new file mode 100644 index 0000000..def90fd --- /dev/null +++ b/src/packages/CosmosDBTableAsyncOutputCacheProvider.nupkg/Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.nuspec @@ -0,0 +1,23 @@ + + + + $NuGetPackageId$ + $NuGetPackageVersion$ + Microsoft + Microsoft + © Microsoft Corporation. All rights reserved. + + + + + Microsoft ASP.NET CosmosDB Table Async OutputCache Provider + In .Net 4.6.2, asp.net enables developer plug in async version of OutputCache module which is a good fit for the non-in-memory OutputCache data store. This OutputCache provider can use both CosmosDB(table model) and Azure storage table as the data store and leverages async operation to provide better scability. + Async version CosmosDB/Azure Storage Table OutputCache Provider + en-US + http://www.asp.net/ + http://www.microsoft.com/web/webpi/eula/net_library_eula_ENU.htm + http://go.microsoft.com/fwlink/?LinkID=288859 + true + ASP.NET CosmosDB Azure Storage Table Async OutputCache Provider + + \ No newline at end of file diff --git a/src/packages/CosmosDBTableAsyncOutputCacheProvider.nupkg/content/Net462/web.config.install.xdt b/src/packages/CosmosDBTableAsyncOutputCacheProvider.nupkg/content/Net462/web.config.install.xdt new file mode 100644 index 0000000..5ac6fed --- /dev/null +++ b/src/packages/CosmosDBTableAsyncOutputCacheProvider.nupkg/content/Net462/web.config.install.xdt @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/packages/CosmosDBTableAsyncOutputCacheProvider.nupkg/content/Net462/web.config.uninstall.xdt b/src/packages/CosmosDBTableAsyncOutputCacheProvider.nupkg/content/Net462/web.config.uninstall.xdt new file mode 100644 index 0000000..0a210c1 --- /dev/null +++ b/src/packages/CosmosDBTableAsyncOutputCacheProvider.nupkg/content/Net462/web.config.uninstall.xdt @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/packages/OutputCacheModuleAsync.nupkg/Microsoft.AspNet.OutputCache.OutputCacheModuleAsync.nuproj b/src/packages/OutputCacheModuleAsync.nupkg/Microsoft.AspNet.OutputCache.OutputCacheModuleAsync.nuproj index 814e7fb..9a50cfa 100644 --- a/src/packages/OutputCacheModuleAsync.nupkg/Microsoft.AspNet.OutputCache.OutputCacheModuleAsync.nuproj +++ b/src/packages/OutputCacheModuleAsync.nupkg/Microsoft.AspNet.OutputCache.OutputCacheModuleAsync.nuproj @@ -1,10 +1,11 @@ - + $(MSBuildProjectName) $(MSBuildProjectName) Microsoft.AspNet.OutputCache.OutputCacheModuleAsync.nuspec + true diff --git a/src/packages/OutputCacheModuleAsync.nupkg/Microsoft.AspNet.OutputCache.OutputCacheModuleAsync.nuspec b/src/packages/OutputCacheModuleAsync.nupkg/Microsoft.AspNet.OutputCache.OutputCacheModuleAsync.nuspec index 413f371..a18583e 100644 --- a/src/packages/OutputCacheModuleAsync.nupkg/Microsoft.AspNet.OutputCache.OutputCacheModuleAsync.nuspec +++ b/src/packages/OutputCacheModuleAsync.nupkg/Microsoft.AspNet.OutputCache.OutputCacheModuleAsync.nuspec @@ -12,6 +12,7 @@ en-US http://www.asp.net/ http://www.microsoft.com/web/webpi/eula/net_library_eula_ENU.htm + http://go.microsoft.com/fwlink/?LinkID=288859 true ASP.NET Async OutputCache Module diff --git a/src/packages/Packages.csproj b/src/packages/Packages.csproj index 3cf7781..7368414 100644 --- a/src/packages/Packages.csproj +++ b/src/packages/Packages.csproj @@ -20,6 +20,7 @@ + diff --git a/src/packages/SQLAsyncOutputCacheProvider.nupkg/Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.nuproj b/src/packages/SQLAsyncOutputCacheProvider.nupkg/Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.nuproj index ced4773..b3f77c2 100644 --- a/src/packages/SQLAsyncOutputCacheProvider.nupkg/Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.nuproj +++ b/src/packages/SQLAsyncOutputCacheProvider.nupkg/Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.nuproj @@ -1,10 +1,11 @@ - + $(MSBuildProjectName) $(MSBuildProjectName) Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.nuspec + true diff --git a/src/packages/SQLAsyncOutputCacheProvider.nupkg/Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.nuspec b/src/packages/SQLAsyncOutputCacheProvider.nupkg/Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.nuspec index 1f41769..205ea06 100644 --- a/src/packages/SQLAsyncOutputCacheProvider.nupkg/Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.nuspec +++ b/src/packages/SQLAsyncOutputCacheProvider.nupkg/Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.nuspec @@ -15,6 +15,7 @@ en-US http://www.asp.net/ http://www.microsoft.com/web/webpi/eula/net_library_eula_ENU.htm + http://go.microsoft.com/fwlink/?LinkID=288859 true ASP.NET SQL Async OutputCache Provider diff --git a/test/Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.Test/CacheEntityTest.cs b/test/Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.Test/CacheEntityTest.cs new file mode 100644 index 0000000..5a5f9a0 --- /dev/null +++ b/test/Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.Test/CacheEntityTest.cs @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See the License.txt file in the project root for full license information. + +namespace Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.Test +{ + using System; + using Xunit; + + public class CacheEntityTest + { + [Fact] + public void SanitizeKey_Should_Replace_Invalid_Chars_In_Key() + { + // invalid chars: '/', '\\', '?', '#' + var sanitizedKey = CacheEntity.SanitizeKey("/k\\e?y#"); + Assert.Equal("_k_e_y_", sanitizedKey); + + sanitizedKey = CacheEntity.SanitizeKey("_key_/\\?#"); + Assert.Equal("_key_____", sanitizedKey); + + sanitizedKey = CacheEntity.SanitizeKey("#\\k?#e/\\y?/"); + Assert.Equal("__k__e__y__", sanitizedKey); + } + + [Fact] + public void GeneratePartitionKey_Should_Generate_PartitionKey_Based_On_Key_Length() + { + var pkey = CacheEntity.GeneratePartitionKey(""); + Assert.Equal("0", pkey); + + pkey = CacheEntity.GeneratePartitionKey("a"); + Assert.Equal("1", pkey); + + pkey = CacheEntity.GeneratePartitionKey("123456789"); + Assert.Equal("9", pkey); + + pkey = CacheEntity.GeneratePartitionKey("1234567890"); + Assert.Equal("0", pkey); + + pkey = CacheEntity.GeneratePartitionKey("123456789012"); + Assert.Equal("2", pkey); + } + + [Fact] + public void Constructor_Should_Initialize_Properties_Correctly() + { + var cacheItem = new object(); + var expiry = DateTime.UtcNow.AddMinutes(10); + var ce = new CacheEntity("/webform.aspxac", cacheItem, expiry); + + Assert.Equal("_webform.aspxac", ce.RowKey); + Assert.Equal(cacheItem, ce.CacheItem); + Assert.Equal("5", ce.PartitionKey); + Assert.Equal(expiry, ce.UtcExpiry); + } + } +} diff --git a/test/Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.Test/CosmosDBTableAsyncOutputCacheProviderTest.cs b/test/Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.Test/CosmosDBTableAsyncOutputCacheProviderTest.cs new file mode 100644 index 0000000..2ff5fb5 --- /dev/null +++ b/test/Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.Test/CosmosDBTableAsyncOutputCacheProviderTest.cs @@ -0,0 +1,126 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See the License.txt file in the project root for full license information. + +namespace Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.Test +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + using Xunit; + using Moq; + using System.Collections.Specialized; + + public class CosmosDBTableAsyncOutputCacheProviderTest + { + private const string ProviderName = "MyCosmosDBTableAsyncOutputCacheProvider"; + private static readonly NameValueCollection TestConfig = new NameValueCollection(); + private const string TestKey = "testkey"; + private static readonly object TestEntry = new object(); + private static readonly DateTime TestExpiry = DateTime.UtcNow.AddSeconds(100); + + [Fact] + public void Add_Should_Return_Result_From_Repository_Add() + { + var repoMoq = new Mock(); + repoMoq.Setup(repo => repo.Add(TestKey, TestEntry, TestExpiry)).Returns(TestEntry); + var provider = CreateAndInitProvider(repoMoq.Object); + + var addedEntry = provider.Add(TestKey, TestEntry, TestExpiry); + Assert.Equal(TestEntry, addedEntry); + } + + [Fact] + public async void AddAsync_Should_Return_Result_From_Repository_AddAsync() + { + var repoMoq = new Mock(); + repoMoq.Setup(repo => repo.AddAsync(TestKey, TestEntry, TestExpiry)) + .Returns(Task.FromResult(TestEntry)); + var provider = CreateAndInitProvider(repoMoq.Object); + + var addedEntry = await provider.AddAsync(TestKey, TestEntry, TestExpiry); + Assert.Equal(TestEntry, addedEntry); + } + + [Fact] + public void Get_Should_Return_Result_From_Repository_Get() + { + var repoMoq = new Mock(); + repoMoq.Setup(repo => repo.Get(TestKey)).Returns(TestEntry); + var provider = CreateAndInitProvider(repoMoq.Object); + + var result = provider.Get(TestKey); + Assert.Equal(TestEntry, result); + } + + [Fact] + public async void GetAsync_Should_Return_Result_From_Repository_GetAsync() + { + var repoMoq = new Mock(); + repoMoq.Setup(repo => repo.GetAsync(TestKey)).Returns(Task.FromResult(TestEntry)); + var provider = CreateAndInitProvider(repoMoq.Object); + + var result = await provider.GetAsync(TestKey); + Assert.Equal(TestEntry, result); + } + + [Fact] + public void Remove_Should_Call_Repository_Remove() + { + var repoMoq = new Mock(); + var removeCalled = false; + repoMoq.Setup(repo => repo.Remove(TestKey)).Callback(() => removeCalled = true); + var provider = CreateAndInitProvider(repoMoq.Object); + + provider.Remove(TestKey); + Assert.True(removeCalled); + } + + [Fact] + public async void RemoveAsync_Should_Call_Repository_RemoveAsync() + { + var repoMoq = new Mock(); + var removeAsyncCalled = false; + repoMoq.Setup(repo => repo.RemoveAsync(TestKey)).Returns(Task.CompletedTask) + .Callback(() => removeAsyncCalled = true); + var provider = CreateAndInitProvider(repoMoq.Object); + + await provider.RemoveAsync(TestKey); + Assert.True(removeAsyncCalled); + } + + [Fact] + public void Set_Should_Call_Repository_Set() + { + var repoMoq = new Mock(); + var setCalled = false; + repoMoq.Setup(repo => repo.Set(TestKey, TestEntry, TestExpiry)).Callback(() => setCalled = true); + var provider = CreateAndInitProvider(repoMoq.Object); + + provider.Set(TestKey, TestEntry, TestExpiry); + Assert.True(setCalled); + } + + [Fact] + public async void SetAsync_Should_Call_Repository_SetAsync() + { + var repoMoq = new Mock(); + var setAsyncCalled = false; + repoMoq.Setup(repo => repo.SetAsync(TestKey, TestEntry, TestExpiry)).Returns(Task.CompletedTask) + .Callback(() => setAsyncCalled = true); + var provider = CreateAndInitProvider(repoMoq.Object); + + await provider.SetAsync(TestKey, TestEntry, TestExpiry); + Assert.True(setAsyncCalled); + } + + private CosmosDBTableAsyncOutputCacheProvider CreateAndInitProvider(ITableOutputCacheRepository repo) + { + var provider = new CosmosDBTableAsyncOutputCacheProvider(); + provider.Initialize(ProviderName, TestConfig, repo); + + return provider; + } + } +} diff --git a/test/Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.Test/Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.Test.csproj b/test/Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.Test/Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.Test.csproj new file mode 100644 index 0000000..e687dd3 --- /dev/null +++ b/test/Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.Test/Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.Test.csproj @@ -0,0 +1,84 @@ + + + + + Debug + AnyCPU + {107EF28E-05AA-485E-A5C1-9A20A6DFE877} + Library + Properties + Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.Test + Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.Test + v4.7 + 512 + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + + + ..\..\tools\35MSSharedLib1024.snk + + + true + + + + + + + + + + + + + + + + + + + + 4.9.0 + + + 2.3.1 + + + 2.3.1 + + + 2.3.1 + + + + + 35MSSharedLib1024.snk + + + + + {9fdbb781-c469-4d9e-b687-4087cd422b13} + Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider + + + + \ No newline at end of file diff --git a/test/Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.Test/Properties/AssemblyInfo.cs b/test/Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.Test/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..2bfcaf3 --- /dev/null +++ b/test/Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.Test/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.Test")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Microsoft.AspNet.OutputCache.CosmosDBTableAsyncOutputCacheProvider.Test")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("107ef28e-05aa-485e-a5c1-9a20a6dfe877")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/test/Microsoft.AspNet.OutputCache.OutputCacheModuleAsync.Test/35MSSharedLib1024.snk b/test/Microsoft.AspNet.OutputCache.OutputCacheModuleAsync.Test/35MSSharedLib1024.snk deleted file mode 100644 index 695f1b38774e839e5b90059bfb7f32df1dff4223..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 160 zcmV;R0AK$ABme*efB*oL000060ssI2Bme+XQ$aBR1ONa50098C{E+7Ye`kjtcRG*W zi8#m|)B?I?xgZ^2Sw5D;l4TxtPwG;3)3^j?qDHjEteSTF{rM+4WI`v zCD?tsZ^;k+S&r1&HRMb=j738S=;J$tCKNrc$@P|lZMicrosoft.AspNet.OutputCache.OutputCacheModuleAsync.Test v4.6.2 512 - - + true true @@ -38,7 +37,7 @@ true - 35MSSharedLib1024.snk + ..\..\tools\35MSSharedLib1024.snk true @@ -89,7 +88,9 @@ - + + 35MSSharedLib1024.snk + diff --git a/test/Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.Test/35MSSharedLib1024.snk b/test/Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.Test/35MSSharedLib1024.snk deleted file mode 100644 index 695f1b38774e839e5b90059bfb7f32df1dff4223..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 160 zcmV;R0AK$ABme*efB*oL000060ssI2Bme+XQ$aBR1ONa50098C{E+7Ye`kjtcRG*W zi8#m|)B?I?xgZ^2Sw5D;l4TxtPwG;3)3^j?qDHjEteSTF{rM+4WI`v zCD?tsZ^;k+S&r1&HRMb=j738S=;J$tCKNrc$@P|lZMicrosoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.Test v4.6.2 512 - - + true true @@ -41,7 +40,7 @@ true - 35MSSharedLib1024.snk + ..\..\tools\35MSSharedLib1024.snk @@ -87,7 +86,9 @@ - + + 35MSSharedLib1024.snk + diff --git a/test/Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.Test/SQLAsyncOutputCacheProviderTest.cs b/test/Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.Test/SQLAsyncOutputCacheProviderTest.cs index 3955078..2f77e31 100644 --- a/test/Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.Test/SQLAsyncOutputCacheProviderTest.cs +++ b/test/Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.Test/SQLAsyncOutputCacheProviderTest.cs @@ -25,7 +25,7 @@ public async void AddAsync_Should_Return_Result_From_Repository_AddAsync() { var provider = CreateAndInitProvider(repoMoq.Object); var result = await provider.AddAsync(TestKey, TestEntry, TestExpiry); - Assert.Equal(TestEntry, TestEntry); + Assert.Equal(TestEntry, result); } [Fact] @@ -38,7 +38,7 @@ public async void AddAsync_Should_Use_AbsoluteExpiration_From_CacheItemPolicy_As var provider = CreateAndInitProvider(repoMoq.Object); var result = await provider.AddAsync(TestKey, TestEntry, policy); - Assert.Equal(TestEntry, TestEntry); + Assert.Equal(TestEntry, result); } [Fact] @@ -108,7 +108,7 @@ public void Add_Should_Return_Result_From_Repository_Add() { var provider = CreateAndInitProvider(repoMoq.Object); var result = provider.Add(TestKey, TestEntry, TestExpiry); - Assert.Equal(TestEntry, TestEntry); + Assert.Equal(TestEntry, result); } [Fact] diff --git a/tools/CosmosDBTableAsyncOutputCacheProvider.settings.targets b/tools/CosmosDBTableAsyncOutputCacheProvider.settings.targets new file mode 100644 index 0000000..1e8069e --- /dev/null +++ b/tools/CosmosDBTableAsyncOutputCacheProvider.settings.targets @@ -0,0 +1,25 @@ + + + + rtm + 2018 + 1 + 0 + 0 + + + 1.0.2 + 1.1.0 + + + + + NuGetPackageVersion=$(NuGetPackageVersion); + NuGetPackageId=$(NuGetPackageId); + OutputCacheModuleAsyncNuGetPackageVersion=$(OutputCacheModuleAsyncNuGetPackageVersion); + CosmosDBTableNuGetPackageVersion=$(CosmosDBTableNuGetPackageVersion); + + + + \ No newline at end of file diff --git a/tools/MicrosoftAspNetOutputCache.Extensions.targets b/tools/MicrosoftAspNetOutputCache.Extensions.targets index b0c2257..3de75b3 100644 --- a/tools/MicrosoftAspNetOutputCache.Extensions.targets +++ b/tools/MicrosoftAspNetOutputCache.Extensions.targets @@ -1,9 +1,8 @@ - - + $(NuGetPackageVersion)-b$(VersionBuild) - diff --git a/tools/MicrosoftAspNetOutputCache.settings.targets b/tools/MicrosoftAspNetOutputCache.settings.targets index 5e29bd5..13ad83f 100644 --- a/tools/MicrosoftAspNetOutputCache.settings.targets +++ b/tools/MicrosoftAspNetOutputCache.settings.targets @@ -1,31 +1,18 @@ - + $(MSBuildThisFileDirectory)MicrosoftAspNetOutputCache.Extensions.settings.targets - - - - - - rtm - 2016 - 1 - 0 - 1 - $(VersionRelease)-$(BuildQuality) - - - - 1.0.1 - - - - $([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), Microsoft.Aspnet.OutputCache.sln))\ $([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), Microsoft.Aspnet.OutputCache.sln))\ + + + + Release $(RepositoryRoot)bin\$(Configuration)\ @@ -49,4 +36,21 @@ $(RepositoryRoot)tools\MicrosoftAspNetOutputCache.targets $(CustomAfterMicrosoftCommonTargets) + + + + $(PackageOutputDir) + + + + NuGet + + + + + Microsoft + MsSharedLib72 + + + diff --git a/tools/MicrosoftAspNetOutputCache.targets b/tools/MicrosoftAspNetOutputCache.targets index 5f54917..9cbd8bb 100644 --- a/tools/MicrosoftAspNetOutputCache.targets +++ b/tools/MicrosoftAspNetOutputCache.targets @@ -1,29 +1,13 @@ - - + SetNuSpecProperties;$(BuildDependsOn) - - - $(AssemblyVersion) - - - - - - - - NuGetPackageVersion=$(NuGetPackageVersion); - NuGetPackageId=$(NuGetPackageId); - OutputCacheModuleAsyncNuGetPackageVersion=$(OutputCacheModuleAsyncNuGetPackageVersion); - - - diff --git a/tools/NuGetProj.targets b/tools/NuGetProj.targets index 70e9d99..5f181a1 100644 --- a/tools/NuGetProj.targets +++ b/tools/NuGetProj.targets @@ -304,10 +304,11 @@ GetNuGetProjectOutputs: Reads build-generated files from outputs file. $(PackageOutputDir)\$(NuGetPackageId).$(NuGetPackageVersion).nupkg - $(PackageOutputDir)\$(NuGetPackageId).$(NuGetPackageVersion).symbols.nupkg + $(PackageOutputDir)\$(NuGetPackageId).$(NuGetPackageVersion).symbols.nupkg + diff --git a/tools/OutputCacheModuleAsync.settings.targets b/tools/OutputCacheModuleAsync.settings.targets new file mode 100644 index 0000000..c9c9604 --- /dev/null +++ b/tools/OutputCacheModuleAsync.settings.targets @@ -0,0 +1,18 @@ + + + + rtm + 2016 + 1 + 0 + 2 + + + + + NuGetPackageVersion=$(NuGetPackageVersion); + NuGetPackageId=$(NuGetPackageId); + + + + \ No newline at end of file diff --git a/tools/SQLAsyncOutputCacheProvider.settings.targets b/tools/SQLAsyncOutputCacheProvider.settings.targets new file mode 100644 index 0000000..af2bfe8 --- /dev/null +++ b/tools/SQLAsyncOutputCacheProvider.settings.targets @@ -0,0 +1,23 @@ + + + + rtm + 2016 + 1 + 0 + 1 + + + 1.0.1 + + + + + NuGetPackageVersion=$(NuGetPackageVersion); + NuGetPackageId=$(NuGetPackageId); + OutputCacheModuleAsyncNuGetPackageVersion=$(OutputCacheModuleAsyncNuGetPackageVersion); + + + + \ No newline at end of file diff --git a/tools/version.targets b/tools/version.targets index 90b0928..2043058 100644 --- a/tools/version.targets +++ b/tools/version.targets @@ -36,6 +36,7 @@ Project global versioning targets. $(VersionMajor).$(VersionMinor).$(VersionRelease) + $(NuGetPackageVersion)-$(BuildQuality)