├── icon.ico ├── icon.png ├── Azure.Data.Wrappers ├── icon.ico ├── Properties │ └── AssemblyInfo.cs ├── AzureStorage.cs ├── StorageQueuedMessage.cs ├── AzureStorageFactory.cs ├── StorageQueuePoller.cs ├── FileShare.cs ├── Azure.Data.Wrappers.csproj ├── AzureStorageResources.cs ├── StorageQueueShards.cs ├── StorageQueue.cs ├── Container.cs ├── TableStorage.cs └── Interfaces.cs ├── .gitattributes ├── Azure.Data.Wrappers.Test ├── Properties │ └── AssemblyInfo.cs ├── Integration │ ├── FileShareTests.cs │ ├── AzureStorageFactoryTests.cs │ ├── AzureStorageResourcesTests.cs │ ├── QueueTests.cs │ └── ContainerTests.cs ├── Unit │ ├── AzureStorageResourcesTests.cs │ ├── AzureStorageTests.cs │ ├── FileShareTests.cs │ ├── StorageQueuedMessageTests.cs │ ├── StorageQueueTests.cs │ ├── StorageQueuePollerTests.cs │ ├── StorageQueueShardsTests.cs │ ├── ContainerTests.cs │ └── TableStorageTests.cs ├── TestHelpers.cs └── Azure.Data.Wrappers.Test.csproj ├── README.md ├── LICENSE ├── Azure.Data.Wrappers.sln └── .gitignore /icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Azure.Data.Wrappers/HEAD/icon.ico -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Azure.Data.Wrappers/HEAD/icon.png -------------------------------------------------------------------------------- /Azure.Data.Wrappers/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Azure.Data.Wrappers/HEAD/Azure.Data.Wrappers/icon.ico -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /Azure.Data.Wrappers/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | [assembly: AssemblyTitle("Azure.Data.Wrappers")] 5 | [assembly: AssemblyDescription("Azure Storage Simplified")] 6 | [assembly: AssemblyConfiguration("")] 7 | [assembly: AssemblyCompany("Microsoft")] 8 | [assembly: AssemblyProduct("Azure.Data.Wrappers")] 9 | [assembly: AssemblyCopyright("Copyright © Microsoft 2017")] 10 | [assembly: AssemblyTrademark("")] 11 | [assembly: AssemblyCulture("")] 12 | [assembly: ComVisible(false)] 13 | [assembly: Guid("4e7ae8fe-0ee3-44a4-bae7-2ea46062c10e")] 14 | [assembly: AssemblyVersion("3.0.0.0")] 15 | [assembly: AssemblyFileVersion("3.0.9.0")] -------------------------------------------------------------------------------- /Azure.Data.Wrappers.Test/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | [assembly: AssemblyTitle("Azure.Data.Wrappers.Test")] 5 | [assembly: AssemblyDescription("Azure.Data.Wrappers Tests (Unit & Integration)")] 6 | [assembly: AssemblyConfiguration("")] 7 | [assembly: AssemblyCompany("")] 8 | [assembly: AssemblyProduct("Azure.Data.Wrappers.Test")] 9 | [assembly: AssemblyCopyright("Copyright © Jef King 2017")] 10 | [assembly: AssemblyTrademark("")] 11 | [assembly: AssemblyCulture("")] 12 | [assembly: ComVisible(false)] 13 | [assembly: AssemblyVersion("3.0.0.0")] 14 | [assembly: AssemblyFileVersion("3.0.0.0")] 15 | [assembly: Guid("91edff1c-a9c5-4f92-b3e9-781651577d75")] 16 | -------------------------------------------------------------------------------- /Azure.Data.Wrappers.Test/Integration/FileShareTests.cs: -------------------------------------------------------------------------------- 1 | namespace Azure.Data.Wrappers.Test.Integration 2 | { 3 | using Azure.Data.Wrappers; 4 | using NUnit.Framework; 5 | using System; 6 | using System.Threading.Tasks; 7 | 8 | [TestFixture] 9 | [Category("Integration")] 10 | public class FileShareTests 11 | { 12 | private const string ConnectionString = "UseDevelopmentStorage=true;"; 13 | 14 | [Test] 15 | [Ignore("not supported by development storage")] 16 | public async Task CreateIfNotExists() 17 | { 18 | var random = new Random(); 19 | var storage = new FileShare(string.Format("a{0}b", random.Next()), ConnectionString); 20 | var created = await storage.CreateIfNotExists(); 21 | 22 | Assert.IsTrue(created); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /Azure.Data.Wrappers.Test/Unit/AzureStorageResourcesTests.cs: -------------------------------------------------------------------------------- 1 | namespace Azure.Data.Wrappers.Test.Unit 2 | { 3 | using Azure.Data.Wrappers; 4 | using NUnit.Framework; 5 | 6 | [TestFixture] 7 | public class AzureStorageResourcesTests 8 | { 9 | private readonly string ConnectionString = "UseDevelopmentStorage=true;"; 10 | 11 | [Test] 12 | public void Constructor() 13 | { 14 | new AzureStorageResources(ConnectionString); 15 | } 16 | 17 | [Test] 18 | public void IsIAzureStorageResources() 19 | { 20 | Assert.IsNotNull(new AzureStorageResources(ConnectionString) as IAzureStorageResources); 21 | } 22 | 23 | [Test] 24 | public void IsAzureStorage() 25 | { 26 | Assert.IsNotNull(new AzureStorageResources(ConnectionString) as AzureStorage); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /Azure.Data.Wrappers.Test/TestHelpers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Azure.Data.Wrappers.Test 8 | { 9 | public class TestHelpers 10 | { 11 | public const string DevConnectionString = "UseDevelopmentStorage=true;"; 12 | public const string DevAccountName = "devstoreaccount1"; 13 | public const string DevAccountKey = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw=="; 14 | 15 | public static async Task GetTestContainer() 16 | { 17 | var container = new Container(generateUniqueName(), DevConnectionString); 18 | await container.CreateIfNotExists(); 19 | return container; 20 | } 21 | 22 | public static string generateUniqueName() 23 | { 24 | return 'a' + Guid.NewGuid().ToString().Replace("-", ""); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Nuget Publish Build 2 | ![NuGet Publish Build](https://azuredatawrappers.visualstudio.com/_apis/public/build/definitions/ec2cab4d-fd76-4acf-b43e-63adccf6988e/1/badge) 3 | 4 | # Contributing 5 | 6 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 7 | 8 | # Azure Storage Simplified 9 | - Queues/Blobs/Tables/File Shares 10 | - Azure Storage Resources 11 | - Dependancy Injection 12 | - Mockable for testing 13 | - Prefer async calls 14 | 15 | ## Running Tests 16 | - Make sure the Azure Storage Emulator is running 17 | - execute the NUnit console runner against the test assembly 18 | 19 | # [NuGet](https://www.nuget.org/packages/Azure.Data.Wrappers) 20 | ``` 21 | PM> Install-Package Azure.Data.Wrappers 22 | ``` 23 | 24 | # [Wiki](https://github.com/microsoft/Azure.Data.Wrappers/wiki) 25 | View the wiki to learn how to use this. 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. All rights reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE -------------------------------------------------------------------------------- /Azure.Data.Wrappers.Test/Unit/AzureStorageTests.cs: -------------------------------------------------------------------------------- 1 | namespace Azure.Data.Wrappers.Test.Unit 2 | { 3 | using Azure.Data.Wrappers; 4 | using Microsoft.WindowsAzure.Storage; 5 | using NUnit.Framework; 6 | using System; 7 | 8 | [TestFixture] 9 | public class AzureStorageTests 10 | { 11 | const string ConnectionString = "UseDevelopmentStorage=true"; 12 | 13 | [Test] 14 | public void Constructor() 15 | { 16 | new AzureStorage(ConnectionString); 17 | } 18 | 19 | [Test] 20 | public void IsIStorageAccount() 21 | { 22 | Assert.IsNotNull(new AzureStorage(ConnectionString) as IStorageAccount); 23 | } 24 | 25 | [Test] 26 | public void ConstructorConnectionStringNull() 27 | { 28 | Assert.That(() => new AzureStorage(default(string)), Throws.TypeOf()); 29 | } 30 | 31 | [Test] 32 | public void ConstructorAccountNull() 33 | { 34 | Assert.That(() => new AzureStorage(default(CloudStorageAccount)), Throws.TypeOf()); 35 | } 36 | 37 | [Test] 38 | public void GetSharedAccessSignatureThrowsOnNullPolicy() 39 | { 40 | var target = new AzureStorage(ConnectionString); 41 | 42 | Assert.That(() => target.GetSharedAccessSignature(default(SharedAccessAccountPolicy)), Throws.TypeOf()); 43 | } 44 | 45 | [Test] 46 | public void GetSharedAccessSignatureSuccess() 47 | { 48 | var target = new AzureStorage(ConnectionString); 49 | 50 | var result = target.GetSharedAccessSignature(new SharedAccessAccountPolicy()); 51 | 52 | Assert.IsNotNull(result); 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /Azure.Data.Wrappers.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26430.12 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B65D0744-3BE9-4B13-A7D4-1D30E2587DD9}" 7 | ProjectSection(SolutionItems) = preProject 8 | README.md = README.md 9 | EndProjectSection 10 | EndProject 11 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Data.Wrappers", "Azure.Data.Wrappers\Azure.Data.Wrappers.csproj", "{C73C757E-D847-459E-BEC1-AF681E0F1341}" 12 | EndProject 13 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Data.Wrappers.Test", "Azure.Data.Wrappers.Test\Azure.Data.Wrappers.Test.csproj", "{D8AE1424-0ED0-4475-B915-0A5FF3BD02E4}" 14 | EndProject 15 | Global 16 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 17 | Debug|Any CPU = Debug|Any CPU 18 | Release|Any CPU = Release|Any CPU 19 | EndGlobalSection 20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 21 | {C73C757E-D847-459E-BEC1-AF681E0F1341}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 22 | {C73C757E-D847-459E-BEC1-AF681E0F1341}.Debug|Any CPU.Build.0 = Debug|Any CPU 23 | {C73C757E-D847-459E-BEC1-AF681E0F1341}.Release|Any CPU.ActiveCfg = Release|Any CPU 24 | {C73C757E-D847-459E-BEC1-AF681E0F1341}.Release|Any CPU.Build.0 = Release|Any CPU 25 | {D8AE1424-0ED0-4475-B915-0A5FF3BD02E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 26 | {D8AE1424-0ED0-4475-B915-0A5FF3BD02E4}.Debug|Any CPU.Build.0 = Debug|Any CPU 27 | {D8AE1424-0ED0-4475-B915-0A5FF3BD02E4}.Release|Any CPU.ActiveCfg = Release|Any CPU 28 | {D8AE1424-0ED0-4475-B915-0A5FF3BD02E4}.Release|Any CPU.Build.0 = Release|Any CPU 29 | EndGlobalSection 30 | GlobalSection(SolutionProperties) = preSolution 31 | HideSolutionNode = FALSE 32 | EndGlobalSection 33 | EndGlobal 34 | -------------------------------------------------------------------------------- /Azure.Data.Wrappers/AzureStorage.cs: -------------------------------------------------------------------------------- 1 | namespace Azure.Data.Wrappers 2 | { 3 | using Microsoft.WindowsAzure.Storage; 4 | using System; 5 | 6 | /// 7 | /// Azure Storage 8 | /// 9 | public class AzureStorage : IStorageAccount 10 | { 11 | #region Members 12 | /// 13 | /// Cloud Storage Account 14 | /// 15 | private readonly CloudStorageAccount account; 16 | #endregion 17 | 18 | #region Constructors 19 | /// 20 | /// Constructor 21 | /// 22 | /// Connection String 23 | public AzureStorage(string connectionString) 24 | : this(CloudStorageAccount.Parse(connectionString)) 25 | { 26 | } 27 | 28 | /// 29 | /// Constructor 30 | /// 31 | /// Storage Account 32 | public AzureStorage(CloudStorageAccount account) 33 | { 34 | if (null == account) 35 | { 36 | throw new ArgumentNullException(nameof(account)); 37 | } 38 | 39 | this.account = account; 40 | } 41 | #endregion 42 | 43 | #region Properties 44 | /// 45 | /// Cloud Storage Account 46 | /// 47 | public CloudStorageAccount Account 48 | { 49 | get 50 | { 51 | return this.account; 52 | } 53 | } 54 | 55 | public string GetSharedAccessSignature(SharedAccessAccountPolicy policy) 56 | { 57 | if (policy == null) throw new ArgumentNullException(nameof(policy)); 58 | 59 | return this.account.GetSharedAccessSignature(policy); 60 | } 61 | #endregion 62 | } 63 | } -------------------------------------------------------------------------------- /Azure.Data.Wrappers.Test/Azure.Data.Wrappers.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Azure.Data.Wrappers Tests 5 | 2.1.0 6 | net462 7 | Azure.Data.Wrappers.Test 8 | Library 9 | Azure.Data.Wrappers.Test 10 | false 11 | false 12 | false 13 | false 14 | false 15 | false 16 | false 17 | false 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /Azure.Data.Wrappers.Test/Unit/FileShareTests.cs: -------------------------------------------------------------------------------- 1 | namespace Azure.Data.Wrappers.Test.Unit 2 | { 3 | using Azure.Data.Wrappers; 4 | using Microsoft.WindowsAzure.Storage; 5 | using NUnit.Framework; 6 | using System; 7 | 8 | [TestFixture] 9 | public class FileShareTests 10 | { 11 | private const string ConnectionString = "DefaultEndpointsProtocol=https;AccountName=kingazure;AccountKey=LQFXI8kFSh0TR0dk2bvukQZRxymByGn1amCiR8chpIZ+NkLHqx6IFMcApHGWQutKpWfPloJfNv3ySM+uOJ3f9g==;"; 12 | 13 | [Test] 14 | public void Constructor() 15 | { 16 | new FileShare("test", ConnectionString); 17 | } 18 | 19 | [Test] 20 | public void ConstructorAccount() 21 | { 22 | new FileShare("test", CloudStorageAccount.Parse(ConnectionString)); 23 | } 24 | 25 | [Test] 26 | public void IsAzureStorage() 27 | { 28 | Assert.IsNotNull(new FileShare("test", ConnectionString) as AzureStorage); 29 | } 30 | 31 | [Test] 32 | public void IsIFileShare() 33 | { 34 | Assert.IsNotNull(new FileShare("test", ConnectionString) as IFileShare); 35 | } 36 | 37 | [Test] 38 | public void ConstructorNameNull() 39 | { 40 | Assert.That(() => new FileShare(null, ConnectionString), Throws.TypeOf()); 41 | } 42 | 43 | [Test] 44 | public void ConstructorAccountNameNull() 45 | { 46 | Assert.That(() => new FileShare(null, CloudStorageAccount.Parse(ConnectionString)), Throws.TypeOf()); 47 | } 48 | 49 | [Test] 50 | public void Client() 51 | { 52 | var name = Guid.NewGuid().ToString(); 53 | var t = new FileShare(name, ConnectionString); 54 | Assert.IsNotNull(t.Client); 55 | } 56 | 57 | [Test] 58 | public void Reference() 59 | { 60 | var name = Guid.NewGuid().ToString(); 61 | var t = new FileShare(name, ConnectionString); 62 | Assert.IsNotNull(t.Reference); 63 | } 64 | 65 | [Test] 66 | public void Name() 67 | { 68 | var name = Guid.NewGuid().ToString(); 69 | var t = new FileShare(name, ConnectionString); 70 | Assert.AreEqual(name, t.Name); 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /Azure.Data.Wrappers/StorageQueuedMessage.cs: -------------------------------------------------------------------------------- 1 | namespace Azure.Data.Wrappers 2 | { 3 | using Microsoft.WindowsAzure.Storage.Queue; 4 | using Newtonsoft.Json; 5 | using System; 6 | using System.Threading.Tasks; 7 | 8 | /// 9 | /// Queued Message 10 | /// 11 | /// Type 12 | public class StorageQueuedMessage : IQueued 13 | { 14 | #region Members 15 | /// 16 | /// Storage Queue 17 | /// 18 | protected readonly IStorageQueue queue = null; 19 | 20 | /// 21 | /// Cloud Queue Message 22 | /// 23 | protected readonly CloudQueueMessage message = null; 24 | #endregion 25 | 26 | #region Constructors 27 | /// 28 | /// Default Constructor 29 | /// 30 | /// Queue 31 | /// Cloud Queue Message 32 | public StorageQueuedMessage(IStorageQueue queue, CloudQueueMessage message) 33 | { 34 | if (null == queue) 35 | { 36 | throw new ArgumentNullException("queue"); 37 | } 38 | if (null == message) 39 | { 40 | throw new ArgumentNullException("message"); 41 | } 42 | 43 | this.queue = queue; 44 | this.message = message; 45 | } 46 | #endregion 47 | 48 | #region Methods 49 | /// 50 | /// Delete Message 51 | /// 52 | /// Task 53 | public virtual async Task Complete() 54 | { 55 | await this.queue.Delete(this.message).ConfigureAwait(false); 56 | } 57 | 58 | /// 59 | /// Abandon Message 60 | /// 61 | /// Task 62 | public virtual async Task Abandon() 63 | { 64 | await Task.Factory.StartNew(() => { }).ConfigureAwait(false); //No Abandon? 65 | } 66 | 67 | /// 68 | /// Data 69 | /// 70 | /// Data 71 | public virtual async Task Data() 72 | { 73 | return await Task.Factory.StartNew(() => JsonConvert.DeserializeObject(this.message.AsString)).ConfigureAwait(false); 74 | } 75 | #endregion 76 | } 77 | } -------------------------------------------------------------------------------- /Azure.Data.Wrappers/AzureStorageFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.WindowsAzure.Storage; 3 | using Microsoft.WindowsAzure.Storage.Auth; 4 | using Microsoft.WindowsAzure.Storage.RetryPolicies; 5 | 6 | namespace Azure.Data.Wrappers 7 | { 8 | public class AzureStorageFactory : IAzureStorageFactory 9 | { 10 | public IStorageAccount GetAccount(string accountName, string key, bool useHttps) 11 | { 12 | if (string.IsNullOrEmpty(accountName)) throw new ArgumentNullException(nameof(accountName)); 13 | if (string.IsNullOrEmpty(key)) throw new ArgumentNullException(nameof(key)); 14 | 15 | return new AzureStorage(new CloudStorageAccount(new StorageCredentials(accountName, key), useHttps)); 16 | } 17 | 18 | public IStorageAccount GetAccount(string connectionString) 19 | { 20 | if (string.IsNullOrEmpty(connectionString)) throw new ArgumentNullException(nameof(connectionString)); 21 | 22 | return new AzureStorage(connectionString); 23 | } 24 | 25 | public IStorageQueue GetAzureQueue(IStorageAccount storageAccount, string queueName, int visibilityTimeoutInMS = 300000) 26 | { 27 | if (storageAccount == null) throw new ArgumentNullException(nameof(storageAccount)); 28 | if (string.IsNullOrEmpty(queueName)) throw new ArgumentNullException(nameof(queueName)); 29 | 30 | return new StorageQueue(queueName, storageAccount.Account, new TimeSpan(0, 0, 0, 0, visibilityTimeoutInMS)); 31 | } 32 | 33 | public ITableStorage GetAzureTable(IStorageAccount storageAccount, string tableName) 34 | { 35 | if (storageAccount == null) throw new ArgumentNullException(nameof(storageAccount)); 36 | if (string.IsNullOrEmpty(tableName)) throw new ArgumentNullException(nameof(tableName)); 37 | 38 | return new TableStorage(tableName, storageAccount.Account); 39 | } 40 | 41 | public IContainer GetBlobFileContainer(IStorageAccount storageAccount, string containerName, bool isPublic = false, LocationMode location = LocationMode.PrimaryThenSecondary) 42 | { 43 | if (storageAccount == null) throw new ArgumentNullException(nameof(storageAccount)); 44 | if (string.IsNullOrEmpty(containerName)) throw new ArgumentNullException(nameof(containerName)); 45 | 46 | return new Container(containerName, storageAccount.Account, isPublic, location); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Azure.Data.Wrappers.Test/Unit/StorageQueuedMessageTests.cs: -------------------------------------------------------------------------------- 1 | namespace Azure.Data.Wrappers.Test.Unit 2 | { 3 | using Azure.Data.Wrappers; 4 | using Microsoft.WindowsAzure.Storage.Queue; 5 | using Newtonsoft.Json; 6 | using NSubstitute; 7 | using NUnit.Framework; 8 | using System; 9 | using System.Threading.Tasks; 10 | 11 | [TestFixture] 12 | public class StorageQueuedMessageTests 13 | { 14 | public class Helper 15 | { 16 | public Guid Test 17 | { 18 | get; 19 | set; 20 | } 21 | } 22 | 23 | [Test] 24 | public void Constructor() 25 | { 26 | var queue = Substitute.For(); 27 | new StorageQueuedMessage(queue, new CloudQueueMessage("ship")); 28 | } 29 | 30 | [Test] 31 | public void ConstructorQueueNull() 32 | { 33 | var message = new CloudQueueMessage("ship"); 34 | 35 | Assert.That(() => new StorageQueuedMessage(null, message), Throws.TypeOf()); 36 | } 37 | 38 | [Test] 39 | public void ConstructorMessageNull() 40 | { 41 | var queue = Substitute.For(); 42 | 43 | Assert.That(() => new StorageQueuedMessage(queue, null), Throws.TypeOf()); 44 | } 45 | 46 | [Test] 47 | public async Task Complete() 48 | { 49 | var queue = Substitute.For(); 50 | var message = new CloudQueueMessage("ship"); 51 | await queue.Delete(message); 52 | 53 | var sqm = new StorageQueuedMessage(queue, message); 54 | await sqm.Complete(); 55 | 56 | await queue.Received().Delete(message); 57 | } 58 | 59 | [Test] 60 | public async Task Abandon() 61 | { 62 | var queue = Substitute.For(); 63 | var message = new CloudQueueMessage("ship"); 64 | 65 | var sqm = new StorageQueuedMessage(queue, message); 66 | await sqm.Abandon(); 67 | } 68 | 69 | [Test] 70 | public async Task Data() 71 | { 72 | var expected = new Helper() 73 | { 74 | Test = Guid.NewGuid(), 75 | }; 76 | var json = JsonConvert.SerializeObject(expected); 77 | var queue = Substitute.For(); 78 | var message = new CloudQueueMessage(json); 79 | 80 | var sqm = new StorageQueuedMessage(queue, message); 81 | var data = await sqm.Data(); 82 | 83 | Assert.IsNotNull(data); 84 | Assert.AreEqual(expected.Test, data.Test); 85 | } 86 | } 87 | } -------------------------------------------------------------------------------- /Azure.Data.Wrappers/StorageQueuePoller.cs: -------------------------------------------------------------------------------- 1 | namespace Azure.Data.Wrappers 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | 8 | /// 9 | /// Storage Queue Poller 10 | /// 11 | /// Type 12 | public class StorageQueuePoller : IStorageQueuePoller 13 | { 14 | #region Members 15 | /// 16 | /// Queue 17 | /// 18 | protected readonly IStorageQueue queue = null; 19 | #endregion 20 | 21 | #region Constructors 22 | /// 23 | /// Default Constructor 24 | /// 25 | /// Queue Name 26 | /// Connection String 27 | public StorageQueuePoller(string queueName, string connectionString) 28 | : this(new StorageQueue(queueName, connectionString)) 29 | { 30 | } 31 | 32 | /// 33 | /// Constructor for Mocking 34 | /// 35 | /// Queue 36 | public StorageQueuePoller(IStorageQueue queue) 37 | { 38 | if (null == queue) 39 | { 40 | throw new ArgumentNullException("queue"); 41 | } 42 | 43 | this.queue = queue; 44 | } 45 | #endregion 46 | 47 | #region Properties 48 | /// 49 | /// Storage Queue 50 | /// 51 | public virtual IStorageQueue Queue 52 | { 53 | get 54 | { 55 | return this.queue; 56 | } 57 | } 58 | #endregion 59 | 60 | #region Methods 61 | /// 62 | /// Poll for Queued Message 63 | /// 64 | /// Queued Item 65 | public virtual async Task> Poll() 66 | { 67 | var msg = await this.queue.Get().ConfigureAwait(false); 68 | return null == msg ? null : new StorageQueuedMessage(this.queue, msg); 69 | } 70 | 71 | /// 72 | /// Poll for Queued Message 73 | /// 74 | /// Queued Item 75 | public virtual async Task>> PollMany(int messageCount = 5) 76 | { 77 | messageCount = 0 >= messageCount ? 5 : messageCount; 78 | 79 | var msgs = await this.queue.GetMany(messageCount).ConfigureAwait(false); 80 | if (null == msgs || !msgs.Any()) 81 | { 82 | return null; 83 | } 84 | 85 | return msgs.Where(m => m != null).Select(m => new StorageQueuedMessage(this.queue, m)); 86 | } 87 | #endregion 88 | } 89 | } -------------------------------------------------------------------------------- /Azure.Data.Wrappers.Test/Unit/StorageQueueTests.cs: -------------------------------------------------------------------------------- 1 | namespace Azure.Data.Wrappers.Test.Unit 2 | { 3 | using Azure.Data.Wrappers; 4 | using Microsoft.WindowsAzure.Storage; 5 | using Microsoft.WindowsAzure.Storage.Queue; 6 | using NUnit.Framework; 7 | using System; 8 | 9 | [TestFixture] 10 | public class StorageQueueTests 11 | { 12 | private const string ConnectionString = "UseDevelopmentStorage=true;"; 13 | 14 | [Test] 15 | public void Constructor() 16 | { 17 | new StorageQueue("test", ConnectionString, TimeSpan.FromSeconds(22)); 18 | } 19 | 20 | [Test] 21 | public void IQueue() 22 | { 23 | Assert.IsNotNull(new StorageQueue("test", ConnectionString) as IStorageQueue); 24 | } 25 | 26 | [Test] 27 | public void ConstructorTableNull() 28 | { 29 | Assert.That(() => new StorageQueue(null, ConnectionString), Throws.TypeOf()); 30 | } 31 | 32 | [Test] 33 | public void ConstructorAccountTableNull() 34 | { 35 | Assert.That(() => new StorageQueue(null, CloudStorageAccount.Parse(ConnectionString)), Throws.TypeOf()); 36 | } 37 | 38 | [Test] 39 | public void ConstructorKeyNull() 40 | { 41 | Assert.That(() => new StorageQueue("test", (string)null), Throws.TypeOf()); 42 | } 43 | 44 | [Test] 45 | public void Name() 46 | { 47 | var name = Guid.NewGuid().ToString(); 48 | var t = new StorageQueue(name, ConnectionString); 49 | Assert.AreEqual(name, t.Name); 50 | } 51 | 52 | [Test] 53 | public void Client() 54 | { 55 | var name = Guid.NewGuid().ToString(); 56 | var t = new StorageQueue(name, ConnectionString); 57 | Assert.IsNotNull(t.Client); 58 | } 59 | 60 | [Test] 61 | public void Reference() 62 | { 63 | var name = Guid.NewGuid().ToString(); 64 | var t = new StorageQueue(name, ConnectionString); 65 | Assert.IsNotNull(t.Reference); 66 | } 67 | 68 | [Test] 69 | public void DeleteNull() 70 | { 71 | var name = Guid.NewGuid().ToString(); 72 | var t = new StorageQueue(name, ConnectionString); 73 | 74 | Assert.That(() => t.Delete(null), Throws.TypeOf()); 75 | } 76 | 77 | [Test] 78 | public void SaveMessageNull() 79 | { 80 | var name = Guid.NewGuid().ToString(); 81 | var t = new StorageQueue(name, ConnectionString); 82 | 83 | Assert.That(() => t.Send((CloudQueueMessage)null), Throws.TypeOf()); 84 | } 85 | 86 | [Test] 87 | public void SaveNull() 88 | { 89 | var name = Guid.NewGuid().ToString(); 90 | var t = new StorageQueue(name, ConnectionString); 91 | 92 | Assert.That(() => t.Send((object)null), Throws.TypeOf()); 93 | } 94 | } 95 | } -------------------------------------------------------------------------------- /Azure.Data.Wrappers/FileShare.cs: -------------------------------------------------------------------------------- 1 | namespace Azure.Data.Wrappers 2 | { 3 | using Microsoft.WindowsAzure.Storage; 4 | using Microsoft.WindowsAzure.Storage.File; 5 | using System; 6 | using System.Threading.Tasks; 7 | 8 | /// 9 | /// File Share 10 | /// 11 | public class FileShare : AzureStorage, IFileShare 12 | { 13 | #region Members 14 | /// 15 | /// Client 16 | /// 17 | private readonly CloudFileClient client; 18 | 19 | /// 20 | /// Reference 21 | /// 22 | private readonly CloudFileShare reference; 23 | #endregion 24 | 25 | #region Constructors 26 | /// 27 | /// File Share Constructor 28 | /// 29 | /// Name 30 | /// Connection String 31 | public FileShare(string name, string connectionString) 32 | : base(connectionString) 33 | { 34 | if (string.IsNullOrWhiteSpace(name)) 35 | { 36 | throw new ArgumentException("name"); 37 | } 38 | 39 | this.client = this.Account.CreateCloudFileClient(); 40 | this.reference = this.client.GetShareReference(name); 41 | } 42 | 43 | /// 44 | /// File Share Constructor 45 | /// 46 | /// Name 47 | /// Storage Account 48 | public FileShare(string name, CloudStorageAccount account) 49 | : base(account) 50 | { 51 | if (string.IsNullOrWhiteSpace(name)) 52 | { 53 | throw new ArgumentException("name"); 54 | } 55 | 56 | this.client = this.Account.CreateCloudFileClient(); 57 | this.reference = this.client.GetShareReference(name); 58 | } 59 | #endregion 60 | 61 | #region Properties 62 | /// 63 | /// Name 64 | /// 65 | public virtual string Name 66 | { 67 | get 68 | { 69 | return this.reference.Name; 70 | } 71 | } 72 | 73 | /// 74 | /// Client 75 | /// 76 | public virtual CloudFileClient Client 77 | { 78 | get 79 | { 80 | return this.client; 81 | } 82 | } 83 | 84 | /// 85 | /// Reference 86 | /// 87 | public virtual CloudFileShare Reference 88 | { 89 | get 90 | { 91 | return this.reference; 92 | } 93 | } 94 | #endregion 95 | 96 | #region Methods 97 | /// 98 | /// Create If Not Exists 99 | /// 100 | /// Created 101 | public virtual async Task CreateIfNotExists() 102 | { 103 | return await this.reference.CreateIfNotExistsAsync().ConfigureAwait(false); 104 | } 105 | #endregion 106 | } 107 | } -------------------------------------------------------------------------------- /Azure.Data.Wrappers.Test/Integration/AzureStorageFactoryTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Microsoft.WindowsAzure.Storage; 7 | using Microsoft.WindowsAzure.Storage.Auth; 8 | using Microsoft.WindowsAzure.Storage.Queue; 9 | using NSubstitute; 10 | using NUnit.Framework; 11 | 12 | namespace Azure.Data.Wrappers.Test.Integration 13 | { 14 | [SetUpFixture] 15 | [TestFixture] 16 | [Category("Integration")] 17 | public class AzureStorageFactoryTests 18 | { 19 | private static IStorageAccount mockAccount; 20 | 21 | [OneTimeSetUp] 22 | public void ClassInit() 23 | { 24 | mockAccount = Substitute.For(); 25 | mockAccount.Account.Returns(new CloudStorageAccount(new StorageCredentials(TestHelpers.DevAccountName, TestHelpers.DevAccountKey), true)); 26 | } 27 | 28 | [TestFixture] 29 | [Category("Integration")] 30 | public class CreateAccount 31 | { 32 | [Test] 33 | public void ValidatesArgs() 34 | { 35 | //Arrange 36 | var target = new AzureStorageFactory(); 37 | 38 | //Act Assert 39 | Assert.That(() => target.GetAccount(null), Throws.ArgumentNullException); 40 | Assert.That(() => target.GetAccount(null, "key", true), Throws.ArgumentNullException); 41 | Assert.That(() => target.GetAccount("foo", null, true), Throws.ArgumentNullException); 42 | } 43 | 44 | [Test] 45 | public void SuccessWithConnectionString() 46 | { 47 | //Arrange 48 | var target = new AzureStorageFactory(); 49 | 50 | //Act 51 | var result = target.GetAccount(TestHelpers.DevConnectionString); 52 | 53 | //Assert 54 | Assert.IsNotNull(result); 55 | } 56 | 57 | [Test] 58 | public void SuccessWithAccountName() 59 | { 60 | //Arrange 61 | var target = new AzureStorageFactory(); 62 | 63 | //Act 64 | var result = target.GetAccount(TestHelpers.DevAccountName, TestHelpers.DevAccountKey, true); 65 | 66 | //Assert 67 | Assert.IsNotNull(result); 68 | } 69 | } 70 | 71 | [TestFixture] 72 | [Category("Integration")] 73 | public class GetAzureQueue 74 | { 75 | [Test] 76 | public void ValidatesArgs() 77 | { 78 | //Arrange 79 | var target = new AzureStorageFactory(); 80 | 81 | //Act Assert 82 | Assert.That(() => target.GetAzureQueue(null, "foo"), Throws.ArgumentNullException); 83 | Assert.That(() => target.GetAzureQueue(mockAccount, string.Empty), Throws.ArgumentNullException); 84 | } 85 | 86 | [Test] 87 | public void Sucesss() 88 | { 89 | //Arrange 90 | var target = new AzureStorageFactory(); 91 | 92 | //Act 93 | var result = target.GetAzureQueue(mockAccount, "foo"); 94 | 95 | //Assert 96 | Assert.IsNotNull(result); 97 | } 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Azure.Data.Wrappers.Test/Integration/AzureStorageResourcesTests.cs: -------------------------------------------------------------------------------- 1 | namespace Azure.Data.Wrappers.Test.Integration 2 | { 3 | using NUnit.Framework; 4 | using System; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | 8 | [TestFixture] 9 | [Category("Integration")] 10 | public class AzureStorageResourcesTests 11 | { 12 | private readonly string ConnectionString = "UseDevelopmentStorage=true;"; 13 | 14 | [Test] 15 | public async Task TableNames() 16 | { 17 | var name = 'a' + Guid.NewGuid().ToString().ToLowerInvariant().Replace('-', 'a'); 18 | var storage = new TableStorage(name, ConnectionString); 19 | var created = await storage.CreateIfNotExists(); 20 | 21 | var resources = new AzureStorageResources(ConnectionString); 22 | var tables = await resources.TableNames(); 23 | 24 | Assert.IsTrue(tables.Contains(name)); 25 | } 26 | 27 | [Test] 28 | public async Task Tables() 29 | { 30 | var name = 'a' + Guid.NewGuid().ToString().ToLowerInvariant().Replace('-', 'a'); 31 | var storage = new TableStorage(name, ConnectionString); 32 | var created = await storage.CreateIfNotExists(); 33 | 34 | var resources = new AzureStorageResources(ConnectionString); 35 | var tables = await resources.Tables(); 36 | 37 | var exists = (from t in tables 38 | where t.Name == name 39 | select true).FirstOrDefault(); 40 | 41 | Assert.IsTrue(exists); 42 | } 43 | 44 | [Test] 45 | public async Task QueueNames() 46 | { 47 | var name = 'a' + Guid.NewGuid().ToString().ToLowerInvariant().Replace('-', 'a'); 48 | var storage = new StorageQueue(name, ConnectionString); 49 | var created = await storage.CreateIfNotExists(); 50 | 51 | var resources = new AzureStorageResources(ConnectionString); 52 | var queues = await resources.QueueNames(); 53 | 54 | Assert.IsTrue(queues.Contains(name)); 55 | } 56 | 57 | [Test] 58 | public async Task Queues() 59 | { 60 | var name = 'a' + Guid.NewGuid().ToString().ToLowerInvariant().Replace('-', 'a'); 61 | var storage = new StorageQueue(name, ConnectionString); 62 | var created = await storage.CreateIfNotExists(); 63 | 64 | var resources = new AzureStorageResources(ConnectionString); 65 | var queues = await resources.Queues(); 66 | 67 | var exists = (from q in queues 68 | where q.Name == name 69 | select true).FirstOrDefault(); 70 | 71 | Assert.IsTrue(exists); 72 | 73 | await storage.Delete(); 74 | } 75 | 76 | [Test] 77 | public async Task ContainerNames() 78 | { 79 | var name = 'a' + Guid.NewGuid().ToString().ToLowerInvariant().Replace('-', 'a'); 80 | var storage = new Container(name, ConnectionString); 81 | var created = await storage.CreateIfNotExists(); 82 | 83 | var resources = new AzureStorageResources(ConnectionString); 84 | var containers = await resources.ContainerNames(); 85 | 86 | Assert.IsTrue(containers.Contains(name)); 87 | } 88 | 89 | [Test] 90 | public async Task Containers() 91 | { 92 | var name = 'a' + Guid.NewGuid().ToString().ToLowerInvariant().Replace('-', 'a'); 93 | var storage = new Container(name, ConnectionString); 94 | var created = await storage.CreateIfNotExists(); 95 | 96 | var resources = new AzureStorageResources(ConnectionString); 97 | var containers = await resources.Containers(); 98 | 99 | var exists = (from c in containers 100 | where c.Name == name 101 | select true).FirstOrDefault(); 102 | 103 | Assert.IsTrue(exists); 104 | } 105 | } 106 | } -------------------------------------------------------------------------------- /Azure.Data.Wrappers/Azure.Data.Wrappers.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Azure.Data.Wrappers Class Library 5 | Azure.Data.Wrappers 6 | 3.0.0 7 | Jef King,Christopher Scott 8 | netcoreapp1.0;netstandard1.3;net45;net451;net452;net46;net461;net462 9 | Azure.Data.Wrappers 10 | Azure.Data.Wrappers 11 | King.Azure;Azure;Storage;Mock;Mockable;Simple;Data;Table;Storage;File;Share;File-Share;Queue;Queuing;Blob;Query;dependency;injection;dependency-injection;Cloud;Table-Storage;Windows-Azure;Windows;dotNet;CSharp;Mocking;Data-Table;Blob;Json;WindowsAzure.Storage;.NetCore;DNX,storage-account,blobs,binary,text,data,event-store 12 | Implemented IContainer.GetSharedAccessSignature Method including groupPolicyIdentifier 13 | Implemented IContainer.GetSharedAccessSignature Method with groupPolicyIdentifier 14 | Added ConfigureAwait(false) to all awaited methods. https://msdn.microsoft.com/en-us/magazine/jj991977.aspx 15 | https://raw.githubusercontent.com/Microsoft/Azure.Data.Wrappers/master/icon.png 16 | https://github.com/Microsoft/Azure.Data.Wrappers 17 | https://github.com/Microsoft/Azure.Data.Wrappers/blob/master/LICENSE 18 | $(PackageTargetFallback);portable-net451+win8 19 | $(PackageTargetFallback);portable-net451+win8 20 | false 21 | false 22 | false 23 | false 24 | false 25 | false 26 | false 27 | false 28 | True 29 | icon.ico 30 | https://github.com/Microsoft/Azure.Data.Wrappers 31 | Git 32 | Azure.Data.Wrappers 33 | 3.0.10 34 | True 35 | 36 | 37 | 38 | True 39 | 40 | TRACE;RELEASE;NETCOREAPP1_0 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /Azure.Data.Wrappers.Test/Unit/StorageQueuePollerTests.cs: -------------------------------------------------------------------------------- 1 | namespace Azure.Data.Wrappers.Test.Unit 2 | { 3 | using Azure.Data.Wrappers; 4 | using Microsoft.WindowsAzure.Storage.Queue; 5 | using NSubstitute; 6 | using NUnit.Framework; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Linq; 10 | using System.Threading.Tasks; 11 | 12 | [TestFixture] 13 | public class StorageQueuePollerTests 14 | { 15 | const string ConnectionString = "UseDevelopmentStorage=true"; 16 | 17 | [Test] 18 | public void Constructor() 19 | { 20 | new StorageQueuePoller("queue", ConnectionString); 21 | } 22 | 23 | [Test] 24 | public void ConstructorStorageQueueNull() 25 | { 26 | Assert.That(() => new StorageQueuePoller(null), Throws.TypeOf()); 27 | } 28 | 29 | [Test] 30 | public void IsIStorageQueuePoller() 31 | { 32 | Assert.IsNotNull(new StorageQueuePoller("queue", ConnectionString) as IStorageQueuePoller); 33 | } 34 | 35 | [Test] 36 | public void Queue() 37 | { 38 | var queue = Substitute.For(); 39 | var poller = new StorageQueuePoller(queue); 40 | var returned = poller.Queue; 41 | Assert.AreEqual(queue, returned); 42 | } 43 | 44 | [Test] 45 | public async Task Poll() 46 | { 47 | var msg = new CloudQueueMessage("data"); 48 | var queue = Substitute.For(); 49 | queue.Get().Returns(Task.FromResult(msg)); 50 | 51 | var poller = new StorageQueuePoller(queue); 52 | var returned = await poller.Poll(); 53 | 54 | Assert.IsNotNull(returned); 55 | 56 | await queue.Received().Get(); 57 | } 58 | 59 | [Test] 60 | public async Task PollGetNull() 61 | { 62 | var queue = Substitute.For(); 63 | queue.Get().Returns(Task.FromResult(null)); 64 | 65 | var poller = new StorageQueuePoller(queue); 66 | var returned = await poller.Poll(); 67 | 68 | Assert.IsNull(returned); 69 | 70 | await queue.Received().Get(); 71 | } 72 | 73 | [Test] 74 | public void PollGetThrows() 75 | { 76 | var msg = new CloudQueueMessage("data"); 77 | var queue = Substitute.For(); 78 | queue.Get().ReturnsForAnyArgs(x => { throw new ApplicationException(); }); 79 | 80 | var poller = new StorageQueuePoller(queue); 81 | 82 | Assert.That(() => poller.Poll(), Throws.TypeOf()); 83 | } 84 | 85 | [Test] 86 | public async Task PollMany() 87 | { 88 | var msg = new CloudQueueMessage("data"); 89 | var msgs = new List(3); 90 | msgs.Add(msg); 91 | msgs.Add(msg); 92 | msgs.Add(msg); 93 | 94 | var queue = Substitute.For(); 95 | queue.GetMany(3).Returns(Task.FromResult>(msgs)); 96 | 97 | var poller = new StorageQueuePoller(queue); 98 | var returned = await poller.PollMany(3); 99 | 100 | Assert.IsNotNull(returned); 101 | Assert.AreEqual(3, returned.Count()); 102 | 103 | await queue.Received().GetMany(3); 104 | } 105 | 106 | [Test] 107 | public async Task PollGetManyNull() 108 | { 109 | var queue = Substitute.For(); 110 | queue.GetMany(3).Returns(Task.FromResult>(null)); 111 | 112 | var poller = new StorageQueuePoller(queue); 113 | var returned = await poller.PollMany(3); 114 | 115 | Assert.IsNull(returned); 116 | 117 | await queue.Received().GetMany(3); 118 | } 119 | 120 | [Test] 121 | public void PollGetManyThrows() 122 | { 123 | var msg = new CloudQueueMessage("data"); 124 | var queue = Substitute.For(); 125 | queue.GetMany().ReturnsForAnyArgs(x => { throw new ApplicationException(); }); 126 | 127 | var poller = new StorageQueuePoller(queue); 128 | 129 | Assert.That(() => poller.PollMany(), Throws.TypeOf()); 130 | } 131 | } 132 | } -------------------------------------------------------------------------------- /Azure.Data.Wrappers/AzureStorageResources.cs: -------------------------------------------------------------------------------- 1 | namespace Azure.Data.Wrappers 2 | { 3 | using Microsoft.WindowsAzure.Storage.Blob; 4 | using Microsoft.WindowsAzure.Storage.Queue; 5 | using Microsoft.WindowsAzure.Storage.Table; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Threading.Tasks; 9 | 10 | /// 11 | /// Azure Storage Resources 12 | /// 13 | public class AzureStorageResources : AzureStorage, IAzureStorageResources 14 | { 15 | #region Constructors 16 | /// 17 | /// Constructor 18 | /// 19 | /// Storage Account 20 | public AzureStorageResources(string connectionString) 21 | : base(connectionString) 22 | { 23 | } 24 | #endregion 25 | 26 | #region Methods 27 | /// 28 | /// List Table Names 29 | /// 30 | /// Table Names 31 | public virtual async Task> TableNames() 32 | { 33 | TableContinuationToken token = null; 34 | var names = new List(); 35 | 36 | var client = base.Account.CreateCloudTableClient(); 37 | 38 | do 39 | { 40 | var segments = await client.ListTablesSegmentedAsync(token).ConfigureAwait(false); 41 | names.AddRange(segments.Results.Select(s => s.Name)); 42 | token = segments.ContinuationToken; 43 | } 44 | while (null != token); 45 | 46 | return names; 47 | } 48 | 49 | /// 50 | /// List Tables 51 | /// 52 | /// Tables 53 | public virtual async Task> Tables() 54 | { 55 | var tables = new List(); 56 | 57 | var names = await this.TableNames().ConfigureAwait(false); 58 | foreach (var name in names) 59 | { 60 | tables.Add(new TableStorage(name, base.Account)); 61 | } 62 | 63 | return tables; 64 | } 65 | 66 | /// 67 | /// List Container Names 68 | /// 69 | /// Container Names 70 | public virtual async Task> ContainerNames() 71 | { 72 | BlobContinuationToken token = null; 73 | var names = new List(); 74 | 75 | var client = base.Account.CreateCloudBlobClient(); 76 | 77 | do 78 | { 79 | var segments = await client.ListContainersSegmentedAsync(token).ConfigureAwait(false); 80 | names.AddRange(segments.Results.Select(s => s.Name)); 81 | token = segments.ContinuationToken; 82 | } 83 | while (null != token); 84 | 85 | return names; 86 | } 87 | 88 | /// 89 | /// List Containers 90 | /// 91 | /// Containers 92 | public virtual async Task> Containers() 93 | { 94 | var containers = new List(); 95 | var names = await this.ContainerNames().ConfigureAwait(false); 96 | foreach (var name in names) 97 | { 98 | containers.Add(new Container(name, base.Account)); 99 | } 100 | 101 | return containers; 102 | } 103 | 104 | /// 105 | /// List Queue Names 106 | /// 107 | /// Queue Names 108 | public virtual async Task> QueueNames() 109 | { 110 | QueueContinuationToken token = null; 111 | var names = new List(); 112 | 113 | var client = base.Account.CreateCloudQueueClient(); 114 | 115 | do 116 | { 117 | var segments = await client.ListQueuesSegmentedAsync(token).ConfigureAwait(false); 118 | names.AddRange(segments.Results.Select(s => s.Name)); 119 | token = segments.ContinuationToken; 120 | } 121 | while (null != token); 122 | 123 | return names; 124 | } 125 | 126 | /// 127 | /// List Queues 128 | /// 129 | /// Queues 130 | public virtual async Task> Queues() 131 | { 132 | var queues = new List(); 133 | var names = await this.QueueNames().ConfigureAwait(false); 134 | foreach (var name in names) 135 | { 136 | queues.Add(new StorageQueue(name, base.Account)); 137 | } 138 | 139 | return queues; 140 | } 141 | #endregion 142 | } 143 | } -------------------------------------------------------------------------------- /Azure.Data.Wrappers/StorageQueueShards.cs: -------------------------------------------------------------------------------- 1 | namespace Azure.Data.Wrappers 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Collections.ObjectModel; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | 9 | /// 10 | /// Queue Shard Sender 11 | /// 12 | public class StorageQueueShards : IQueueShardSender, IAzureStorage 13 | { 14 | #region Members 15 | /// 16 | /// Queues 17 | /// 18 | protected readonly IEnumerable queues; 19 | 20 | /// 21 | /// Base of the Name 22 | /// 23 | protected readonly string baseName; 24 | #endregion 25 | 26 | #region Constructors 27 | /// 28 | /// Constructor 29 | /// 30 | /// Name 31 | /// Connection 32 | /// Shard Count 33 | public StorageQueueShards(string name, string connection, byte shardCount = 2) 34 | { 35 | if (string.IsNullOrWhiteSpace(name)) 36 | { 37 | throw new ArgumentException("name"); 38 | } 39 | if (string.IsNullOrWhiteSpace(connection)) 40 | { 41 | throw new ArgumentException("connection"); 42 | } 43 | 44 | this.baseName = name; 45 | shardCount = shardCount > 0 ? shardCount : (byte)2; 46 | 47 | var qs = new IStorageQueue[shardCount]; 48 | for (var i = 0; i < shardCount; i++) 49 | { 50 | var n = string.Format("{0}{1}", this.baseName, i); 51 | qs[i] = new StorageQueue(n, connection); 52 | } 53 | 54 | this.queues = new ReadOnlyCollection(qs); 55 | } 56 | 57 | /// 58 | /// Constructor for mocking 59 | /// 60 | /// Queues 61 | public StorageQueueShards(IEnumerable queues) 62 | { 63 | if (null == queues) 64 | { 65 | throw new ArgumentNullException("queue"); 66 | } 67 | if (0 == queues.Count()) 68 | { 69 | throw new ArgumentException("Queues length is 0."); 70 | } 71 | 72 | this.queues = queues; 73 | } 74 | #endregion 75 | 76 | #region Properties 77 | /// 78 | /// Queues 79 | /// 80 | public virtual IReadOnlyCollection Queues 81 | { 82 | get 83 | { 84 | return new ReadOnlyCollection(this.queues.ToList()); 85 | } 86 | } 87 | 88 | /// 89 | /// Name 90 | /// 91 | public string Name 92 | { 93 | get 94 | { 95 | return this.baseName; 96 | } 97 | } 98 | #endregion 99 | 100 | #region Methods 101 | /// 102 | /// Queue Message 103 | /// 104 | /// Message 105 | /// Shard Target 106 | /// Created 107 | public virtual async Task CreateIfNotExists() 108 | { 109 | var success = true; 110 | foreach (var q in this.queues) 111 | { 112 | success &= await q.CreateIfNotExists().ConfigureAwait(false); 113 | } 114 | 115 | return success; 116 | } 117 | 118 | /// 119 | /// Delete all queues 120 | /// 121 | /// Task 122 | public virtual async Task Delete() 123 | { 124 | foreach (var q in this.queues) 125 | { 126 | await q.Delete().ConfigureAwait(false); 127 | } 128 | } 129 | 130 | /// 131 | /// Queue Message to shard, 0 means at random 132 | /// 133 | /// Message 134 | /// Shard Target 135 | /// Task 136 | public virtual async Task Save(object obj, byte shardTarget = 0) 137 | { 138 | var index = this.Index(shardTarget); 139 | var q = this.queues.ElementAt(index); 140 | await q.Send(obj).ConfigureAwait(false); 141 | } 142 | 143 | /// 144 | /// Determine index of queues to interact with 145 | /// 146 | /// 147 | /// Specifically broken out for testing safety 148 | /// 149 | /// Shard Target 150 | /// Index 151 | public virtual byte Index(byte shardTarget) 152 | { 153 | var random = new Random(); 154 | var count = this.queues.Count(); 155 | return shardTarget == 0 || shardTarget > count ? (byte)random.Next(0, count) : shardTarget; 156 | } 157 | #endregion 158 | } 159 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # MSTest test Results 33 | [Tt]est[Rr]esult*/ 34 | [Bb]uild[Ll]og.* 35 | 36 | # NUNIT 37 | *.VisualState.xml 38 | TestResult.xml 39 | 40 | # Build Results of an ATL Project 41 | [Dd]ebugPS/ 42 | [Rr]eleasePS/ 43 | dlldata.c 44 | 45 | # .NET Core 46 | project.lock.json 47 | project.fragment.lock.json 48 | artifacts/ 49 | **/Properties/launchSettings.json 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # Visual Studio code coverage results 117 | *.coverage 118 | *.coveragexml 119 | 120 | # NCrunch 121 | _NCrunch_* 122 | .*crunch*.local.xml 123 | nCrunchTemp_* 124 | 125 | # MightyMoose 126 | *.mm.* 127 | AutoTest.Net/ 128 | 129 | # Web workbench (sass) 130 | .sass-cache/ 131 | 132 | # Installshield output folder 133 | [Ee]xpress/ 134 | 135 | # DocProject is a documentation generator add-in 136 | DocProject/buildhelp/ 137 | DocProject/Help/*.HxT 138 | DocProject/Help/*.HxC 139 | DocProject/Help/*.hhc 140 | DocProject/Help/*.hhk 141 | DocProject/Help/*.hhp 142 | DocProject/Help/Html2 143 | DocProject/Help/html 144 | 145 | # Click-Once directory 146 | publish/ 147 | 148 | # Publish Web Output 149 | *.[Pp]ublish.xml 150 | *.azurePubxml 151 | # TODO: Comment the next line if you want to checkin your web deploy settings 152 | # but database connection strings (with potential passwords) will be unencrypted 153 | *.pubxml 154 | *.publishproj 155 | 156 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 157 | # checkin your Azure Web App publish settings, but sensitive information contained 158 | # in these scripts will be unencrypted 159 | PublishScripts/ 160 | 161 | # NuGet Packages 162 | *.nupkg 163 | # The packages folder can be ignored because of Package Restore 164 | **/packages/* 165 | # except build/, which is used as an MSBuild target. 166 | !**/packages/build/ 167 | # Uncomment if necessary however generally it will be regenerated when needed 168 | #!**/packages/repositories.config 169 | # NuGet v3's project.json files produces more ignorable files 170 | *.nuget.props 171 | *.nuget.targets 172 | 173 | # Microsoft Azure Build Output 174 | csx/ 175 | *.build.csdef 176 | 177 | # Microsoft Azure Emulator 178 | ecf/ 179 | rcf/ 180 | 181 | # Windows Store app package directories and files 182 | AppPackages/ 183 | BundleArtifacts/ 184 | Package.StoreAssociation.xml 185 | _pkginfo.txt 186 | 187 | # Visual Studio cache files 188 | # files ending in .cache can be ignored 189 | *.[Cc]ache 190 | # but keep track of directories ending in .cache 191 | !*.[Cc]ache/ 192 | 193 | # Others 194 | ClientBin/ 195 | ~$* 196 | *~ 197 | *.dbmdl 198 | *.dbproj.schemaview 199 | *.jfm 200 | *.pfx 201 | *.publishsettings 202 | orleans.codegen.cs 203 | 204 | # Since there are multiple workflows, uncomment next line to ignore bower_components 205 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 206 | #bower_components/ 207 | 208 | # RIA/Silverlight projects 209 | Generated_Code/ 210 | 211 | # Backup & report files from converting an old project file 212 | # to a newer Visual Studio version. Backup files are not needed, 213 | # because we have git ;-) 214 | _UpgradeReport_Files/ 215 | Backup*/ 216 | UpgradeLog*.XML 217 | UpgradeLog*.htm 218 | 219 | # SQL Server files 220 | *.mdf 221 | *.ldf 222 | *.ndf 223 | 224 | # Business Intelligence projects 225 | *.rdl.data 226 | *.bim.layout 227 | *.bim_*.settings 228 | 229 | # Microsoft Fakes 230 | FakesAssemblies/ 231 | 232 | # GhostDoc plugin setting file 233 | *.GhostDoc.xml 234 | 235 | # Node.js Tools for Visual Studio 236 | .ntvs_analysis.dat 237 | node_modules/ 238 | 239 | # Typescript v1 declaration files 240 | typings/ 241 | 242 | # Visual Studio 6 build log 243 | *.plg 244 | 245 | # Visual Studio 6 workspace options file 246 | *.opt 247 | 248 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 249 | *.vbw 250 | 251 | # Visual Studio LightSwitch build output 252 | **/*.HTMLClient/GeneratedArtifacts 253 | **/*.DesktopClient/GeneratedArtifacts 254 | **/*.DesktopClient/ModelManifest.xml 255 | **/*.Server/GeneratedArtifacts 256 | **/*.Server/ModelManifest.xml 257 | _Pvt_Extensions 258 | 259 | # Paket dependency manager 260 | .paket/paket.exe 261 | paket-files/ 262 | 263 | # FAKE - F# Make 264 | .fake/ 265 | 266 | # JetBrains Rider 267 | .idea/ 268 | *.sln.iml 269 | 270 | # CodeRush 271 | .cr/ 272 | 273 | # Python Tools for Visual Studio (PTVS) 274 | __pycache__/ 275 | *.pyc 276 | 277 | # Cake - Uncomment if you are using it 278 | # tools/** 279 | # !tools/packages.config 280 | 281 | # Telerik's JustMock configuration file 282 | *.jmconfig 283 | 284 | # BizTalk build output 285 | *.btp.cs 286 | *.btm.cs 287 | *.odx.cs 288 | *.xsd.cs 289 | *.ncrunchsolution 290 | -------------------------------------------------------------------------------- /Azure.Data.Wrappers.Test/Unit/StorageQueueShardsTests.cs: -------------------------------------------------------------------------------- 1 | namespace Azure.Data.Wrappers.Test.Unit 2 | { 3 | using Azure.Data.Wrappers; 4 | using NSubstitute; 5 | using NUnit.Framework; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using System.Threading.Tasks; 10 | 11 | [TestFixture] 12 | public class StorageQueueShardsTests 13 | { 14 | private const string ConnectionString = "UseDevelopmentStorage=true;"; 15 | 16 | [Test] 17 | public void Constructor() 18 | { 19 | var sqs = new StorageQueueShards("test", ConnectionString, 2); 20 | Assert.AreEqual(2, sqs.Queues.Count()); 21 | } 22 | 23 | [Test] 24 | public void ConstructorConnectionNull() 25 | { 26 | Assert.That(() => new StorageQueueShards("test", null), Throws.TypeOf()); 27 | } 28 | 29 | [Test] 30 | public void ConstructorNameNull() 31 | { 32 | Assert.That(() => new StorageQueueShards(null, ConnectionString), Throws.TypeOf()); 33 | } 34 | 35 | [Test] 36 | public void ConstructorQueuesNull() 37 | { 38 | Assert.That(() => new StorageQueueShards(null), Throws.TypeOf()); 39 | } 40 | 41 | [Test] 42 | public void ConstructorQueuesEmpty() 43 | { 44 | Assert.That(() => new StorageQueueShards(new IStorageQueue[0]), Throws.TypeOf()); 45 | } 46 | 47 | [Test] 48 | public void ConstructorShardDefault() 49 | { 50 | var sqs = new StorageQueueShards("test", ConnectionString); 51 | Assert.AreEqual(2, sqs.Queues.Count()); 52 | } 53 | 54 | [Test] 55 | public void IsIQueueShardSender() 56 | { 57 | Assert.IsNotNull(new StorageQueueShards("test", ConnectionString) as IQueueShardSender); 58 | } 59 | 60 | [Test] 61 | public void IsIAzureStorage() 62 | { 63 | Assert.IsNotNull(new StorageQueueShards("test", ConnectionString) as IAzureStorage); 64 | } 65 | 66 | [Test] 67 | public void Name() 68 | { 69 | var name = Guid.NewGuid().ToString(); 70 | var sqs = new StorageQueueShards(name, ConnectionString, 2); 71 | Assert.AreEqual(name, sqs.Name); 72 | } 73 | 74 | [Test] 75 | public void Queues() 76 | { 77 | var random = new Random(); 78 | var i = (byte)random.Next(1, byte.MaxValue); 79 | var sqs = new StorageQueueShards("test", ConnectionString, i); 80 | Assert.IsNotNull(sqs.Queues); 81 | Assert.AreEqual(i, sqs.Queues.Count()); 82 | } 83 | 84 | [Test] 85 | public async Task CreateIfNotExists() 86 | { 87 | var random = new Random(); 88 | var i = random.Next(1, byte.MaxValue); 89 | var qs = new List(); 90 | for (var j = 0; j < i; j++) 91 | { 92 | var q = Substitute.For(); 93 | q.CreateIfNotExists().Returns(Task.FromResult(true)); 94 | qs.Add(q); 95 | } 96 | var sqs = new StorageQueueShards(qs.ToArray()); 97 | 98 | var success = await sqs.CreateIfNotExists(); 99 | Assert.IsTrue(success); 100 | 101 | foreach (var q in qs) 102 | { 103 | await q.Received().CreateIfNotExists(); 104 | } 105 | } 106 | 107 | [Test] 108 | public async Task Delete() 109 | { 110 | var random = new Random(); 111 | var i = random.Next(1, byte.MaxValue); 112 | var qs = new List(); 113 | for (var j = 0; j < i; j++) 114 | { 115 | var q = Substitute.For(); 116 | q.Delete().Returns(Task.FromResult(true)); 117 | qs.Add(q); 118 | } 119 | var sqs = new StorageQueueShards(qs.ToArray()); 120 | 121 | await sqs.Delete(); 122 | 123 | foreach (var q in qs) 124 | { 125 | await q.Received().Delete(); 126 | } 127 | } 128 | 129 | [Test] 130 | public async Task Save() 131 | { 132 | var random = new Random(); 133 | var i = (byte)random.Next(1, byte.MaxValue); 134 | var index = random.Next(0, i); 135 | 136 | var msg = new object(); 137 | var qs = new List(); 138 | 139 | for (var j = 0; j < i; j++) 140 | { 141 | var q = Substitute.For(); 142 | q.Send(msg).Returns(Task.CompletedTask); 143 | qs.Add(q); 144 | } 145 | 146 | var sqs = new StorageQueueShards(qs); 147 | 148 | await sqs.Save(msg, (byte)index); 149 | 150 | for (var j = 0; j < i; j++) 151 | { 152 | if (j == index) 153 | { 154 | await qs[j].Received().Send(msg); 155 | } 156 | else 157 | { 158 | await qs[j].DidNotReceive().Send(msg); 159 | } 160 | } 161 | } 162 | 163 | [Test] 164 | public void Index() 165 | { 166 | var msg = new object(); 167 | var q = Substitute.For(); 168 | 169 | var qs = new List(); 170 | qs.Add(q); 171 | qs.Add(q); 172 | qs.Add(q); 173 | 174 | var sqs = new StorageQueueShards(qs); 175 | 176 | var index = sqs.Index(0); 177 | 178 | Assert.IsTrue(0 <= index && 3 > index); 179 | } 180 | 181 | [Test] 182 | public void IndexBad() 183 | { 184 | var val = new int[] { 0, 255 }; 185 | var expected = new int[] { 0, 0 }; 186 | for (int i = 0; i < val.Length; i++) 187 | { 188 | for (int ii = 0; ii < expected.Length; ii++) 189 | { 190 | var msg = new object(); 191 | var q = Substitute.For(); 192 | 193 | var qs = new List(); 194 | qs.Add(q); 195 | 196 | var sqs = new StorageQueueShards(qs); 197 | 198 | var index = sqs.Index((byte)val[i]); 199 | 200 | Assert.AreEqual(expected[ii], index); 201 | } 202 | } 203 | 204 | } 205 | } 206 | } -------------------------------------------------------------------------------- /Azure.Data.Wrappers.Test/Unit/ContainerTests.cs: -------------------------------------------------------------------------------- 1 | namespace Azure.Data.Wrappers.Test.Unit 2 | { 3 | using Azure.Data.Wrappers; 4 | using Microsoft.WindowsAzure.Storage; 5 | using NUnit.Framework; 6 | using System; 7 | 8 | [TestFixture] 9 | public class ContainerTests 10 | { 11 | private const string ConnectionString = "UseDevelopmentStorage=true;"; 12 | 13 | [Test] 14 | public void Constructor() 15 | { 16 | new Container("test", ConnectionString); 17 | } 18 | 19 | [Test] 20 | public void IsIContainer() 21 | { 22 | Assert.IsNotNull(new Container("test", ConnectionString) as IContainer); 23 | } 24 | 25 | [Test] 26 | public void IsAzureStorage() 27 | { 28 | Assert.IsNotNull(new Container("test", ConnectionString) as AzureStorage); 29 | } 30 | 31 | [Test] 32 | public void ConstructorNameNull() 33 | { 34 | Assert.That(() => new Container(null, ConnectionString), Throws.TypeOf()); 35 | } 36 | 37 | [Test] 38 | public void ConstructorAccountNameNull() 39 | { 40 | Assert.That(() => new Container(null, CloudStorageAccount.Parse(ConnectionString)), Throws.TypeOf()); 41 | } 42 | 43 | [Test] 44 | public void ConstructorKeyNull() 45 | { 46 | Assert.That(() => new Container("test", (string)null), Throws.TypeOf()); 47 | } 48 | 49 | [Test] 50 | public void DefaultCacheDuration() 51 | { 52 | Assert.AreEqual(31536000, Container.DefaultCacheDuration); 53 | } 54 | 55 | [Test] 56 | public void Name() 57 | { 58 | var name = Guid.NewGuid().ToString(); 59 | var t = new Container(name, ConnectionString); 60 | Assert.AreEqual(name, t.Name); 61 | } 62 | 63 | [Test] 64 | public void IsPublic() 65 | { 66 | var name = Guid.NewGuid().ToString(); 67 | var t = new Container(name, ConnectionString, true); 68 | Assert.IsTrue(t.IsPublic); 69 | } 70 | 71 | [Test] 72 | public void Client() 73 | { 74 | var name = Guid.NewGuid().ToString(); 75 | var t = new Container(name, ConnectionString); 76 | Assert.IsNotNull(t.Client); 77 | } 78 | 79 | [Test] 80 | public void Reference() 81 | { 82 | var name = Guid.NewGuid().ToString(); 83 | var t = new Container(name, ConnectionString); 84 | Assert.IsNotNull(t.Reference); 85 | } 86 | 87 | [Test] 88 | public void DeleteBlobNameNull() 89 | { 90 | var c = new Container("test", ConnectionString); 91 | Assert.That(() => c.Delete(null), Throws.TypeOf()); 92 | } 93 | 94 | [Test] 95 | public void ExistsBlobNameNull() 96 | { 97 | var c = new Container("test", ConnectionString); 98 | Assert.That(() => c.Exists(null), Throws.TypeOf()); 99 | } 100 | 101 | [Test] 102 | public void GetBlobNameNull() 103 | { 104 | var c = new Container("test", ConnectionString); 105 | 106 | Assert.That(() => c.Get(null), Throws.TypeOf()); 107 | } 108 | 109 | [Test] 110 | public void StreamBlobNameNull() 111 | { 112 | var c = new Container("test", ConnectionString); 113 | 114 | Assert.That(() => c.Stream(null), Throws.TypeOf()); 115 | } 116 | 117 | [Test] 118 | public void SaveBlobNameNull() 119 | { 120 | var c = new Container("test", ConnectionString); 121 | 122 | Assert.That(() => c.Save(null, new object()), Throws.TypeOf()); 123 | } 124 | 125 | [Test] 126 | public void SaveObjectNull() 127 | { 128 | var c = new Container("test", ConnectionString); 129 | 130 | Assert.That(() => c.Save(Guid.NewGuid().ToString(), (object)null), Throws.TypeOf()); 131 | } 132 | 133 | [Test] 134 | public void GetBytesBlobNameNull() 135 | { 136 | var c = new Container("test", ConnectionString); 137 | 138 | Assert.That(() => c.Get(null), Throws.TypeOf()); 139 | } 140 | 141 | [Test] 142 | public void GetTextBlobNameNull() 143 | { 144 | var c = new Container("test", ConnectionString); 145 | 146 | Assert.That(() => c.GetText(null), Throws.TypeOf()); 147 | } 148 | 149 | [Test] 150 | public void SnapShotBlobNameNull() 151 | { 152 | var c = new Container("test", ConnectionString); 153 | 154 | Assert.That(() => c.Snapshot(null), Throws.TypeOf()); 155 | } 156 | 157 | [Test] 158 | public void SaveBytesBlobNameNull() 159 | { 160 | var random = new Random(); 161 | var bytes = new byte[1024]; 162 | random.NextBytes(bytes); 163 | 164 | var c = new Container("test", ConnectionString); 165 | 166 | Assert.That(() => c.Save(null, bytes), Throws.TypeOf()); 167 | } 168 | 169 | [Test] 170 | public void SaveTextBlobNameNull() 171 | { 172 | var c = new Container("test", ConnectionString); 173 | 174 | Assert.That(() => c.Save(null, Guid.NewGuid().ToString()), Throws.TypeOf()); 175 | } 176 | 177 | [Test] 178 | public void SaveBytesNull() 179 | { 180 | var c = new Container("test", ConnectionString); 181 | 182 | Assert.That(() => c.Save(Guid.NewGuid().ToString(), (byte[])null), Throws.TypeOf()); 183 | } 184 | 185 | [Test] 186 | public void SaveTextNull() 187 | { 188 | var c = new Container("test", ConnectionString); 189 | 190 | Assert.That(() => c.Save(Guid.NewGuid().ToString(), (string)null), Throws.TypeOf()); 191 | } 192 | 193 | [Test] 194 | public void GetBlockReferenceBlobNameNull() 195 | { 196 | var c = new Container("test", ConnectionString); 197 | 198 | Assert.That(() => c.GetBlockReference(null), Throws.TypeOf()); 199 | } 200 | 201 | [Test] 202 | public void GetPageReferenceBlobNameNull() 203 | { 204 | var c = new Container("test", ConnectionString); 205 | 206 | Assert.That(() => c.GetPageReference(null), Throws.TypeOf()); 207 | } 208 | 209 | [Test] 210 | public void PropertiesBlobNameNull() 211 | { 212 | var c = new Container("test", ConnectionString); 213 | 214 | Assert.That(() => c.Properties(null), Throws.TypeOf()); 215 | } 216 | 217 | [Test] 218 | public void SetCacheControlBlobNameNull() 219 | { 220 | var c = new Container("test", ConnectionString); 221 | 222 | Assert.That(() => c.SetCacheControl(null), Throws.TypeOf()); 223 | } 224 | 225 | [Test] 226 | public void CopyFromToFromNull() 227 | { 228 | var c = new Container("test", ConnectionString); 229 | 230 | Assert.That(() => c.Copy(null, Guid.NewGuid().ToString()), Throws.TypeOf()); 231 | } 232 | 233 | [Test] 234 | public void CopyFromToToNull() 235 | { 236 | var c = new Container("test", ConnectionString); 237 | 238 | Assert.That(() => c.Copy(Guid.NewGuid().ToString(), null), Throws.TypeOf()); 239 | } 240 | 241 | [Test] 242 | public void CopyFromNull() 243 | { 244 | var c = new Container("test", ConnectionString); 245 | 246 | Assert.That(() => c.Copy(null, c, Guid.NewGuid().ToString()), Throws.TypeOf()); 247 | } 248 | 249 | [Test] 250 | public void CopyToNull() 251 | { 252 | var c = new Container("test", ConnectionString); 253 | 254 | Assert.That(() => c.Copy(Guid.NewGuid().ToString(), c, null), Throws.TypeOf()); 255 | } 256 | 257 | [Test] 258 | public void CopyTargetNull() 259 | { 260 | var c = new Container("test", ConnectionString); 261 | 262 | Assert.That(() => c.Copy(Guid.NewGuid().ToString(), (IContainer)null, Guid.NewGuid().ToString()), Throws.TypeOf()); 263 | } 264 | 265 | [Test] 266 | public void GetSharedAccessSignatureThrowsOnNullPolicy() 267 | { 268 | var target = new Container("test", ConnectionString); 269 | 270 | Assert.That(() => target.GetSharedAccessSignature(default(SharedAccessAccountPolicy)), Throws.TypeOf()); 271 | } 272 | } 273 | } -------------------------------------------------------------------------------- /Azure.Data.Wrappers/StorageQueue.cs: -------------------------------------------------------------------------------- 1 | namespace Azure.Data.Wrappers 2 | { 3 | using Microsoft.WindowsAzure.Storage; 4 | using Microsoft.WindowsAzure.Storage.Queue; 5 | using Newtonsoft.Json; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Threading.Tasks; 9 | using System.Linq; 10 | 11 | /// 12 | /// Storage Queue 13 | /// 14 | public class StorageQueue : AzureStorage, IStorageQueue 15 | { 16 | #region Members 17 | /// 18 | /// Cloud Queue Client 19 | /// 20 | private readonly CloudQueueClient client; 21 | 22 | /// 23 | /// Cloud Reference 24 | /// 25 | private readonly CloudQueue reference; 26 | 27 | /// 28 | /// Visibility Timeout 29 | /// 30 | protected readonly TimeSpan? visibilityTimeout = null; 31 | #endregion 32 | 33 | #region Constructors 34 | /// 35 | /// Constructor 36 | /// 37 | /// Name 38 | /// Connection String 39 | public StorageQueue(string name, string connectionString, TimeSpan? visibilityTimeout = null) 40 | : base(connectionString) 41 | { 42 | if (string.IsNullOrWhiteSpace(name)) 43 | { 44 | throw new ArgumentException("name"); 45 | } 46 | 47 | this.client = base.Account.CreateCloudQueueClient(); 48 | this.reference = client.GetQueueReference(name); 49 | this.visibilityTimeout = visibilityTimeout; 50 | } 51 | 52 | /// 53 | /// Constructor 54 | /// 55 | /// Name 56 | /// Storage Account 57 | public StorageQueue(string name, CloudStorageAccount account, TimeSpan? visibilityTimeout = null) 58 | : base(account) 59 | { 60 | if (string.IsNullOrWhiteSpace(name)) 61 | { 62 | throw new ArgumentException("name"); 63 | } 64 | 65 | this.client = base.Account.CreateCloudQueueClient(); 66 | this.reference = client.GetQueueReference(name); 67 | this.visibilityTimeout = visibilityTimeout; 68 | } 69 | #endregion 70 | 71 | #region Properties 72 | /// 73 | /// Table Name 74 | /// 75 | public virtual string Name 76 | { 77 | get 78 | { 79 | return this.reference.Name; 80 | } 81 | } 82 | 83 | /// 84 | /// Cloud Queue Client 85 | /// 86 | public virtual CloudQueueClient Client 87 | { 88 | get 89 | { 90 | return this.client; 91 | } 92 | } 93 | 94 | /// 95 | /// Cloud Reference 96 | /// 97 | public virtual CloudQueue Reference 98 | { 99 | get 100 | { 101 | return this.reference; 102 | } 103 | } 104 | #endregion 105 | 106 | #region Methods 107 | /// 108 | /// Create If Not Exists 109 | /// 110 | /// Created 111 | public virtual async Task CreateIfNotExists() 112 | { 113 | return await this.reference.CreateIfNotExistsAsync().ConfigureAwait(false); 114 | } 115 | 116 | /// 117 | /// Delete Queue 118 | /// 119 | /// Task 120 | public virtual async Task Delete() 121 | { 122 | await this.reference.DeleteAsync().ConfigureAwait(false); 123 | } 124 | 125 | /// 126 | /// Get Cloud Queue Message 127 | /// 128 | /// Message 129 | public virtual async Task Get() 130 | { 131 | return await this.reference.GetMessageAsync(this.visibilityTimeout, null, null).ConfigureAwait(false); 132 | } 133 | 134 | /// 135 | /// Approixmate Message Count 136 | /// 137 | /// Message Count 138 | public virtual async Task ApproixmateMessageCount() 139 | { 140 | await this.reference.FetchAttributesAsync().ConfigureAwait(false); 141 | return this.reference.ApproximateMessageCount; 142 | } 143 | 144 | /// 145 | /// Get Many Cloud Queue Message 146 | /// 147 | /// Message Count 148 | /// Messages 149 | public virtual async Task> GetMany(int messageCount = 5) 150 | { 151 | if (0 >= messageCount) 152 | { 153 | messageCount = 1; 154 | } 155 | 156 | return await this.reference.GetMessagesAsync(messageCount, this.visibilityTimeout, null, null).ConfigureAwait(false); 157 | } 158 | 159 | /// 160 | /// Get Cloud Queue Message 161 | /// 162 | /// Message 163 | public virtual async Task GetAsync() 164 | { 165 | var returned = await this.reference.GetMessageAsync(this.visibilityTimeout, null, null).ConfigureAwait(false); 166 | return JsonConvert.DeserializeObject(returned.AsString); 167 | } 168 | 169 | /// 170 | /// Get Cloud Queue Message(s) 171 | /// 172 | /// 173 | /// number of messages to get. Default is 5 174 | /// Message 175 | public async Task> GetManyAsync(int messageCount = 5) 176 | { 177 | if (0 >= messageCount) 178 | { 179 | messageCount = 1; 180 | } 181 | 182 | var returned = await this.reference.GetMessagesAsync(messageCount, this.visibilityTimeout, null, null).ConfigureAwait(false); 183 | return returned.Select(m => JsonConvert.DeserializeObject(m.AsString)); 184 | } 185 | 186 | /// 187 | /// Save Message to Queue 188 | /// 189 | /// Message 190 | /// Task 191 | public virtual async Task Send(CloudQueueMessage message) 192 | { 193 | if (null == message) 194 | { 195 | throw new ArgumentNullException(nameof(message)); 196 | } 197 | 198 | await this.reference.AddMessageAsync(message).ConfigureAwait(false); 199 | } 200 | 201 | /// 202 | /// Save Model to queue, as json 203 | /// 204 | /// object 205 | /// Task 206 | public virtual async Task Send(object obj) 207 | { 208 | await this.SendAsync(obj).ConfigureAwait(false); 209 | } 210 | 211 | /// 212 | /// Save Model to queue, as json 213 | /// 214 | /// object 215 | /// Task 216 | public virtual async Task SendAsync(T message) 217 | { 218 | if (null == message) 219 | { 220 | throw new ArgumentNullException(nameof(message)); 221 | } 222 | 223 | if (message is CloudQueueMessage) 224 | { 225 | await this.Send(message as CloudQueueMessage).ConfigureAwait(false); 226 | } 227 | else 228 | { 229 | await this.Send(new CloudQueueMessage(JsonConvert.SerializeObject(message))).ConfigureAwait(false); 230 | } 231 | 232 | } 233 | 234 | /// 235 | /// Delete Message from Queue 236 | /// 237 | /// Message 238 | /// Task 239 | public virtual async Task Delete(CloudQueueMessage message) 240 | { 241 | if (null == message) 242 | { 243 | throw new ArgumentNullException("message"); 244 | } 245 | 246 | await this.reference.DeleteMessageAsync(message).ConfigureAwait(false); 247 | } 248 | 249 | /// 250 | /// Clears the contents of the queue 251 | /// 252 | /// 253 | public virtual async Task ClearAsync() 254 | { 255 | await this.reference.ClearAsync().ConfigureAwait(false); 256 | } 257 | 258 | /// 259 | /// Peeks the queue 260 | /// 261 | /// number of messages to peek with a default value of 1 262 | /// Messages 263 | public virtual async Task> PeekAsync(int count = 1) 264 | { 265 | return await this.reference.PeekMessagesAsync(count).ConfigureAwait(false); 266 | } 267 | 268 | /// 269 | /// Peeks the queue 270 | /// 271 | /// number of messages to peek with a default value of 1 272 | /// Messages 273 | public virtual async Task> PeekAsync(int count = 1) 274 | { 275 | var returned = await this.reference.PeekMessagesAsync(count).ConfigureAwait(false); 276 | return returned.Select(m => JsonConvert.DeserializeObject(m.AsString)); 277 | } 278 | #endregion 279 | } 280 | } -------------------------------------------------------------------------------- /Azure.Data.Wrappers.Test/Integration/QueueTests.cs: -------------------------------------------------------------------------------- 1 | namespace Azure.Data.Wrappers.Test.Integration 2 | { 3 | using Azure.Data.Wrappers; 4 | using Microsoft.WindowsAzure.Storage; 5 | using Microsoft.WindowsAzure.Storage.Queue; 6 | using Newtonsoft.Json; 7 | using NUnit.Framework; 8 | using System; 9 | using System.Linq; 10 | using System.Threading.Tasks; 11 | using System.Collections.Generic; 12 | 13 | [TestFixture] 14 | [Category("Integration")] 15 | public class QueueTests 16 | { 17 | private class TestCustomObject 18 | { 19 | public string FooString { get; set; } 20 | public int FooInt { get; set; } 21 | } 22 | private string GetQueueName() 23 | { 24 | return 'a' + Guid.NewGuid().ToString().ToLowerInvariant().Replace('-', 'a'); 25 | } 26 | 27 | private async Task QueueSetup() 28 | { 29 | var storage = new StorageQueue(GetQueueName(), TestHelpers.DevConnectionString); 30 | await storage.CreateIfNotExists(); 31 | return storage; 32 | } 33 | 34 | [Test] 35 | public async Task CreateIfNotExists() 36 | { 37 | var name = GetQueueName(); 38 | var storage = new StorageQueue(name, TestHelpers.DevConnectionString); 39 | var created = await storage.CreateIfNotExists(); 40 | 41 | Assert.IsTrue(created); 42 | await storage.Delete(); 43 | } 44 | 45 | [Test] 46 | public async Task ConstructorAccount() 47 | { 48 | var name = GetQueueName(); 49 | var account = CloudStorageAccount.Parse(TestHelpers.DevConnectionString); 50 | var storage = new StorageQueue(name, account, TimeSpan.FromSeconds(34)); 51 | var created = await storage.CreateIfNotExists(); 52 | 53 | Assert.IsTrue(created); 54 | await storage.Delete(); 55 | } 56 | 57 | [Test] 58 | public async Task RoundTrip() 59 | { 60 | StorageQueue storage = await QueueSetup(); 61 | var msg = new CloudQueueMessage(Guid.NewGuid().ToByteArray()); 62 | await storage.Send(msg); 63 | var returned = await storage.Get(); 64 | 65 | Assert.AreEqual(msg.AsBytes, returned.AsBytes); 66 | await storage.Delete(); 67 | } 68 | 69 | [Test] 70 | public async Task RoundTripMsgAsObj() 71 | { 72 | StorageQueue storage = await QueueSetup(); 73 | 74 | var msg = new CloudQueueMessage(Guid.NewGuid().ToByteArray()); 75 | await storage.Send((object)msg); 76 | var returned = await storage.Get(); 77 | 78 | Assert.AreEqual(msg.AsBytes, returned.AsBytes); 79 | await storage.Delete(); 80 | } 81 | 82 | [Test] 83 | public async Task RoundTripObject() 84 | { 85 | StorageQueue storage = await QueueSetup(); 86 | var expected = new TestCustomObject { FooInt = 42, FooString = "The Answer" }; 87 | await storage.SendAsync(expected); 88 | 89 | var returned = await storage.Get(); 90 | 91 | var deserialized = JsonConvert.DeserializeObject(returned.AsString); 92 | 93 | Assert.AreEqual(expected.FooString, deserialized.FooString); 94 | Assert.AreEqual(expected.FooInt, deserialized.FooInt ); 95 | await storage.Delete(); 96 | } 97 | 98 | [Test] 99 | public async Task GenericGet() 100 | { 101 | StorageQueue storage = await QueueSetup(); 102 | var expected = new TestCustomObject { FooInt = 42, FooString = "The Answer" }; 103 | await storage.SendAsync(expected); 104 | 105 | var returned = await storage.GetAsync(); 106 | 107 | Assert.AreEqual(expected.FooString, returned.FooString); 108 | Assert.AreEqual(expected.FooInt, returned.FooInt); 109 | await storage.Delete(); 110 | } 111 | 112 | [Test] 113 | public async Task GenericGetMany() 114 | { 115 | StorageQueue storage = await QueueSetup(); 116 | var expected = new TestCustomObject { FooInt = 42, FooString = "The Answer" }; 117 | var expected2 = new TestCustomObject { FooInt = 43, FooString = "The Answer 2" }; 118 | var expectedList = new List { expected, expected2 }; 119 | 120 | foreach (var item in expectedList) 121 | { 122 | await storage.SendAsync(item); 123 | } 124 | 125 | var returned = (await storage.GetManyAsync()).ToList(); 126 | 127 | Assert.AreEqual(expectedList.Count, returned.Count); 128 | Assert.IsTrue(returned.Any(m => m.FooInt == expected.FooInt && m.FooString == expected.FooString)); 129 | Assert.IsTrue(returned.Any(m => m.FooInt == expected2.FooInt && m.FooString == expected2.FooString)); 130 | await storage.Delete(); 131 | } 132 | 133 | [Test] 134 | public async Task ApproixmateMessageCount() 135 | { 136 | var random = new Random(); 137 | var count = random.Next(1, 1000); 138 | StorageQueue storage = await QueueSetup(); 139 | for (var i = 0; i < count; i++) 140 | { 141 | await storage.SendAsync(Guid.NewGuid()); 142 | } 143 | 144 | var result = await storage.ApproixmateMessageCount(); 145 | Assert.AreEqual(count, result); 146 | await storage.Delete(); 147 | } 148 | 149 | [Test] 150 | public async Task ApproixmateMessageCountNone() 151 | { 152 | StorageQueue storage = await QueueSetup(); 153 | var result = await storage.ApproixmateMessageCount(); 154 | Assert.AreEqual(0, result); 155 | await storage.Delete(); 156 | } 157 | 158 | [Test] 159 | public async Task Delete() 160 | { 161 | StorageQueue storage = await QueueSetup(); 162 | 163 | var msg = new CloudQueueMessage(Guid.NewGuid().ToByteArray()); 164 | await storage.Send(msg); 165 | var returned = await storage.Get(); 166 | await storage.Delete(returned); 167 | await storage.Delete(); 168 | } 169 | 170 | [Test] 171 | public async Task RoundTripMany() 172 | { 173 | var random = new Random(); 174 | var count = random.Next(1, 25); 175 | 176 | StorageQueue storage = await QueueSetup(); 177 | 178 | for (var i = 0; i < count; i++) 179 | { 180 | var msg = new CloudQueueMessage(Guid.NewGuid().ToByteArray()); 181 | await storage.Send(msg); 182 | } 183 | 184 | var returned = await storage.GetMany(count); 185 | 186 | Assert.AreEqual(count, returned.Count()); 187 | await storage.Delete(); 188 | } 189 | 190 | [Test] 191 | public async Task GetManyNegative() 192 | { 193 | var random = new Random(); 194 | var count = random.Next(1, 25); 195 | 196 | StorageQueue storage = await QueueSetup(); 197 | 198 | for (var i = 0; i < count; i++) 199 | { 200 | var msg = new CloudQueueMessage(Guid.NewGuid().ToByteArray()); 201 | await storage.Send(msg); 202 | } 203 | 204 | var returned = await storage.GetMany(-1); 205 | 206 | Assert.AreEqual(1, returned.Count()); 207 | await storage.Delete(); 208 | } 209 | 210 | [Test] 211 | public async Task Peek() 212 | { 213 | StorageQueue storage = await QueueSetup(); 214 | var msg = new CloudQueueMessage(Guid.NewGuid().ToByteArray()); 215 | await storage.Send(msg); 216 | 217 | var peeked = await storage.PeekAsync(); 218 | Assert.AreEqual(msg.AsBytes, peeked.First().AsBytes); 219 | 220 | var returned = await storage.Get(); 221 | Assert.AreEqual(msg.AsBytes, returned.AsBytes); 222 | await storage.Delete(); 223 | } 224 | 225 | [Test] 226 | public async Task PeekGeneric() 227 | { 228 | StorageQueue storage = await QueueSetup(); 229 | var expected = new TestCustomObject { FooInt = 42, FooString = "The Answer" }; 230 | var expected2 = new TestCustomObject { FooInt = 43, FooString = "The Answer 2" }; 231 | var expectedList = new List { expected, expected2 }; 232 | 233 | foreach (var item in expectedList) 234 | { 235 | await storage.SendAsync(item); 236 | } 237 | 238 | var peeked = (await storage.PeekAsync(2)).ToList(); 239 | Assert.AreEqual(expectedList.Count, peeked.Count); 240 | Assert.IsTrue(expectedList.Any(m => m.FooInt == expected.FooInt && m.FooString == expected.FooString)); 241 | Assert.IsTrue(expectedList.Any(m => m.FooInt == expected2.FooInt && m.FooString == expected2.FooString)); 242 | 243 | var returned = (await storage.GetManyAsync()).ToList(); 244 | Assert.AreEqual(expectedList.Count, returned.Count); 245 | Assert.IsTrue(returned.Any(m => m.FooInt == expected.FooInt && m.FooString == expected.FooString)); 246 | Assert.IsTrue(returned.Any(m => m.FooInt == expected2.FooInt && m.FooString == expected2.FooString)); 247 | await storage.Delete(); 248 | } 249 | 250 | [Test] 251 | public async Task ClearAsync() 252 | { 253 | StorageQueue storage = await QueueSetup(); 254 | var msg = new CloudQueueMessage(Guid.NewGuid().ToByteArray()); 255 | await storage.Send(msg); 256 | 257 | await storage.ClearAsync(); 258 | 259 | var returned = await storage.Get(); 260 | Assert.IsNull(returned); 261 | await storage.Delete(); 262 | } 263 | } 264 | } -------------------------------------------------------------------------------- /Azure.Data.Wrappers.Test/Unit/TableStorageTests.cs: -------------------------------------------------------------------------------- 1 | namespace Azure.Data.Wrappers.Test.Unit 2 | { 3 | using Azure.Data.Wrappers; 4 | using Microsoft.WindowsAzure.Storage; 5 | using Microsoft.WindowsAzure.Storage.Table; 6 | using NUnit.Framework; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Linq; 10 | 11 | [TestFixture] 12 | public class TableStorageTests 13 | { 14 | private const string ConnectionString = "UseDevelopmentStorage=true;"; 15 | 16 | [Test] 17 | public void Constructor() 18 | { 19 | new TableStorage("TestTable", ConnectionString); 20 | } 21 | [Test] 22 | public void ConstructorWithOptions() 23 | { 24 | new TableStorage("TestTable", ConnectionString, new TableRequestOptions()); 25 | } 26 | [Test] 27 | public void ConstructorWithOptionsNull() 28 | { 29 | Assert.That(() => new TableStorage("TestTable", ConnectionString, null), Throws.TypeOf()); 30 | } 31 | [Test] 32 | public void IsITableStorage() 33 | { 34 | Assert.IsNotNull(new TableStorage("TestTable", ConnectionString) as ITableStorage); 35 | } 36 | 37 | [Test] 38 | public void PartitionKey() 39 | { 40 | Assert.AreEqual("PartitionKey", TableStorage.PartitionKey); 41 | } 42 | 43 | [Test] 44 | public void RowKey() 45 | { 46 | Assert.AreEqual("RowKey", TableStorage.RowKey); 47 | } 48 | 49 | [Test] 50 | public void Timestamp() 51 | { 52 | Assert.AreEqual("Timestamp", TableStorage.Timestamp); 53 | } 54 | 55 | [Test] 56 | public void ETag() 57 | { 58 | Assert.AreEqual("ETag", TableStorage.ETag); 59 | } 60 | 61 | [Test] 62 | public void ConstructorTableNull() 63 | { 64 | Assert.That(() => new TableStorage(null, ConnectionString), Throws.TypeOf()); 65 | } 66 | 67 | [Test] 68 | public void ConstructorAccountTableNull() 69 | { 70 | Assert.That(() => new TableStorage(null, CloudStorageAccount.Parse(ConnectionString)), Throws.TypeOf()); 71 | } 72 | 73 | [Test] 74 | public void ConstructorConnectionStringNull() 75 | { 76 | Assert.That(() => new TableStorage("TestTable", (string)null), Throws.TypeOf()); 77 | } 78 | 79 | [Test] 80 | public void Name() 81 | { 82 | var name = Guid.NewGuid().ToString(); 83 | var t = new TableStorage(name, ConnectionString); 84 | Assert.AreEqual(name, t.Name); 85 | } 86 | 87 | [Test] 88 | public void Client() 89 | { 90 | var name = Guid.NewGuid().ToString(); 91 | var t = new TableStorage(name, ConnectionString); 92 | Assert.IsNotNull(t.Client); 93 | } 94 | 95 | [Test] 96 | public void Reference() 97 | { 98 | var name = Guid.NewGuid().ToString(); 99 | var t = new TableStorage(name, ConnectionString); 100 | Assert.IsNotNull(t.Reference); 101 | } 102 | 103 | [Test] 104 | public void InsertDictionaryNull() 105 | { 106 | var name = Guid.NewGuid().ToString(); 107 | var t = new TableStorage(name, ConnectionString); 108 | 109 | Assert.That(() => t.InsertOrReplace((IDictionary)null), Throws.TypeOf()); 110 | } 111 | 112 | [Test] 113 | public void QueryFunctionNull() 114 | { 115 | var name = Guid.NewGuid().ToString(); 116 | var t = new TableStorage(name, ConnectionString); 117 | 118 | Assert.That(() => t.Query(null, 1000), Throws.TypeOf()); 119 | } 120 | 121 | 122 | [Test] 123 | public void QueryFunctionResultsNegative() 124 | { 125 | var name = Guid.NewGuid().ToString(); 126 | var t = new TableStorage(name, ConnectionString); 127 | 128 | Assert.That(() => t.Query(i => i.PartitionKey == "hi", -100), Throws.TypeOf()); 129 | } 130 | 131 | [Test] 132 | public void QueryTableQueryNull() 133 | { 134 | var name = Guid.NewGuid().ToString(); 135 | var t = new TableStorage(name, ConnectionString); 136 | 137 | Assert.That(() => t.Query(null), Throws.TypeOf()); 138 | } 139 | 140 | [Test] 141 | public void QueryDictionaryQueryNull() 142 | { 143 | var name = Guid.NewGuid().ToString(); 144 | var t = new TableStorage(name, ConnectionString); 145 | 146 | Assert.That(() => t.Query((TableQuery)null), Throws.TypeOf()); 147 | } 148 | 149 | [Test] 150 | public void DeleteEntityNull() 151 | { 152 | var name = Guid.NewGuid().ToString(); 153 | var t = new TableStorage(name, ConnectionString); 154 | 155 | Assert.That(() => t.Delete((ITableEntity)null), Throws.TypeOf()); 156 | } 157 | 158 | [Test] 159 | public void DeleteEntitiesNull() 160 | { 161 | var name = Guid.NewGuid().ToString(); 162 | var t = new TableStorage(name, ConnectionString); 163 | 164 | Assert.That(() => t.Delete((IEnumerable)null), Throws.TypeOf()); 165 | } 166 | 167 | [Test] 168 | public void BatchOne() 169 | { 170 | var items = new List(); 171 | items.Add(new TableEntity()); 172 | 173 | var name = Guid.NewGuid().ToString(); 174 | var t = new TableStorage(name, ConnectionString); 175 | 176 | var batches = t.Batch(items); 177 | Assert.AreEqual(1, batches.Count()); 178 | Assert.AreEqual(1, batches.First().Count()); 179 | } 180 | 181 | [Test] 182 | public void BatchNone() 183 | { 184 | var items = new List(); 185 | 186 | var name = Guid.NewGuid().ToString(); 187 | var t = new TableStorage(name, ConnectionString); 188 | 189 | var batches = t.Batch(items); 190 | Assert.AreEqual(0, batches.Count()); 191 | } 192 | 193 | [Test] 194 | public void BatchThousandsDifferentPartitions() 195 | { 196 | var random = new Random(); 197 | var count = random.Next(2001, 10000); 198 | var items = new List(); 199 | 200 | for (var i = 0; i < count; i++) 201 | { 202 | items.Add(new TableEntity() { PartitionKey = Guid.NewGuid().ToString() }); 203 | } 204 | 205 | var name = Guid.NewGuid().ToString(); 206 | var t = new TableStorage(name, ConnectionString); 207 | 208 | var batches = t.Batch(items); 209 | Assert.AreEqual(count, batches.Count()); 210 | } 211 | 212 | [Test] 213 | public void BatchThousands() 214 | { 215 | var random = new Random(); 216 | var count = random.Next(2001, 10000); 217 | var partition = Guid.NewGuid().ToString(); 218 | var items = new List(); 219 | 220 | for (var i = 0; i < count; i++) 221 | { 222 | items.Add(new TableEntity() { PartitionKey = partition }); 223 | } 224 | 225 | var name = Guid.NewGuid().ToString(); 226 | var t = new TableStorage(name, ConnectionString); 227 | 228 | var batches = t.Batch(items); 229 | Assert.AreEqual(Math.Ceiling(((double)count / TableStorage.MaimumxInsertBatch)), batches.Count()); 230 | var resultCount = 0; 231 | foreach (var b in batches) 232 | { 233 | resultCount += b.Count(); 234 | } 235 | Assert.AreEqual(count, resultCount); 236 | } 237 | 238 | [Test] 239 | public void ChunkThousands() 240 | { 241 | var random = new Random(); 242 | var count = random.Next(2001, 15000); 243 | var partition = Guid.NewGuid().ToString(); 244 | var items = new List(); 245 | 246 | for (var i = 0; i < count; i++) 247 | { 248 | items.Add(new TableEntity() { PartitionKey = partition }); 249 | } 250 | 251 | var name = Guid.NewGuid().ToString(); 252 | var t = new TableStorage(name, ConnectionString); 253 | 254 | var batches = t.Chunk(items); 255 | Assert.AreEqual(Math.Ceiling(((double)count / TableStorage.MaimumxInsertBatch)), batches.Count()); 256 | var resultCount = 0; 257 | foreach (var b in batches) 258 | { 259 | resultCount += b.Count(); 260 | } 261 | Assert.AreEqual(count, resultCount); 262 | } 263 | 264 | [Test] 265 | public void ChunkOne() 266 | { 267 | var items = new List(); 268 | items.Add(new TableEntity()); 269 | 270 | var name = Guid.NewGuid().ToString(); 271 | var t = new TableStorage(name, ConnectionString); 272 | 273 | var batches = t.Chunk(items); 274 | Assert.AreEqual(1, batches.Count()); 275 | Assert.AreEqual(1, batches.First().Count()); 276 | } 277 | 278 | [Test] 279 | public void ChunkNone() 280 | { 281 | var items = new List(); 282 | 283 | var name = Guid.NewGuid().ToString(); 284 | var t = new TableStorage(name, ConnectionString); 285 | 286 | var batches = t.Chunk(items); 287 | Assert.AreEqual(0, batches.Count()); 288 | } 289 | 290 | [Test] 291 | public void BatchDictionaryOne() 292 | { 293 | var items = new List>(); 294 | var dic = new Dictionary(); 295 | dic.Add(TableStorage.PartitionKey, Guid.NewGuid().ToString()); 296 | items.Add(dic); 297 | 298 | var name = Guid.NewGuid().ToString(); 299 | var t = new TableStorage(name, ConnectionString); 300 | 301 | var batches = t.Batch(items); 302 | Assert.AreEqual(1, batches.Count()); 303 | Assert.AreEqual(1, batches.First().Count()); 304 | } 305 | 306 | [Test] 307 | public void BatchDictionaryNone() 308 | { 309 | var items = new List>(); 310 | 311 | var name = Guid.NewGuid().ToString(); 312 | var t = new TableStorage(name, ConnectionString); 313 | 314 | var batches = t.Batch(items); 315 | Assert.AreEqual(0, batches.Count()); 316 | } 317 | 318 | [Test] 319 | public void BatchDictionaryThousandsDifferentPartitions() 320 | { 321 | var random = new Random(); 322 | var count = random.Next(2001, 10000); 323 | var items = new List>(); 324 | 325 | for (var i = 0; i < count; i++) 326 | { 327 | var dic = new Dictionary(); 328 | dic.Add(TableStorage.PartitionKey, Guid.NewGuid().ToString()); 329 | items.Add(dic); 330 | } 331 | 332 | var name = Guid.NewGuid().ToString(); 333 | var t = new TableStorage(name, ConnectionString); 334 | 335 | var batches = t.Batch(items); 336 | Assert.AreEqual(count, batches.Count()); 337 | } 338 | 339 | [Test] 340 | public void BatchDictionaryThousands() 341 | { 342 | var random = new Random(); 343 | var count = random.Next(2001, 10000); 344 | var partition = Guid.NewGuid().ToString(); 345 | var items = new List>(); 346 | 347 | for (var i = 0; i < count; i++) 348 | { 349 | var dic = new Dictionary(); 350 | dic.Add(TableStorage.PartitionKey, partition); 351 | items.Add(dic); 352 | } 353 | 354 | var name = Guid.NewGuid().ToString(); 355 | var t = new TableStorage(name, ConnectionString); 356 | 357 | var batches = t.Batch(items); 358 | Assert.AreEqual(Math.Ceiling(((double)count / TableStorage.MaimumxInsertBatch)), batches.Count()); 359 | var resultCount = 0; 360 | foreach (var b in batches) 361 | { 362 | resultCount += b.Count(); 363 | } 364 | Assert.AreEqual(count, resultCount); 365 | } 366 | } 367 | } -------------------------------------------------------------------------------- /Azure.Data.Wrappers.Test/Integration/ContainerTests.cs: -------------------------------------------------------------------------------- 1 | namespace Azure.Data.Wrappers.Test.Integration 2 | { 3 | using Azure.Data.Wrappers; 4 | using Microsoft.WindowsAzure.Storage; 5 | using Microsoft.WindowsAzure.Storage.Blob; 6 | using NUnit.Framework; 7 | using System; 8 | using System.IO; 9 | using System.Linq; 10 | using System.Threading.Tasks; 11 | 12 | [TestFixture] 13 | [Category("Integration")] 14 | public class ContainerTests 15 | { 16 | #region Helper 17 | private class Helper 18 | { 19 | public Guid Id 20 | { 21 | get; 22 | set; 23 | } 24 | } 25 | #endregion 26 | 27 | [Test] 28 | public async Task ConstructorAccount() 29 | { 30 | var name = 'a' + Guid.NewGuid().ToString().ToLowerInvariant().Replace('-', 'a'); 31 | var account = CloudStorageAccount.Parse(TestHelpers.DevConnectionString); 32 | var storage = new Container(name, account); 33 | var created = await storage.CreateIfNotExists(); 34 | 35 | Assert.IsTrue(created); 36 | } 37 | 38 | [Test] 39 | public async Task CreateIfNotExists() 40 | { 41 | var name = 'a' + Guid.NewGuid().ToString().ToLowerInvariant().Replace('-', 'a'); 42 | var storage = new Container(name,TestHelpers.DevConnectionString); 43 | var created = await storage.CreateIfNotExists(); 44 | 45 | Assert.IsTrue(created); 46 | 47 | var blobClient = storage.Account.CreateCloudBlobClient(); 48 | var container = blobClient.GetContainerReference(name); 49 | var permissions = await container.GetPermissionsAsync(); 50 | Assert.AreEqual(BlobContainerPublicAccessType.Off, permissions.PublicAccess); 51 | } 52 | 53 | [Test] 54 | public async Task CreateIfNotExistsPublic() 55 | { 56 | var name = 'a' + Guid.NewGuid().ToString().ToLowerInvariant().Replace('-', 'a'); 57 | var storage = new Container(name,TestHelpers.DevConnectionString, true); 58 | var created = await storage.CreateIfNotExists(); 59 | 60 | Assert.IsTrue(created); 61 | 62 | var blobClient = storage.Account.CreateCloudBlobClient(); 63 | var container = blobClient.GetContainerReference(name); 64 | var permissions = await container.GetPermissionsAsync(); 65 | Assert.AreEqual(BlobContainerPublicAccessType.Blob, permissions.PublicAccess); 66 | } 67 | 68 | [Test] 69 | public async Task Exists() 70 | { 71 | var blobName = Guid.NewGuid().ToString(); 72 | var storage = await TestHelpers.GetTestContainer(); 73 | await storage.Save(blobName, Guid.NewGuid()); 74 | var exists = await storage.Exists(blobName); 75 | 76 | Assert.IsTrue(exists); 77 | } 78 | 79 | [Test] 80 | public async Task ExistsNo() 81 | { 82 | var blobName = Guid.NewGuid().ToString(); 83 | var storage = new Container(TestHelpers.generateUniqueName(),TestHelpers.DevConnectionString); 84 | 85 | var exists = await storage.Exists(blobName); 86 | 87 | Assert.IsFalse(exists); 88 | } 89 | 90 | [Test] 91 | public async Task GetBlockReference() 92 | { 93 | var name = string.Format("{0}.bin", Guid.NewGuid()); 94 | var storage = await TestHelpers.GetTestContainer(); 95 | await storage.Save(name, new Helper()); 96 | 97 | var block = storage.GetBlockReference(name); 98 | Assert.IsNotNull(block); 99 | Assert.IsTrue(block.Exists()); 100 | } 101 | 102 | [Test] 103 | public async Task GetPageReference() 104 | { 105 | var random = new Random(); 106 | var bytes = new byte[1024]; 107 | random.NextBytes(bytes); 108 | 109 | var name = string.Format("{0}.bin", Guid.NewGuid()); 110 | var storage = await TestHelpers.GetTestContainer(); 111 | var blob = storage.Reference.GetPageBlobReference(name); 112 | await blob.CreateAsync(1024); 113 | await blob.UploadFromByteArrayAsync(bytes, 0, bytes.Length); 114 | 115 | var page = storage.GetPageReference(name); 116 | Assert.IsNotNull(page); 117 | Assert.IsTrue(page.Exists()); 118 | } 119 | 120 | [Test] 121 | public async Task RoundTripObject() 122 | { 123 | var helper = new Helper() 124 | { 125 | Id = Guid.NewGuid(), 126 | }; 127 | 128 | var blobName = Guid.NewGuid().ToString(); 129 | var storage = await TestHelpers.GetTestContainer(); 130 | 131 | await storage.Save(blobName, helper); 132 | var returned = await storage.Get(blobName); 133 | 134 | Assert.IsNotNull(returned); 135 | Assert.AreEqual(helper.Id, returned.Id); 136 | 137 | var properties = await storage.Properties(blobName); 138 | Assert.IsNotNull(properties); 139 | Assert.AreEqual("application/json", properties.ContentType); 140 | } 141 | 142 | [Test] 143 | public async Task RoundTripText() 144 | { 145 | var data = Guid.NewGuid().ToString(); 146 | var blobName = Guid.NewGuid().ToString(); 147 | var storage = await TestHelpers.GetTestContainer(); 148 | 149 | await storage.Save(blobName, data); 150 | var returned = await storage.GetText(blobName); 151 | 152 | Assert.IsNotNull(returned); 153 | Assert.AreEqual(data, returned); 154 | 155 | var properties = await storage.Properties(blobName); 156 | Assert.IsNotNull(properties); 157 | Assert.AreEqual("text/plain", properties.ContentType); 158 | } 159 | 160 | [Test] 161 | public async Task JsonContentType() 162 | { 163 | var helper = new Helper() 164 | { 165 | Id = Guid.NewGuid(), 166 | }; 167 | 168 | var blobName = Guid.NewGuid().ToString(); 169 | var storage = await TestHelpers.GetTestContainer(); 170 | 171 | await storage.Save(blobName, helper); 172 | var returned = await storage.Properties(blobName); 173 | 174 | Assert.IsNotNull(returned); 175 | Assert.AreEqual("application/json", returned.ContentType); 176 | } 177 | 178 | [Test] 179 | public async Task RoundTripBytes() 180 | { 181 | var random = new Random(); 182 | var bytes = new byte[1024]; 183 | random.NextBytes(bytes); 184 | 185 | var blobName = Guid.NewGuid().ToString(); 186 | var storage = await TestHelpers.GetTestContainer(); 187 | 188 | await storage.Save(blobName, bytes); 189 | var returned = await storage.Get(blobName); 190 | 191 | Assert.IsNotNull(returned); 192 | Assert.AreEqual(bytes.Length, returned.Length); 193 | Assert.AreEqual(bytes, returned); 194 | 195 | var properties = await storage.Properties(blobName); 196 | Assert.IsNotNull(properties); 197 | Assert.AreEqual("application/octet-stream", properties.ContentType); 198 | } 199 | 200 | [Test] 201 | public async Task RoundTripStream() 202 | { 203 | var random = new Random(); 204 | var bytes = new byte[1024]; 205 | random.NextBytes(bytes); 206 | 207 | var blobName = Guid.NewGuid().ToString(); 208 | var storage = await TestHelpers.GetTestContainer(); 209 | 210 | await storage.Save(blobName, bytes); 211 | using (var returned = await storage.Stream(blobName) as MemoryStream) 212 | { 213 | var stored = returned.ToArray(); 214 | 215 | Assert.IsNotNull(stored); 216 | Assert.AreEqual(bytes.Length, stored.Length); 217 | Assert.AreEqual(bytes, stored); 218 | } 219 | } 220 | 221 | [Test] 222 | public async Task BytesDefaultContentType() 223 | { 224 | var random = new Random(); 225 | var bytes = new byte[1024]; 226 | random.NextBytes(bytes); 227 | 228 | var blobName = Guid.NewGuid().ToString(); 229 | var storage = await TestHelpers.GetTestContainer(); 230 | 231 | await storage.Save(blobName, bytes); 232 | var returned = await storage.Properties(blobName); 233 | 234 | Assert.IsNotNull(returned); 235 | Assert.AreEqual(bytes.Length, returned.Length); 236 | Assert.AreEqual("application/octet-stream", returned.ContentType); 237 | } 238 | 239 | [Test] 240 | public async Task BytesContentType() 241 | { 242 | var random = new Random(); 243 | var bytes = new byte[1024]; 244 | random.NextBytes(bytes); 245 | 246 | var blobName = Guid.NewGuid().ToString(); 247 | var storage = await TestHelpers.GetTestContainer(); 248 | 249 | await storage.Save(blobName, bytes, "application/pdf"); 250 | var returned = await storage.Properties(blobName); 251 | 252 | Assert.IsNotNull(returned); 253 | Assert.AreEqual(bytes.Length, returned.Length); 254 | Assert.AreEqual("application/pdf", returned.ContentType); 255 | } 256 | 257 | [Test] 258 | public async Task Delete() 259 | { 260 | var helper = new Helper() 261 | { 262 | Id = Guid.NewGuid(), 263 | }; 264 | 265 | var blobName = Guid.NewGuid().ToString(); 266 | var storage = await TestHelpers.GetTestContainer(); 267 | 268 | await storage.Save(blobName, helper); 269 | await storage.Delete(blobName); 270 | 271 | Assert.That(() => storage.Get(blobName), Throws.TypeOf()); 272 | } 273 | 274 | [Test] 275 | public async Task List() 276 | { 277 | var random = new Random(); 278 | var bytes = new byte[16]; 279 | random.NextBytes(bytes); 280 | var count = random.Next(1, 32); 281 | var storage = await TestHelpers.GetTestContainer(); 282 | for (var i = 0; i < count; i++) 283 | { 284 | var blobName = Guid.NewGuid().ToString(); 285 | await storage.Save(blobName, bytes); 286 | } 287 | 288 | var blobs = await storage.List(); 289 | Assert.AreEqual(count, blobs.Count()); 290 | } 291 | 292 | [Test] 293 | public async Task SnapShotPageBlob() 294 | { 295 | var random = new Random(); 296 | var bytes = new byte[1024]; 297 | random.NextBytes(bytes); 298 | 299 | var name = string.Format("{0}.bin", Guid.NewGuid()); 300 | var storage = await TestHelpers.GetTestContainer(); 301 | var blob = storage.Reference.GetPageBlobReference(name); 302 | await blob.CreateAsync(1024); 303 | await blob.UploadFromByteArrayAsync(bytes, 0, bytes.Length); 304 | 305 | var snapshot = await storage.Snapshot(name); 306 | Assert.IsTrue(snapshot.IsSnapshot); 307 | 308 | var returned = storage.GetPageReference(snapshot.Name, snapshot.SnapshotTime); 309 | Assert.IsNotNull(returned); 310 | Assert.IsTrue(returned.IsSnapshot); 311 | } 312 | 313 | [Test] 314 | public async Task SnapShotBlockBlob() 315 | { 316 | var random = new Random(); 317 | var bytes = new byte[16]; 318 | random.NextBytes(bytes); 319 | 320 | var name = string.Format("{0}.bin", Guid.NewGuid()); 321 | var storage = await TestHelpers.GetTestContainer(); 322 | await storage.Save(name, bytes); 323 | 324 | var snapshot = await storage.Snapshot(name); 325 | Assert.IsTrue(snapshot.IsSnapshot); 326 | 327 | var returned = storage.GetPageReference(snapshot.Name, snapshot.SnapshotTime); 328 | Assert.IsNotNull(returned); 329 | Assert.IsTrue(returned.IsSnapshot); 330 | } 331 | 332 | [Test] 333 | public async Task SnapShoAndDelete() 334 | { 335 | var random = new Random(); 336 | var bytes = new byte[16]; 337 | random.NextBytes(bytes); 338 | 339 | var name = string.Format("{0}.bin", Guid.NewGuid()); 340 | var storage = await TestHelpers.GetTestContainer(); 341 | await storage.Save(name, bytes); 342 | 343 | var snapshot = await storage.Snapshot(name); 344 | await storage.Delete(name); 345 | } 346 | 347 | [Test] 348 | public async Task SnapShoAndDeleteSafe() 349 | { 350 | var random = new Random(); 351 | var bytes = new byte[16]; 352 | random.NextBytes(bytes); 353 | 354 | var name = string.Format("{0}.bin", Guid.NewGuid()); 355 | var storage = await TestHelpers.GetTestContainer(); 356 | await storage.Save(name, bytes); 357 | 358 | var snapshot = await storage.Snapshot(name); 359 | 360 | Assert.That(() => storage.Delete(name, false), Throws.TypeOf()); 361 | } 362 | 363 | [Test] 364 | public async Task SnapshotNonExistant() 365 | { 366 | var blob = Guid.NewGuid().ToString(); 367 | var storage = await TestHelpers.GetTestContainer(); 368 | Assert.IsNull(await storage.Snapshot(blob)); 369 | } 370 | 371 | [Test] 372 | public async Task DontLoseContentType() 373 | { 374 | var cache = "public, max-age=31536000"; 375 | var contentType = "text/guid"; 376 | var blobName = Guid.NewGuid().ToString(); 377 | var storage = await TestHelpers.GetTestContainer(); 378 | 379 | await storage.Save(blobName, Guid.NewGuid().ToString(), contentType); 380 | await storage.SetCacheControl(blobName); 381 | var returned = await storage.Properties(blobName); 382 | 383 | Assert.IsNotNull(returned); 384 | Assert.AreEqual(cache, returned.CacheControl); 385 | Assert.AreEqual(contentType, returned.ContentType); 386 | } 387 | 388 | [Test] 389 | public async Task DontLoseCacheControl() 390 | { 391 | var cache = "public, max-age=31536000"; 392 | var contentType = "text/guid"; 393 | var blobName = Guid.NewGuid().ToString(); 394 | var storage = await TestHelpers.GetTestContainer(); 395 | 396 | await storage.Save(blobName, Guid.NewGuid().ToString(), contentType); 397 | await storage.SetCacheControl(blobName); 398 | await storage.Save(blobName, Guid.NewGuid().ToString(), contentType); 399 | var returned = await storage.Properties(blobName); 400 | 401 | Assert.IsNotNull(returned); 402 | Assert.AreEqual(cache, returned.CacheControl); 403 | Assert.AreEqual(contentType, returned.ContentType); 404 | } 405 | 406 | [Test] 407 | public async Task SetCacheControlDefault() 408 | { 409 | var cache = "public, max-age=31536000"; 410 | var blobName = Guid.NewGuid().ToString(); 411 | var storage = await TestHelpers.GetTestContainer(); 412 | 413 | await storage.Save(blobName, Guid.NewGuid().ToString()); 414 | await storage.SetCacheControl(blobName); 415 | var returned = await storage.Properties(blobName); 416 | 417 | Assert.IsNotNull(returned); 418 | Assert.AreEqual(cache, returned.CacheControl); 419 | } 420 | 421 | [Test] 422 | public async Task SetCacheControlZero() 423 | { 424 | var cache = "public, max-age=31536000"; 425 | var blobName = Guid.NewGuid().ToString(); 426 | var storage = await TestHelpers.GetTestContainer(); 427 | 428 | await storage.Save(blobName, Guid.NewGuid().ToString()); 429 | await storage.SetCacheControl(blobName, 0); 430 | var returned = await storage.Properties(blobName); 431 | 432 | Assert.IsNotNull(returned); 433 | Assert.AreEqual(cache, returned.CacheControl); 434 | } 435 | 436 | [Test] 437 | public async Task SetCacheControl() 438 | { 439 | var cache = "public, max-age=1000"; 440 | var blobName = Guid.NewGuid().ToString(); 441 | var storage = await TestHelpers.GetTestContainer(); 442 | 443 | await storage.Save(blobName, Guid.NewGuid().ToString()); 444 | await storage.SetCacheControl(blobName, 1000); 445 | var returned = await storage.Properties(blobName); 446 | 447 | Assert.IsNotNull(returned); 448 | Assert.AreEqual(cache, returned.CacheControl); 449 | } 450 | 451 | [Test] 452 | public async Task CopyToFrom() 453 | { 454 | var random = new Random(); 455 | var bytes = new byte[16]; 456 | random.NextBytes(bytes); 457 | 458 | var from = string.Format("{0}.bin", Guid.NewGuid()); 459 | var to = string.Format("{0}.bin", Guid.NewGuid()); 460 | var storage = await TestHelpers.GetTestContainer(); 461 | await storage.Save(from, bytes); 462 | 463 | var uri = await storage.Copy(from, to); 464 | 465 | Assert.IsNotNull(uri); 466 | 467 | var exists = await storage.Exists(to); 468 | var data = await storage.Get(to); 469 | Assert.AreEqual(bytes, data); 470 | 471 | await storage.Delete(from); 472 | await storage.Delete(to); 473 | } 474 | 475 | [Test] 476 | public async Task Copy() 477 | { 478 | var random = new Random(); 479 | var bytes = new byte[16]; 480 | random.NextBytes(bytes); 481 | 482 | var toContainerName = 'a' + Guid.NewGuid().ToString().Replace("-", string.Empty); 483 | var toContainer = new Container(toContainerName,TestHelpers.DevConnectionString); 484 | await toContainer.CreateIfNotExists(); 485 | 486 | var from = string.Format("{0}.bin", Guid.NewGuid()); 487 | var to = string.Format("{0}.bin", Guid.NewGuid()); 488 | var storage = await TestHelpers.GetTestContainer(); 489 | await storage.Save(from, bytes); 490 | 491 | var uri = await storage.Copy(from, toContainer, to); 492 | 493 | Assert.IsNotNull(uri); 494 | 495 | var exists = await toContainer.Exists(to); 496 | var data = await toContainer.Get(to); 497 | Assert.AreEqual(bytes, data); 498 | 499 | await storage.Delete(from); 500 | await toContainer.Delete(to); 501 | await toContainer.Delete(); 502 | } 503 | 504 | [Test] 505 | public async Task CopyContainerName() 506 | { 507 | var random = new Random(); 508 | var bytes = new byte[16]; 509 | random.NextBytes(bytes); 510 | 511 | var toContainerName = 'a' + Guid.NewGuid().ToString().Replace("-", string.Empty); 512 | var toContainer = new Container(toContainerName,TestHelpers.DevConnectionString); 513 | await toContainer.CreateIfNotExists(); 514 | 515 | var from = string.Format("{0}.bin", Guid.NewGuid()); 516 | var to = string.Format("{0}.bin", Guid.NewGuid()); 517 | var storage = await TestHelpers.GetTestContainer(); 518 | await storage.Save(from, bytes); 519 | 520 | var uri = await storage.Copy(from, toContainerName, to); 521 | 522 | Assert.IsNotNull(uri); 523 | 524 | var exists = await toContainer.Exists(to); 525 | var data = await toContainer.Get(to); 526 | Assert.AreEqual(bytes, data); 527 | 528 | await storage.Delete(from); 529 | await toContainer.Delete(to); 530 | await toContainer.Delete(); 531 | } 532 | 533 | [Test] 534 | public void GetSharedAccessSignatureSuccess() 535 | { 536 | var target = new Container(TestHelpers.generateUniqueName(), TestHelpers.DevConnectionString); 537 | 538 | var result = target.GetSharedAccessSignature(new SharedAccessBlobPolicy()); 539 | 540 | Assert.IsNotNull(result); 541 | } 542 | 543 | [Test] 544 | public void GetSharedAccessSignatureWithGroupPolicySuccess() 545 | { 546 | var target = new Container(TestHelpers.generateUniqueName(), TestHelpers.DevConnectionString); 547 | 548 | var result = target.GetSharedAccessSignature(null, "foo"); 549 | 550 | Assert.IsNotNull(result); 551 | } 552 | } 553 | } -------------------------------------------------------------------------------- /Azure.Data.Wrappers/Container.cs: -------------------------------------------------------------------------------- 1 | namespace Azure.Data.Wrappers 2 | { 3 | using Microsoft.WindowsAzure.Storage; 4 | using Microsoft.WindowsAzure.Storage.Blob; 5 | using Microsoft.WindowsAzure.Storage.RetryPolicies; 6 | using Newtonsoft.Json; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.IO; 10 | using System.Linq; 11 | using System.Threading.Tasks; 12 | 13 | /// 14 | /// Blob Container 15 | /// 16 | public class Container : AzureStorage, IContainer 17 | { 18 | #region Members 19 | /// 20 | /// Default Cache Duration 21 | /// 22 | public const uint DefaultCacheDuration = 31536000; 23 | 24 | /// 25 | /// Client 26 | /// 27 | private readonly CloudBlobClient client; 28 | 29 | /// 30 | /// Reference 31 | /// 32 | private readonly CloudBlobContainer reference; 33 | 34 | /// 35 | /// Is Public 36 | /// 37 | private readonly bool isPublic = false; 38 | #endregion 39 | 40 | #region Constructors 41 | /// 42 | /// Container Constructor 43 | /// 44 | /// Name 45 | /// Connection String 46 | /// Is Public 47 | /// Location Mode 48 | public Container(string name, string connectionString, bool isPublic = false, LocationMode location = LocationMode.PrimaryThenSecondary) 49 | : this(name, CloudStorageAccount.Parse(connectionString), isPublic, location) 50 | { 51 | } 52 | 53 | /// 54 | /// Container Constructor 55 | /// 56 | /// Name 57 | /// Storage Account 58 | /// Is Public 59 | /// Location Mode 60 | public Container(string name, CloudStorageAccount account, bool isPublic = false, LocationMode location = LocationMode.PrimaryThenSecondary) 61 | : base(account) 62 | { 63 | if (string.IsNullOrWhiteSpace(name)) 64 | { 65 | throw new ArgumentException("name"); 66 | } 67 | 68 | this.client = this.Account.CreateCloudBlobClient(); 69 | this.client.DefaultRequestOptions.LocationMode = location; 70 | this.reference = this.client.GetContainerReference(name); 71 | this.isPublic = isPublic; 72 | } 73 | #endregion 74 | 75 | #region Properties 76 | /// 77 | /// Name 78 | /// 79 | public virtual string Name 80 | { 81 | get 82 | { 83 | return this.reference.Name; 84 | } 85 | } 86 | 87 | /// 88 | /// Is Public 89 | /// 90 | public virtual bool IsPublic 91 | { 92 | get 93 | { 94 | return this.isPublic; 95 | } 96 | } 97 | 98 | /// 99 | /// Client 100 | /// 101 | public virtual CloudBlobClient Client 102 | { 103 | get 104 | { 105 | return this.client; 106 | } 107 | } 108 | 109 | /// 110 | /// Reference 111 | /// 112 | public virtual CloudBlobContainer Reference 113 | { 114 | get 115 | { 116 | return this.reference; 117 | } 118 | } 119 | #endregion 120 | 121 | #region Methods 122 | /// 123 | /// Create If Not Exists 124 | /// 125 | /// Created 126 | public virtual async Task CreateIfNotExists() 127 | { 128 | var result = await this.reference.CreateIfNotExistsAsync().ConfigureAwait(false); 129 | if (result) 130 | { 131 | var permissions = new BlobContainerPermissions() 132 | { 133 | PublicAccess = this.isPublic ? BlobContainerPublicAccessType.Blob : BlobContainerPublicAccessType.Off 134 | }; 135 | 136 | await this.reference.SetPermissionsAsync(permissions).ConfigureAwait(false); 137 | } 138 | 139 | return result; 140 | } 141 | 142 | /// 143 | /// Delete Container 144 | /// 145 | /// Task 146 | public virtual async Task Delete() 147 | { 148 | await this.reference.DeleteAsync().ConfigureAwait(false); 149 | } 150 | 151 | /// 152 | /// Delete from Blob Storage 153 | /// 154 | /// Blob Name 155 | /// Delete History (Snapshots) 156 | /// Object 157 | public virtual async Task Delete(string blobName, bool deleteHistory = true) 158 | { 159 | if (string.IsNullOrWhiteSpace(blobName)) 160 | { 161 | throw new ArgumentException("blobName"); 162 | } 163 | 164 | var delSnapshots = deleteHistory ? DeleteSnapshotsOption.IncludeSnapshots : DeleteSnapshotsOption.None; 165 | var blob = this.GetBlockReference(blobName); 166 | await blob.DeleteAsync(delSnapshots, AccessCondition.GenerateEmptyCondition(), new BlobRequestOptions(), new OperationContext()).ConfigureAwait(false); 167 | } 168 | 169 | /// 170 | /// Blob Exists 171 | /// 172 | /// Blob Name 173 | /// bool 174 | public virtual async Task Exists(string blobName) 175 | { 176 | if (string.IsNullOrWhiteSpace(blobName)) 177 | { 178 | throw new ArgumentException("blobName"); 179 | } 180 | 181 | var blob = this.GetBlockReference(blobName); 182 | return await blob.ExistsAsync().ConfigureAwait(false); 183 | } 184 | 185 | /// 186 | /// Save Object as Json to Blob Storage 187 | /// 188 | /// Type 189 | /// Blob Name 190 | /// Object 191 | /// Task 192 | public virtual async Task Save(string blobName, object obj) 193 | { 194 | if (string.IsNullOrWhiteSpace(blobName)) 195 | { 196 | throw new ArgumentException("blobName"); 197 | } 198 | if (null == obj) 199 | { 200 | throw new ArgumentNullException("obj"); 201 | } 202 | 203 | var json = JsonConvert.SerializeObject(obj); 204 | 205 | await this.Save(blobName, json, "application/json").ConfigureAwait(false); 206 | } 207 | 208 | /// 209 | /// Save Text 210 | /// 211 | /// Blob Name 212 | /// Text 213 | /// Content Type 214 | /// Task 215 | public virtual async Task Save(string blobName, string text, string contentType = "text/plain") 216 | { 217 | if (string.IsNullOrWhiteSpace(blobName)) 218 | { 219 | throw new ArgumentException("blobName"); 220 | } 221 | if (string.IsNullOrWhiteSpace(text)) 222 | { 223 | throw new ArgumentException("text"); 224 | } 225 | 226 | var blob = this.GetBlockReference(blobName); 227 | var cacheProperties = await this.Properties(blobName).ConfigureAwait(false); 228 | 229 | await blob.UploadTextAsync(text).ConfigureAwait(false); 230 | 231 | await this.Set(blob, cacheProperties, contentType).ConfigureAwait(false); 232 | } 233 | 234 | /// 235 | /// Set Properties on Blob 236 | /// 237 | /// Blob 238 | /// Cached Properties 239 | /// Content Type 240 | /// Cache Control 241 | /// Content Disposition 242 | /// Content Encoding 243 | /// Content Language 244 | /// 245 | public virtual async Task Set(CloudBlockBlob blob, BlobProperties cached, string type = null, string cacheControl = null, string disposition = null, string encoding = null, string language = null) 246 | { 247 | await blob.FetchAttributesAsync().ConfigureAwait(false); 248 | 249 | if (null != cached) 250 | { 251 | blob.Properties.CacheControl = cached.CacheControl; 252 | blob.Properties.ContentDisposition = cached.ContentDisposition; 253 | blob.Properties.ContentEncoding = cached.ContentEncoding; 254 | blob.Properties.ContentLanguage = cached.ContentLanguage; 255 | blob.Properties.ContentType = cached.ContentType; 256 | } 257 | 258 | blob.Properties.CacheControl = string.IsNullOrWhiteSpace(cacheControl) ? blob.Properties.CacheControl : cacheControl; 259 | blob.Properties.ContentDisposition = string.IsNullOrWhiteSpace(disposition) ? blob.Properties.ContentDisposition : disposition; 260 | blob.Properties.ContentEncoding = string.IsNullOrWhiteSpace(encoding) ? blob.Properties.ContentEncoding : encoding; 261 | blob.Properties.ContentLanguage = string.IsNullOrWhiteSpace(language) ? blob.Properties.ContentLanguage : language; 262 | blob.Properties.ContentType = string.IsNullOrWhiteSpace(type) ? blob.Properties.ContentType : type; 263 | 264 | await blob.SetPropertiesAsync().ConfigureAwait(false); 265 | } 266 | 267 | /// 268 | /// Get Object from Blob Storage 269 | /// 270 | /// Type 271 | /// Blob Name 272 | /// Object 273 | public virtual async Task Get(string blobName) 274 | { 275 | if (string.IsNullOrWhiteSpace(blobName)) 276 | { 277 | throw new ArgumentException("blobName"); 278 | } 279 | 280 | var json = await this.GetText(blobName).ConfigureAwait(false); 281 | return JsonConvert.DeserializeObject(json); 282 | } 283 | 284 | /// 285 | /// Get Bytes 286 | /// 287 | /// Blob Name 288 | /// bytes 289 | public virtual async Task Get(string blobName) 290 | { 291 | if (string.IsNullOrWhiteSpace(blobName)) 292 | { 293 | throw new ArgumentException("blobName"); 294 | } 295 | 296 | var blob = this.GetBlockReference(blobName); 297 | await blob.FetchAttributesAsync().ConfigureAwait(false); 298 | 299 | var bytes = new byte[blob.Properties.Length]; 300 | await blob.DownloadToByteArrayAsync(bytes, 0).ConfigureAwait(false); 301 | 302 | return bytes; 303 | } 304 | 305 | /// 306 | /// Get Bytes 307 | /// 308 | /// Blob Name 309 | /// Text 310 | public virtual async Task GetText(string blobName) 311 | { 312 | if (string.IsNullOrWhiteSpace(blobName)) 313 | { 314 | throw new ArgumentException("blobName"); 315 | } 316 | 317 | var blob = this.GetBlockReference(blobName); 318 | return await blob.DownloadTextAsync().ConfigureAwait(false); 319 | } 320 | 321 | /// 322 | /// Save Bytes 323 | /// 324 | /// Blob Name 325 | /// bytes 326 | /// Task 327 | public virtual async Task Save(string blobName, byte[] bytes, string contentType = "application/octet-stream") 328 | { 329 | if (string.IsNullOrWhiteSpace(blobName)) 330 | { 331 | throw new ArgumentException("blobName"); 332 | } 333 | if (null == bytes) 334 | { 335 | throw new ArgumentNullException("bytes"); 336 | } 337 | 338 | var blob = this.GetBlockReference(blobName); 339 | var cached = await this.Properties(blobName).ConfigureAwait(false); 340 | 341 | await blob.UploadFromByteArrayAsync(bytes, 0, bytes.Length).ConfigureAwait(false); 342 | 343 | await this.Set(blob, cached, contentType).ConfigureAwait(false); 344 | } 345 | 346 | /// 347 | /// Blob Properties 348 | /// 349 | /// Blob Name 350 | /// Blob Container Properties 351 | public virtual async Task Properties(string blobName) 352 | { 353 | if (string.IsNullOrWhiteSpace(blobName)) 354 | { 355 | throw new ArgumentException("blobName"); 356 | } 357 | 358 | var exists = await this.Exists(blobName).ConfigureAwait(false); 359 | if (exists) 360 | { 361 | var blob = this.GetBlockReference(blobName); 362 | await blob.FetchAttributesAsync().ConfigureAwait(false); 363 | return blob.Properties; 364 | } 365 | else 366 | { 367 | return null; 368 | } 369 | } 370 | 371 | /// 372 | /// Set Cache Control 373 | /// 374 | /// Blob Name 375 | /// Cache Duration (Default 1 year) 376 | /// Task 377 | public virtual async Task SetCacheControl(string blobName, uint cacheDuration = DefaultCacheDuration) 378 | { 379 | if (string.IsNullOrWhiteSpace(blobName)) 380 | { 381 | throw new ArgumentException("blobName"); 382 | } 383 | 384 | cacheDuration = cacheDuration < 1 ? DefaultCacheDuration : cacheDuration; 385 | 386 | var blob = this.GetBlockReference(blobName); 387 | await this.Set(blob, null, null, string.Format("public, max-age={0}", cacheDuration)).ConfigureAwait(false); 388 | } 389 | 390 | /// 391 | /// Get Reference 392 | /// 393 | /// Blob Name 394 | /// Snapshot time 395 | /// Cloud Block Blob 396 | public virtual CloudBlockBlob GetBlockReference(string blobName, DateTimeOffset? snapshot = null) 397 | { 398 | if (string.IsNullOrWhiteSpace(blobName)) 399 | { 400 | throw new ArgumentException("blobName"); 401 | } 402 | 403 | return this.reference.GetBlockBlobReference(blobName, snapshot); 404 | } 405 | 406 | /// 407 | /// Get Reference 408 | /// 409 | /// Blob Name 410 | /// Snapshot time 411 | /// Cloud Block Blob 412 | public virtual CloudPageBlob GetPageReference(string blobName, DateTimeOffset? snapshot = null) 413 | { 414 | if (string.IsNullOrWhiteSpace(blobName)) 415 | { 416 | throw new ArgumentException("blobName"); 417 | } 418 | 419 | return this.reference.GetPageBlobReference(blobName, snapshot); 420 | } 421 | 422 | /// 423 | /// Get Stream 424 | /// 425 | /// Blob Name 426 | /// Stream 427 | public virtual async Task Stream(string blobName) 428 | { 429 | if (string.IsNullOrWhiteSpace(blobName)) 430 | { 431 | throw new ArgumentException("blobName"); 432 | } 433 | 434 | var properties = await this.Properties(blobName).ConfigureAwait(false); 435 | var blob = this.GetBlockReference(blobName); 436 | var stream = new MemoryStream(); 437 | await blob.DownloadRangeToStreamAsync(stream, 0, properties.Length).ConfigureAwait(false); 438 | stream.Position = 0; 439 | return stream; 440 | } 441 | 442 | /// 443 | /// List Blobs 444 | /// 445 | /// Prefix 446 | /// Use Flat Blob Listing 447 | /// Blobs 448 | public async Task> List(string prefix = null, bool useFlatBlobListing = true, BlobListingDetails details = BlobListingDetails.All, int? maxResults = int.MaxValue) 449 | { 450 | BlobContinuationToken token = null; 451 | var blobs = new List(); 452 | var options = new BlobRequestOptions(); 453 | var operationContext = new OperationContext(); 454 | 455 | do 456 | { 457 | var segments = await this.reference.ListBlobsSegmentedAsync(prefix, useFlatBlobListing, details, maxResults, token, options, operationContext).ConfigureAwait(false); 458 | blobs.AddRange(segments.Results); 459 | token = segments.ContinuationToken; 460 | } 461 | while (null != token); 462 | 463 | return blobs; 464 | } 465 | 466 | /// 467 | /// Create Snapshot 468 | /// 469 | /// Blob Name 470 | /// Task 471 | public virtual async Task Snapshot(string blobName) 472 | { 473 | if (string.IsNullOrWhiteSpace(blobName)) 474 | { 475 | throw new ArgumentException("blobName"); 476 | } 477 | 478 | var options = new BlobRequestOptions 479 | { 480 | LocationMode = LocationMode.PrimaryOnly, 481 | }; 482 | 483 | var blobs = await this.List(blobName).ConfigureAwait(false); 484 | var blob = blobs.FirstOrDefault(); 485 | var block = blob as CloudBlockBlob; 486 | if (null != block) 487 | { 488 | return await block.CreateSnapshotAsync(null, null, options, null).ConfigureAwait(false); 489 | } 490 | var page = blob as CloudPageBlob; 491 | if (null != page) 492 | { 493 | return await page.CreateSnapshotAsync(null, null, options, null).ConfigureAwait(false); 494 | } 495 | 496 | return null; 497 | } 498 | 499 | /// 500 | /// Copy From Blob to Blob 501 | /// 502 | /// From 503 | /// To 504 | /// Blob Uri 505 | public virtual async Task Copy(string from, string to) 506 | { 507 | if (string.IsNullOrWhiteSpace(from)) 508 | { 509 | throw new ArgumentException("Source blob address"); 510 | } 511 | if (string.IsNullOrWhiteSpace(to)) 512 | { 513 | throw new ArgumentException("Target blob address"); 514 | } 515 | 516 | var source = this.GetBlockReference(from); 517 | var target = this.GetBlockReference(to); 518 | return await target.StartCopyAsync(source).ConfigureAwait(false); 519 | } 520 | 521 | /// 522 | /// Copy from, to seperate container/blob 523 | /// 524 | /// From 525 | /// Target 526 | /// To 527 | /// Blob Uri 528 | public virtual async Task Copy(string from, string target, string to) 529 | { 530 | return await this.Copy(from, new Container(target, this.Account), to).ConfigureAwait(false); 531 | } 532 | 533 | /// 534 | /// Copy from, to seperate container/blob 535 | /// 536 | /// From 537 | /// Target 538 | /// To 539 | /// Blob Uri 540 | public virtual async Task Copy(string from, IContainer target, string to) 541 | { 542 | if (string.IsNullOrWhiteSpace(from)) 543 | { 544 | throw new ArgumentException("from"); 545 | } 546 | if (null == target) 547 | { 548 | throw new ArgumentNullException("target"); 549 | } 550 | if (string.IsNullOrWhiteSpace(to)) 551 | { 552 | throw new ArgumentException("to"); 553 | } 554 | 555 | var source = this.GetBlockReference(from); 556 | var targetBlockBlob = target.GetBlockReference(to); 557 | return await targetBlockBlob.StartCopyAsync(source).ConfigureAwait(false); 558 | } 559 | 560 | /// 561 | /// Returns a shared access signature for the container. 562 | /// 563 | /// the access policy for the shared access signature. 564 | /// A container-level access policy 565 | /// A shared access signature, as a URI query string. 566 | public virtual string GetSharedAccessSignature(SharedAccessBlobPolicy policy, string groupPolicyIdentifier = null) 567 | { 568 | return string.IsNullOrEmpty(groupPolicyIdentifier) ? reference.GetSharedAccessSignature(policy) : reference.GetSharedAccessSignature(policy, groupPolicyIdentifier); 569 | } 570 | 571 | #endregion 572 | } 573 | } -------------------------------------------------------------------------------- /Azure.Data.Wrappers/TableStorage.cs: -------------------------------------------------------------------------------- 1 | namespace Azure.Data.Wrappers 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | using Microsoft.WindowsAzure.Storage; 8 | using Microsoft.WindowsAzure.Storage.RetryPolicies; 9 | using Microsoft.WindowsAzure.Storage.Table; 10 | 11 | /// 12 | /// Table Storage 13 | /// 14 | public class TableStorage : AzureStorage, ITableStorage 15 | { 16 | #region Members 17 | /// 18 | /// Partition Key 19 | /// 20 | public const string PartitionKey = "PartitionKey"; 21 | 22 | /// 23 | /// Row Key 24 | /// 25 | public const string RowKey = "RowKey"; 26 | 27 | /// 28 | /// Timestamp 29 | /// 30 | public const string Timestamp = "Timestamp"; 31 | 32 | /// 33 | /// ETag 34 | /// 35 | public const string ETag = "ETag"; 36 | 37 | /// 38 | /// Maximum Insert Batch 39 | /// 40 | public const int MaimumxInsertBatch = 100; 41 | 42 | /// 43 | /// Table Client 44 | /// 45 | private readonly CloudTableClient client; 46 | 47 | /// 48 | /// Table 49 | /// 50 | private readonly CloudTable reference; 51 | #endregion 52 | 53 | #region Constructors 54 | /// 55 | /// Table Storage 56 | /// 57 | /// Table Name 58 | /// Connection String 59 | /// Location Mode 60 | public TableStorage(string tableName, string connectionString, LocationMode location = LocationMode.PrimaryThenSecondary) 61 | : this(tableName, CloudStorageAccount.Parse(connectionString), location) 62 | { 63 | } 64 | 65 | /// 66 | /// Table Storage 67 | /// 68 | /// Table Name 69 | /// Storage Account 70 | /// Location Mode 71 | public TableStorage(string tableName, CloudStorageAccount account, LocationMode location = LocationMode.PrimaryThenSecondary) 72 | : base(account) 73 | { 74 | if (string.IsNullOrWhiteSpace(tableName)) 75 | { 76 | throw new ArgumentException("tableName"); 77 | } 78 | 79 | this.client = base.Account.CreateCloudTableClient(); 80 | this.client.DefaultRequestOptions.LocationMode = location; 81 | 82 | this.reference = client.GetTableReference(tableName); 83 | } 84 | /// 85 | /// Table Storage 86 | /// 87 | /// Table Name 88 | /// Connection String 89 | /// Default request options 90 | public TableStorage(string tableName, string connectionString, TableRequestOptions options) 91 | : this(tableName, CloudStorageAccount.Parse(connectionString), options) 92 | { 93 | } 94 | /// 95 | /// Table Storage 96 | /// 97 | /// Table Name 98 | /// Storage Account 99 | /// Default request options 100 | public TableStorage(string tableName, CloudStorageAccount account, TableRequestOptions options) 101 | : base(account) 102 | { 103 | if (string.IsNullOrWhiteSpace(tableName)) 104 | { 105 | throw new ArgumentException("tableName"); 106 | } 107 | if (options == null) 108 | throw new ArgumentNullException("options"); 109 | 110 | this.client = base.Account.CreateCloudTableClient(); 111 | this.client.DefaultRequestOptions = options; 112 | 113 | this.reference = client.GetTableReference(tableName); 114 | } 115 | #endregion 116 | 117 | #region Properties 118 | /// 119 | /// Table Name 120 | /// 121 | public virtual string Name 122 | { 123 | get 124 | { 125 | return this.reference.Name; 126 | } 127 | } 128 | 129 | /// 130 | /// Table Client 131 | /// 132 | public virtual CloudTableClient Client 133 | { 134 | get 135 | { 136 | return this.client; 137 | } 138 | } 139 | 140 | /// 141 | /// Table 142 | /// 143 | public virtual CloudTable Reference 144 | { 145 | get 146 | { 147 | return this.reference; 148 | } 149 | } 150 | #endregion 151 | 152 | #region Create Table 153 | /// 154 | /// Create If Not Exists 155 | /// 156 | /// 157 | public virtual async Task CreateIfNotExists() 158 | { 159 | return await this.reference.CreateIfNotExistsAsync().ConfigureAwait(false); 160 | } 161 | 162 | /// 163 | /// Create Table 164 | /// 165 | /// Table Name 166 | public virtual async Task Create() 167 | { 168 | return await this.reference.CreateIfNotExistsAsync().ConfigureAwait(false); 169 | } 170 | #endregion 171 | 172 | #region Delete 173 | /// 174 | /// Delete Table 175 | /// 176 | /// 177 | public virtual async Task Delete() 178 | { 179 | await this.reference.DeleteAsync().ConfigureAwait(false); 180 | } 181 | 182 | /// 183 | /// Delete By Partition 184 | /// 185 | /// Partition Key 186 | /// Task 187 | public virtual async Task DeleteByPartition(string partitionKey) 188 | { 189 | var entities = await this.QueryByPartition(partitionKey).ConfigureAwait(false); 190 | if (null != entities && entities.Any()) 191 | { 192 | await this.Delete(entities).ConfigureAwait(false); 193 | } 194 | } 195 | 196 | /// 197 | /// Delete By Row 198 | /// 199 | /// Row Key 200 | /// Task 201 | public virtual async Task DeleteByRow(string rowKey) 202 | { 203 | var entities = await this.QueryByRow(rowKey).ConfigureAwait(false); 204 | if (null != entities && entities.Any()) 205 | { 206 | foreach (var entity in entities) 207 | { 208 | await this.Delete(entity).ConfigureAwait(false); 209 | } 210 | } 211 | } 212 | 213 | /// 214 | /// Delete By Partition and Row 215 | /// 216 | /// Partition Key 217 | /// Row Key 218 | /// Task 219 | public virtual async Task DeleteByPartitionAndRow(string partitionKey, string rowKey) 220 | { 221 | var entity = await this.QueryByPartitionAndRow(partitionKey, rowKey).ConfigureAwait(false); 222 | 223 | if (null != entity) 224 | { 225 | await this.Delete(entity).ConfigureAwait(false); 226 | } 227 | } 228 | 229 | /// 230 | /// Delete Entity 231 | /// 232 | /// Entity 233 | /// Task 234 | public virtual async Task Delete(ITableEntity entity) 235 | { 236 | if (null == entity) 237 | { 238 | throw new ArgumentNullException("entity"); 239 | } 240 | 241 | return await this.reference.ExecuteAsync(TableOperation.Delete(entity)).ConfigureAwait(false); 242 | } 243 | 244 | /// 245 | /// Delete Entities 246 | /// 247 | /// Entities 248 | /// Table Results 249 | public virtual async Task> Delete(IEnumerable entities) 250 | { 251 | if (null == entities) 252 | { 253 | throw new ArgumentNullException("entities"); 254 | } 255 | if (!entities.Any()) 256 | { 257 | return null; 258 | } 259 | 260 | var result = new List(); 261 | 262 | foreach (var batch in this.Batch(entities)) 263 | { 264 | var batchOperation = new TableBatchOperation(); 265 | batch.ToList().ForEach(e => batchOperation.Delete(e)); 266 | var r = await this.reference.ExecuteBatchAsync(batchOperation).ConfigureAwait(false); 267 | result.AddRange(r); 268 | } 269 | 270 | return result; 271 | } 272 | #endregion 273 | 274 | #region Save Data 275 | /// 276 | /// Insert or update the record in table 277 | /// 278 | /// Entity 279 | public virtual async Task InsertOrReplace(ITableEntity entity) 280 | { 281 | return await this.reference.ExecuteAsync(TableOperation.InsertOrReplace(entity)).ConfigureAwait(false); 282 | } 283 | 284 | /// 285 | /// Insert Batch 286 | /// 287 | /// Entities 288 | public virtual async Task> Insert(IEnumerable entities) 289 | { 290 | var result = new List(); 291 | 292 | foreach (var batch in this.Batch(entities)) 293 | { 294 | var batchOperation = new TableBatchOperation(); 295 | batch.ToList().ForEach(e => batchOperation.InsertOrReplace(e)); 296 | var r = await this.reference.ExecuteBatchAsync(batchOperation).ConfigureAwait(false); 297 | result.AddRange(r); 298 | } 299 | 300 | return result; 301 | } 302 | 303 | /// 304 | /// Insert Or Replace Entity (Dictionary) 305 | /// 306 | /// 307 | /// Specify: PartitionKey, RowKey and ETag 308 | /// 309 | /// Entity 310 | /// Result 311 | public virtual async Task InsertOrReplace(IDictionary entity) 312 | { 313 | if (null == entity) 314 | { 315 | throw new ArgumentNullException("data"); 316 | } 317 | 318 | var properties = new Dictionary(); 319 | entity.Keys.Where(k => k != PartitionKey && k != RowKey && k != ETag).ToList().ForEach(key => properties.Add(key, EntityProperty.CreateEntityPropertyFromObject(entity[key]))); 320 | 321 | var partitionKey = entity.Keys.Contains(PartitionKey) ? entity[PartitionKey].ToString() : string.Empty; 322 | var rowKey = entity.Keys.Contains(RowKey) ? entity[RowKey].ToString() : string.Empty; 323 | var etag = entity.Keys.Contains(ETag) ? entity[ETag].ToString() : null; 324 | var dynamicEntity = new DynamicTableEntity(partitionKey, rowKey, etag, properties); 325 | 326 | return await this.InsertOrReplace(dynamicEntity).ConfigureAwait(false); 327 | } 328 | 329 | /// 330 | /// Insert Batch 331 | /// 332 | /// Entities 333 | public virtual async Task> Insert(IEnumerable> entities) 334 | { 335 | var result = new List(); 336 | 337 | foreach (var batch in this.Batch(entities)) 338 | { 339 | var batchOperation = new TableBatchOperation(); 340 | 341 | foreach (var entity in batch) 342 | { 343 | var properties = new Dictionary(); 344 | entity.Keys.Where(k => k != PartitionKey && k != RowKey && k != ETag).ToList().ForEach(key => properties.Add(key, EntityProperty.CreateEntityPropertyFromObject(entity[key]))); 345 | 346 | var partitionKey = entity.Keys.Contains(PartitionKey) ? entity[PartitionKey].ToString() : string.Empty; 347 | var rowKey = entity.Keys.Contains(RowKey) ? entity[RowKey].ToString() : string.Empty; 348 | var etag = entity.Keys.Contains(ETag) ? entity[ETag].ToString() : null; 349 | 350 | batchOperation.InsertOrMerge(new DynamicTableEntity(partitionKey, rowKey, etag, properties)); 351 | } 352 | 353 | var r = await this.reference.ExecuteBatchAsync(batchOperation).ConfigureAwait(false); 354 | result.AddRange(r); 355 | } 356 | 357 | return result; 358 | } 359 | #endregion 360 | 361 | #region Query Object 362 | /// 363 | /// Query By Partition 364 | /// 365 | /// Return Type 366 | /// 367 | /// Entities 368 | public virtual async Task> QueryByPartition(string partitionKey) 369 | where T : ITableEntity, new() 370 | { 371 | var query = new TableQuery().Where(TableQuery.GenerateFilterCondition(PartitionKey, QueryComparisons.Equal, partitionKey)); 372 | return await this.Query(query).ConfigureAwait(false); 373 | } 374 | 375 | /// 376 | /// Query By Partition 377 | /// 378 | /// 379 | /// Without providing the partion this query may not perform well. 380 | /// 381 | /// Return Type 382 | /// Row Key 383 | /// Entities 384 | public virtual async Task> QueryByRow(string rowKey) 385 | where T : ITableEntity, new() 386 | { 387 | var query = new TableQuery().Where(TableQuery.GenerateFilterCondition(RowKey, QueryComparisons.Equal, rowKey)); 388 | return await this.Query(query).ConfigureAwait(false); 389 | } 390 | 391 | /// 392 | /// Query By Partition and Row 393 | /// 394 | /// Return Type 395 | /// Partition Key 396 | /// Row 397 | /// 398 | public virtual async Task QueryByPartitionAndRow(string partitionKey, string rowKey) 399 | where T : ITableEntity, new() 400 | { 401 | var partitionFilter = TableQuery.GenerateFilterCondition(PartitionKey, QueryComparisons.Equal, partitionKey); 402 | var rowFilter = TableQuery.GenerateFilterCondition(RowKey, QueryComparisons.Equal, rowKey); 403 | var filter = TableQuery.CombineFilters(partitionFilter, TableOperators.And, rowFilter); 404 | var query = new TableQuery().Where(filter); 405 | 406 | var result = await this.Query(query).ConfigureAwait(false); 407 | return result.FirstOrDefault(); 408 | } 409 | 410 | /// 411 | /// Query by Expression 412 | /// 413 | /// Filtering is done on client; can be expensive 414 | /// Return Type 415 | /// Predicate 416 | /// Max Result 417 | /// 418 | public virtual async Task> Query(Func predicate, int maxResults = int.MaxValue) 419 | where T : ITableEntity, new() 420 | { 421 | if (null == predicate) 422 | { 423 | throw new ArgumentNullException("predicate"); 424 | } 425 | if (0 >= maxResults) 426 | { 427 | throw new InvalidOperationException("maxResults: must be above 0."); 428 | } 429 | 430 | var items = await this.Query(new TableQuery()).ConfigureAwait(false); 431 | 432 | return items.Where(predicate).Take(maxResults); 433 | } 434 | 435 | /// 436 | /// Query 437 | /// 438 | /// Return Type 439 | /// Table Query 440 | /// Results 441 | public virtual async Task> Query(TableQuery query) 442 | where T : ITableEntity, new() 443 | { 444 | if (null == query) 445 | { 446 | throw new ArgumentNullException("query"); 447 | } 448 | 449 | var entities = new List(); 450 | TableContinuationToken token = null; 451 | 452 | do 453 | { 454 | var queryResult = await this.reference.ExecuteQuerySegmentedAsync(query, token).ConfigureAwait(false); 455 | entities.AddRange(queryResult.Results); 456 | token = queryResult.ContinuationToken; 457 | } 458 | while (null != token); 459 | 460 | return entities; 461 | } 462 | 463 | 464 | #if (!NETCOREAPP1_0 && !NETSTANDARD1_3) 465 | /// 466 | /// CreateQuery 467 | /// 468 | /// Entity type 469 | /// IQueryable 470 | public IQueryable CreateQuery() where TElement : ITableEntity, new() 471 | { 472 | return this.reference.CreateQuery(); 473 | } 474 | #endif 475 | #endregion 476 | 477 | #region Query Dictionary 478 | /// 479 | /// Query By Partition 480 | /// 481 | /// 482 | /// Entities 483 | public virtual async Task>> QueryByPartition(string partitionKey) 484 | { 485 | return await this.Query(new TableQuery().Where(TableQuery.GenerateFilterCondition(TableStorage.PartitionKey, QueryComparisons.Equal, partitionKey))).ConfigureAwait(false); 486 | } 487 | 488 | /// 489 | /// Query By Partition 490 | /// 491 | /// 492 | /// Without providing the partion this query may not perform well. 493 | /// 494 | /// Row Key 495 | /// Entities 496 | public virtual async Task>> QueryByRow(string rowKey) 497 | { 498 | return await this.Query(new TableQuery().Where(TableQuery.GenerateFilterCondition(TableStorage.RowKey, QueryComparisons.Equal, rowKey))).ConfigureAwait(false); 499 | } 500 | 501 | /// 502 | /// Query By Partition and Row 503 | /// 504 | /// Partition Key 505 | /// Row 506 | /// 507 | public virtual async Task> QueryByPartitionAndRow(string partitionKey, string rowKey) 508 | { 509 | var partitionFilter = TableQuery.GenerateFilterCondition(TableStorage.PartitionKey, QueryComparisons.Equal, partitionKey); 510 | var rowFilter = TableQuery.GenerateFilterCondition(TableStorage.RowKey, QueryComparisons.Equal, rowKey); 511 | var filter = TableQuery.CombineFilters(partitionFilter, TableOperators.And, rowFilter); 512 | var query = new TableQuery().Where(filter); 513 | 514 | var result = await this.Query(query).ConfigureAwait(false); 515 | return result.FirstOrDefault(); 516 | } 517 | 518 | /// 519 | /// Generic Query 520 | /// 521 | /// Query 522 | /// Entities 523 | public virtual async Task>> Query(TableQuery query) 524 | { 525 | if (null == query) 526 | { 527 | throw new ArgumentNullException("query"); 528 | } 529 | 530 | var q = new TableQuery() 531 | { 532 | FilterString = query.FilterString, 533 | SelectColumns = query.SelectColumns, 534 | TakeCount = query.TakeCount 535 | }; 536 | 537 | var entities = new List(); 538 | TableContinuationToken token = null; 539 | 540 | do 541 | { 542 | var queryResult = await this.reference.ExecuteQuerySegmentedAsync(q, token).ConfigureAwait(false); 543 | entities.AddRange(queryResult.Results); 544 | token = queryResult.ContinuationToken; 545 | } 546 | while (null != token); 547 | 548 | var results = new List>(); 549 | foreach (var e in entities) 550 | { 551 | var dic = new Dictionary(); 552 | foreach (var p in e.Properties) 553 | { 554 | dic.Add(p.Key, p.Value.PropertyAsObject); 555 | } 556 | dic.Add(TableStorage.PartitionKey, e.PartitionKey); 557 | dic.Add(TableStorage.RowKey, e.RowKey); 558 | dic.Add(TableStorage.ETag, e.ETag); 559 | dic.Add(TableStorage.Timestamp, e.Timestamp.DateTime); 560 | results.Add(dic); 561 | } 562 | 563 | return results; 564 | } 565 | #endregion 566 | 567 | #region Additional Methods 568 | /// 569 | /// Break Entities into batches 570 | /// 571 | /// Entities 572 | /// Batches 573 | public virtual IEnumerable> Batch(IEnumerable entities) 574 | { 575 | return entities.GroupBy(en => en.PartitionKey).SelectMany(e => this.Chunk(e)); 576 | } 577 | 578 | /// 579 | /// Break Entities into batches 580 | /// 581 | /// Entities 582 | /// Batches 583 | public virtual IEnumerable>> Batch(IEnumerable> entities) 584 | { 585 | return entities.GroupBy(en => en[PartitionKey]).SelectMany(e => this.Chunk>(e)); 586 | } 587 | 588 | /// 589 | /// Chunk data into smaller blocks 590 | /// 591 | /// Type 592 | /// Entities 593 | /// Chunks 594 | public virtual IEnumerable> Chunk(IEnumerable entities) 595 | { 596 | return entities.Select((x, i) => new { Index = i, Value = x }).GroupBy(x => x.Index / TableStorage.MaimumxInsertBatch).Select(x => x.Select(v => v.Value)); 597 | } 598 | #endregion 599 | } 600 | } -------------------------------------------------------------------------------- /Azure.Data.Wrappers/Interfaces.cs: -------------------------------------------------------------------------------- 1 | namespace Azure.Data.Wrappers 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | using Microsoft.WindowsAzure.Storage; 9 | using Microsoft.WindowsAzure.Storage.Blob; 10 | using Microsoft.WindowsAzure.Storage.File; 11 | using Microsoft.WindowsAzure.Storage.Queue; 12 | using Microsoft.WindowsAzure.Storage.RetryPolicies; 13 | using Microsoft.WindowsAzure.Storage.Table; 14 | 15 | #region IAccount 16 | /// 17 | /// Azure Storage Account 18 | /// 19 | public interface IStorageAccount 20 | { 21 | #region Properties 22 | /// 23 | /// Cloud Storage Account 24 | /// 25 | CloudStorageAccount Account 26 | { 27 | get; 28 | } 29 | #endregion 30 | 31 | /// 32 | /// Returns a shared access signature for the account. 33 | /// 34 | /// A object specifying the access policy for the shared access signature. 35 | /// A shared access signature, as a URI query string. 36 | string GetSharedAccessSignature(SharedAccessAccountPolicy policy); 37 | } 38 | #endregion 39 | 40 | #region IStorageClient 41 | /// 42 | /// Storage Client Interface 43 | /// 44 | /// 45 | public interface IStorageClient 46 | { 47 | #region Properties 48 | /// 49 | /// Storage Client 50 | /// 51 | T Client 52 | { 53 | get; 54 | } 55 | #endregion 56 | } 57 | #endregion 58 | 59 | #region IStorageReference 60 | /// 61 | /// Storage Reference Interface 62 | /// 63 | /// 64 | public interface IStorageReference 65 | { 66 | #region Properties 67 | /// 68 | /// Storage Reference 69 | /// 70 | T Reference 71 | { 72 | get; 73 | } 74 | #endregion 75 | } 76 | #endregion 77 | 78 | #region ITableStorage 79 | /// 80 | /// Table Storage Interface 81 | /// 82 | public interface ITableStorage : IAzureStorage, IStorageReference, IStorageClient 83 | { 84 | #region Methods 85 | /// 86 | /// Create Table 87 | /// 88 | /// Table Name 89 | Task Create(); 90 | 91 | /// 92 | /// Insert or update the record in table 93 | /// 94 | /// Entity 95 | Task InsertOrReplace(ITableEntity entity); 96 | 97 | /// 98 | /// Insert Batch 99 | /// 100 | /// 101 | Task> Insert(IEnumerable entities); 102 | 103 | /// 104 | /// Insert Or Replace Entity (Dictionary) 105 | /// 106 | /// 107 | /// Specify: PartitionKey, RowKey and ETag 108 | /// 109 | /// Entity 110 | /// Result 111 | Task InsertOrReplace(IDictionary entity); 112 | 113 | /// 114 | /// Insert Batch 115 | /// 116 | /// Entities 117 | Task> Insert(IEnumerable> entities); 118 | 119 | /// 120 | /// Query By Partition 121 | /// 122 | /// 123 | /// 124 | /// 125 | Task> QueryByPartition(string partition) 126 | where T : ITableEntity, new(); 127 | 128 | /// 129 | /// Query By Partition 130 | /// 131 | /// 132 | /// Without providing the partion this query may not perform well. 133 | /// 134 | /// 135 | /// 136 | /// 137 | Task> QueryByRow(string rowKey) 138 | where T : ITableEntity, new(); 139 | 140 | /// 141 | /// Query By Partition 142 | /// 143 | /// 144 | /// 145 | /// 146 | Task QueryByPartitionAndRow(string partitionKey, string rowKey) 147 | where T : ITableEntity, new(); 148 | 149 | /// 150 | /// Query 151 | /// 152 | /// Type 153 | /// Table Query 154 | /// Results 155 | Task> Query(TableQuery query) 156 | where T : ITableEntity, new(); 157 | 158 | /// 159 | /// Query by Expression 160 | /// 161 | /// Table Entity 162 | /// Predicate 163 | /// Max Result 164 | /// 165 | Task> Query(Func predicate, int maxResults = int.MaxValue) 166 | where T : ITableEntity, new(); 167 | 168 | /// 169 | /// Query By Partition 170 | /// 171 | /// 172 | /// Entities 173 | Task>> QueryByPartition(string partitionKey); 174 | 175 | /// 176 | /// Query By Partition 177 | /// 178 | /// 179 | /// Without providing the partion this query may not perform well. 180 | /// 181 | /// Row Key 182 | /// Entities 183 | Task>> QueryByRow(string rowKey); 184 | 185 | /// 186 | /// Query By Partition and Row 187 | /// 188 | /// Partition Key 189 | /// Row 190 | /// 191 | Task> QueryByPartitionAndRow(string partitionKey, string rowKey); 192 | 193 | /// 194 | /// Generic Query 195 | /// 196 | /// Query 197 | /// Entities 198 | Task>> Query(TableQuery query); 199 | 200 | /// 201 | /// Delete By Partition 202 | /// 203 | /// Partition Key 204 | /// Task 205 | Task DeleteByPartition(string partitionKey); 206 | 207 | /// 208 | /// Delete By Row 209 | /// 210 | /// Row Key 211 | /// Task 212 | Task DeleteByRow(string rowKey); 213 | 214 | /// 215 | /// Delete By Partition and Row 216 | /// 217 | /// Partition Key 218 | /// 219 | /// Task 220 | Task DeleteByPartitionAndRow(string partitionKey, string row); 221 | 222 | /// 223 | /// Delete Entity 224 | /// 225 | /// Entity 226 | /// Task 227 | Task Delete(ITableEntity entity); 228 | 229 | /// 230 | /// Delete Entities 231 | /// 232 | /// Entities 233 | /// Table Results 234 | Task> Delete(IEnumerable entities); 235 | 236 | #if (!NETCOREAPP1_0 && !NETSTANDARD1_3) 237 | /// 238 | /// CreateQuery 239 | /// 240 | /// Entity type 241 | /// IQueryable 242 | IQueryable CreateQuery() where TElement : ITableEntity, new(); 243 | #endif 244 | 245 | #endregion 246 | } 247 | #endregion 248 | 249 | #region IContainer 250 | /// 251 | /// Blob Container 252 | /// 253 | public interface IContainer : IAzureStorage, IStorageReference, IStorageClient 254 | { 255 | #region Properties 256 | /// 257 | /// Is Public 258 | /// 259 | bool IsPublic 260 | { 261 | get; 262 | } 263 | #endregion 264 | 265 | #region Methods 266 | /// 267 | /// Blob Exists 268 | /// 269 | /// Blob Name 270 | /// bool 271 | Task Exists(string blobName); 272 | 273 | /// 274 | /// Delete from Blob Storage 275 | /// 276 | /// Blob Name 277 | /// Delete History (Snapshots) 278 | /// Object 279 | Task Delete(string blobName, bool deleteHistory = true); 280 | 281 | /// 282 | /// Save Object as Json to Blob Storage 283 | /// 284 | /// Type 285 | /// Blob Name 286 | /// Object 287 | /// Task 288 | Task Save(string blobName, object obj); 289 | 290 | /// 291 | /// Get Object from Blob Storage 292 | /// 293 | /// Type 294 | /// Blob Name 295 | /// Object 296 | Task Get(string blobName); 297 | 298 | /// 299 | /// Stream Blob 300 | /// 301 | /// Blob Name 302 | /// Stream 303 | Task Stream(string blobName); 304 | 305 | /// 306 | /// Get Reference 307 | /// 308 | /// Blob Name 309 | /// Snapshot time 310 | /// Cloud Blob 311 | CloudBlockBlob GetBlockReference(string blobName, DateTimeOffset? snapshot = null); 312 | 313 | /// 314 | /// Get Reference 315 | /// 316 | /// Blob Name 317 | /// Snapshot time 318 | /// Cloud Blob 319 | CloudPageBlob GetPageReference(string blobName, DateTimeOffset? snapshot = null); 320 | 321 | /// 322 | /// Save Binary Data 323 | /// 324 | /// Blob Name 325 | /// Bytes 326 | /// Content Type 327 | /// Task 328 | Task Save(string blobName, byte[] bytes, string contentType = "application/octet-stream"); 329 | 330 | /// 331 | /// Save Text 332 | /// 333 | /// Blob Name 334 | /// Text 335 | /// Content Type 336 | /// Task 337 | Task Save(string blobName, string text, string contentType = "text/plain"); 338 | 339 | /// 340 | /// Get Binary Data 341 | /// 342 | /// Blob Name 343 | /// Bytes 344 | Task Get(string blobName); 345 | 346 | /// 347 | /// Get Bytes 348 | /// 349 | /// Blob Name 350 | /// Text 351 | Task GetText(string blobName); 352 | 353 | /// 354 | /// Blob Properties 355 | /// 356 | /// Blob Name 357 | /// Blob Container Properties 358 | Task Properties(string blobName); 359 | 360 | /// 361 | /// Set Cache Control 362 | /// 363 | /// Blob Name 364 | /// Cache Duration (Default 1 year) 365 | /// Task 366 | Task SetCacheControl(string blobName, uint cacheDuration = Container.DefaultCacheDuration); 367 | 368 | /// 369 | /// List Blobs 370 | /// 371 | /// Prefix 372 | /// Use Flat Blob Listing 373 | /// Blobs 374 | Task> List(string prefix = null, bool useFlatBlobListing = true, BlobListingDetails details = BlobListingDetails.All, int? maxResults = int.MaxValue); 375 | 376 | /// 377 | /// Create Snapshot 378 | /// 379 | /// Blob Name 380 | /// Task 381 | Task Snapshot(string blobName); 382 | 383 | /// 384 | /// Copy from, to seperate container/blob 385 | /// 386 | /// From 387 | /// Target 388 | /// To 389 | /// Blob Uri 390 | Task Copy(string from, IContainer target, string to); 391 | 392 | /// 393 | /// Copy from, to seperate container/blob 394 | /// 395 | /// From 396 | /// Target 397 | /// To 398 | /// Blob Uri 399 | Task Copy(string from, string target, string to); 400 | 401 | /// 402 | /// Copy From Blob to Blob 403 | /// 404 | /// From 405 | /// To 406 | /// Blob Uri 407 | Task Copy(string from, string to); 408 | 409 | /// 410 | /// Returns a shared access signature for the container. 411 | /// 412 | /// the access policy for the shared access signature. 413 | /// A container-level access policy 414 | /// A shared access signature, as a URI query string. 415 | string GetSharedAccessSignature(SharedAccessBlobPolicy policy, string groupPolicyIdentifier = null); 416 | #endregion 417 | } 418 | #endregion 419 | 420 | #region IQueueObject 421 | /// 422 | /// Queue Object Interface 423 | /// 424 | public interface IQueueObject 425 | { 426 | #region Methods 427 | /// 428 | /// Save Specific Message to Queue 429 | /// 430 | /// Object 431 | /// Task 432 | Task Send(object obj); 433 | 434 | /// 435 | /// Send a generic Object 436 | /// 437 | /// 438 | /// 439 | /// 440 | Task SendAsync(T message); 441 | 442 | /// 443 | /// Get Cloud Queue Message 444 | /// 445 | /// Message 446 | Task GetAsync(); 447 | 448 | /// 449 | /// Get Cloud Queue Message(s) 450 | /// 451 | /// 452 | /// number of messages to get. Default is 5 453 | /// Message 454 | Task> GetManyAsync(int count = 5); 455 | 456 | /// 457 | /// Peeks the queue 458 | /// 459 | /// 460 | /// number of messages to peek with a default value of 1 461 | /// Messages 462 | Task> PeekAsync(int count = 1); 463 | #endregion 464 | } 465 | #endregion 466 | 467 | #region IQueue 468 | /// 469 | /// IQueue 470 | /// 471 | /// 472 | public interface IQueue 473 | { 474 | #region Methods 475 | /// 476 | /// Get Cloud Queue Message 477 | /// 478 | /// Message 479 | Task Get(); 480 | 481 | /// 482 | /// Delete Message from Queue 483 | /// 484 | /// Message 485 | /// Task 486 | Task Delete(T message); 487 | 488 | /// 489 | /// Save Message to Queue 490 | /// 491 | /// Message 492 | /// Task 493 | Task Send(T message); 494 | 495 | /// 496 | /// Clear the contents of the queue 497 | /// 498 | /// 499 | Task ClearAsync(); 500 | 501 | /// 502 | /// Peeks the queue 503 | /// 504 | /// number of messages to peek with a default value of 1 505 | /// Messages 506 | Task> PeekAsync(int count = 1); 507 | #endregion 508 | } 509 | #endregion 510 | 511 | #region IStorageQueue 512 | /// 513 | /// Storage Queue Interface 514 | /// 515 | public interface IStorageQueue : IQueue, IQueueObject, IAzureStorage, IStorageReference, IStorageClient, IQueueCount 516 | { 517 | #region Methods 518 | /// 519 | /// Get Many Cloud Queue Message 520 | /// 521 | /// Messages 522 | Task> GetMany(int messageCount = 5); 523 | #endregion 524 | } 525 | 526 | public interface IStorageQueue : IQueue, IAzureStorage, IStorageReference, IStorageClient, IQueueCount 527 | { 528 | #region Methods 529 | /// 530 | /// Get Many Cloud Queue Message 531 | /// 532 | /// Messages 533 | Task> GetMany(int messageCount = 5); 534 | #endregion 535 | } 536 | #endregion 537 | 538 | #region IQueueCount 539 | /// 540 | /// Queue Count 541 | /// 542 | public interface IQueueCount 543 | { 544 | #region Methods 545 | /// 546 | /// Approixmate Message Count 547 | /// 548 | /// Message Count 549 | Task ApproixmateMessageCount(); 550 | #endregion 551 | } 552 | #endregion 553 | 554 | #region IAzureStorage 555 | /// 556 | /// Azure Storage 557 | /// 558 | public interface IAzureStorage 559 | { 560 | #region Properties 561 | /// 562 | /// Name 563 | /// 564 | string Name 565 | { 566 | get; 567 | } 568 | #endregion 569 | 570 | #region Methods 571 | /// 572 | /// Create If Not Exists 573 | /// 574 | /// 575 | Task CreateIfNotExists(); 576 | 577 | /// 578 | /// Delete Item 579 | /// 580 | /// Task 581 | Task Delete(); 582 | #endregion 583 | } 584 | #endregion 585 | 586 | #region IProcessor 587 | /// 588 | /// IProcessor 589 | /// 590 | public interface IProcessor 591 | { 592 | #region Methods 593 | /// 594 | /// Process Data 595 | /// 596 | /// Data to Process 597 | /// Successful 598 | Task Process(T data); 599 | #endregion 600 | } 601 | #endregion 602 | 603 | #region IPoller 604 | /// 605 | /// Store Poller Interface 606 | /// 607 | /// Dequeue Type 608 | public interface IPoller 609 | { 610 | #region Methods 611 | /// 612 | /// Poll for Queued Message 613 | /// 614 | /// Queued Item 615 | Task> Poll(); 616 | 617 | /// 618 | /// Poll for Queued Message 619 | /// 620 | /// Queued Item 621 | Task>> PollMany(int messageCount = 5); 622 | #endregion 623 | } 624 | #endregion 625 | 626 | #region IStorageQueuePoller 627 | /// 628 | /// Storage Queue Poller Interface 629 | /// 630 | /// Dequeue Type 631 | public interface IStorageQueuePoller : IPoller 632 | { 633 | #region Properties 634 | /// 635 | /// Storage Queue 636 | /// 637 | IStorageQueue Queue 638 | { 639 | get; 640 | } 641 | #endregion 642 | } 643 | #endregion 644 | 645 | #region IQueued 646 | /// 647 | /// IQueued 648 | /// 649 | /// 650 | public interface IQueued 651 | { 652 | #region Methods 653 | /// 654 | /// Delete Message 655 | /// 656 | /// Task 657 | Task Complete(); 658 | 659 | /// 660 | /// Abandon Message 661 | /// 662 | /// Task 663 | Task Abandon(); 664 | 665 | /// 666 | /// Data 667 | /// 668 | /// Data 669 | Task Data(); 670 | #endregion 671 | } 672 | #endregion 673 | 674 | #region IAzureStorageResources 675 | /// 676 | /// Azure Storage Resources Interface 677 | /// 678 | public interface IAzureStorageResources 679 | { 680 | #region Methods 681 | /// 682 | /// List Table Names 683 | /// 684 | /// Table Names 685 | Task> TableNames(); 686 | 687 | /// 688 | /// List Tables 689 | /// 690 | /// Tables 691 | Task> Tables(); 692 | 693 | /// 694 | /// List Container Names 695 | /// 696 | /// Container Names 697 | Task> ContainerNames(); 698 | 699 | /// 700 | /// List Containers 701 | /// 702 | /// Containers 703 | Task> Containers(); 704 | 705 | /// 706 | /// List Queue Names 707 | /// 708 | /// Queue Names 709 | Task> QueueNames(); 710 | 711 | /// 712 | /// List Queues 713 | /// 714 | /// Queues 715 | Task> Queues(); 716 | #endregion 717 | } 718 | #endregion 719 | 720 | #region IFileShare 721 | /// 722 | /// File Share Interface 723 | /// 724 | public interface IFileShare : IStorageReference, IStorageClient 725 | { 726 | } 727 | #endregion 728 | 729 | #region QueueShardSender 730 | /// 731 | /// Queue Shard Sender 732 | /// 733 | /// 734 | public interface IQueueShardSender 735 | { 736 | #region Properties 737 | /// 738 | /// Queues 739 | /// 740 | IReadOnlyCollection Queues 741 | { 742 | get; 743 | } 744 | #endregion 745 | 746 | #region Methods 747 | /// 748 | /// Queue Message to shard, 0 means at random 749 | /// 750 | /// message 751 | /// Shard Target 752 | /// Task 753 | Task Save(object obj, byte shardTarget = 0); 754 | 755 | /// 756 | /// Create all queues 757 | /// 758 | /// 759 | Task CreateIfNotExists(); 760 | 761 | /// 762 | /// Delete all queues 763 | /// 764 | /// Task 765 | Task Delete(); 766 | #endregion 767 | } 768 | #endregion 769 | 770 | #region IAzureStorageFactory 771 | public interface IAzureStorageFactory 772 | { 773 | IStorageAccount GetAccount(string accountName, string key, bool useHttps); 774 | IStorageAccount GetAccount(string connectionString); 775 | IStorageQueue GetAzureQueue(IStorageAccount storageAccount, string queueName, int visibilityTimeoutInMS = 300000); 776 | ITableStorage GetAzureTable(IStorageAccount storageAccount, string tableName); 777 | IContainer GetBlobFileContainer(IStorageAccount storageAccount, string containerName, bool isPublic = false, LocationMode location = LocationMode.PrimaryThenSecondary); 778 | 779 | } 780 | #endregion 781 | } --------------------------------------------------------------------------------