This commit is contained in:
Коммит
a69be13462
|
@ -0,0 +1,22 @@
|
|||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
||||
|
||||
# Custom for Visual Studio
|
||||
*.cs diff=csharp
|
||||
*.sln merge=union
|
||||
*.csproj merge=union
|
||||
*.vbproj merge=union
|
||||
*.fsproj merge=union
|
||||
*.dbproj merge=union
|
||||
|
||||
# Standard to msysgit
|
||||
*.doc diff=astextplain
|
||||
*.DOC diff=astextplain
|
||||
*.docx diff=astextplain
|
||||
*.DOCX diff=astextplain
|
||||
*.dot diff=astextplain
|
||||
*.DOT diff=astextplain
|
||||
*.pdf diff=astextplain
|
||||
*.PDF diff=astextplain
|
||||
*.rtf diff=astextplain
|
||||
*.RTF diff=astextplain
|
|
@ -0,0 +1,106 @@
|
|||
namespace King.Azure.Integration.Test.Data
|
||||
{
|
||||
using King.Azure.Data;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
[TestFixture]
|
||||
public class AzureStorageResourcesTests
|
||||
{
|
||||
private readonly string ConnectionString = "UseDevelopmentStorage=true;";
|
||||
|
||||
[Test]
|
||||
public async Task TableNames()
|
||||
{
|
||||
var name = 'a' + Guid.NewGuid().ToString().ToLowerInvariant().Replace('-', 'a');
|
||||
var storage = new TableStorage(name, ConnectionString);
|
||||
var created = await storage.CreateIfNotExists();
|
||||
|
||||
var resources = new AzureStorageResources(ConnectionString);
|
||||
var tables = await resources.TableNames();
|
||||
|
||||
Assert.IsTrue(tables.Contains(name));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Tables()
|
||||
{
|
||||
var name = 'a' + Guid.NewGuid().ToString().ToLowerInvariant().Replace('-', 'a');
|
||||
var storage = new TableStorage(name, ConnectionString);
|
||||
var created = await storage.CreateIfNotExists();
|
||||
|
||||
var resources = new AzureStorageResources(ConnectionString);
|
||||
var tables = await resources.Tables();
|
||||
|
||||
var exists = (from t in tables
|
||||
where t.Name == name
|
||||
select true).FirstOrDefault();
|
||||
|
||||
Assert.IsTrue(exists);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task QueueNames()
|
||||
{
|
||||
var name = 'a' + Guid.NewGuid().ToString().ToLowerInvariant().Replace('-', 'a');
|
||||
var storage = new StorageQueue(name, ConnectionString);
|
||||
var created = await storage.CreateIfNotExists();
|
||||
|
||||
var resources = new AzureStorageResources(ConnectionString);
|
||||
var queues = await resources.QueueNames();
|
||||
|
||||
Assert.IsTrue(queues.Contains(name));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Queues()
|
||||
{
|
||||
var name = 'a' + Guid.NewGuid().ToString().ToLowerInvariant().Replace('-', 'a');
|
||||
var storage = new StorageQueue(name, ConnectionString);
|
||||
var created = await storage.CreateIfNotExists();
|
||||
|
||||
var resources = new AzureStorageResources(ConnectionString);
|
||||
var queues = await resources.Queues();
|
||||
|
||||
var exists = (from q in queues
|
||||
where q.Name == name
|
||||
select true).FirstOrDefault();
|
||||
|
||||
Assert.IsTrue(exists);
|
||||
|
||||
await storage.Delete();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task ContainerNames()
|
||||
{
|
||||
var name = 'a' + Guid.NewGuid().ToString().ToLowerInvariant().Replace('-', 'a');
|
||||
var storage = new Container(name, ConnectionString);
|
||||
var created = await storage.CreateIfNotExists();
|
||||
|
||||
var resources = new AzureStorageResources(ConnectionString);
|
||||
var containers = await resources.ContainerNames();
|
||||
|
||||
Assert.IsTrue(containers.Contains(name));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Containers()
|
||||
{
|
||||
var name = 'a' + Guid.NewGuid().ToString().ToLowerInvariant().Replace('-', 'a');
|
||||
var storage = new Container(name, ConnectionString);
|
||||
var created = await storage.CreateIfNotExists();
|
||||
|
||||
var resources = new AzureStorageResources(ConnectionString);
|
||||
var containers = await resources.Containers();
|
||||
|
||||
var exists = (from c in containers
|
||||
where c.Name == name
|
||||
select true).FirstOrDefault();
|
||||
|
||||
Assert.IsTrue(exists);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,549 @@
|
|||
namespace King.Service.Integration
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using King.Azure.Data;
|
||||
using Microsoft.WindowsAzure.Storage;
|
||||
using Microsoft.WindowsAzure.Storage.Blob;
|
||||
using NUnit.Framework;
|
||||
|
||||
[TestFixture]
|
||||
public class ContainerTests
|
||||
{
|
||||
private readonly string ConnectionString = "UseDevelopmentStorage=true;";
|
||||
private readonly string ContainerName = 'a' + Guid.NewGuid().ToString().Replace("-", "");
|
||||
|
||||
#region Helper
|
||||
private class Helper
|
||||
{
|
||||
public Guid Id
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
var storage = new Container(ContainerName, ConnectionString);
|
||||
storage.CreateIfNotExists().Wait();
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
var storage = new Container(ContainerName, ConnectionString);
|
||||
storage.Delete().Wait();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task ConstructorAccount()
|
||||
{
|
||||
var name = 'a' + Guid.NewGuid().ToString().ToLowerInvariant().Replace('-', 'a');
|
||||
var account = CloudStorageAccount.Parse(ConnectionString);
|
||||
var storage = new Container(name, account);
|
||||
var created = await storage.CreateIfNotExists();
|
||||
|
||||
Assert.IsTrue(created);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task CreateIfNotExists()
|
||||
{
|
||||
var name = 'a' + Guid.NewGuid().ToString().ToLowerInvariant().Replace('-', 'a');
|
||||
var storage = new Container(name, ConnectionString);
|
||||
var created = await storage.CreateIfNotExists();
|
||||
|
||||
Assert.IsTrue(created);
|
||||
|
||||
var blobClient = storage.Account.CreateCloudBlobClient();
|
||||
var container = blobClient.GetContainerReference(name);
|
||||
var permissions = await container.GetPermissionsAsync();
|
||||
Assert.AreEqual(BlobContainerPublicAccessType.Off, permissions.PublicAccess);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task CreateIfNotExistsPublic()
|
||||
{
|
||||
var name = 'a' + Guid.NewGuid().ToString().ToLowerInvariant().Replace('-', 'a');
|
||||
var storage = new Container(name, ConnectionString, true);
|
||||
var created = await storage.CreateIfNotExists();
|
||||
|
||||
Assert.IsTrue(created);
|
||||
|
||||
var blobClient = storage.Account.CreateCloudBlobClient();
|
||||
var container = blobClient.GetContainerReference(name);
|
||||
var permissions = await container.GetPermissionsAsync();
|
||||
Assert.AreEqual(BlobContainerPublicAccessType.Blob, permissions.PublicAccess);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Exists()
|
||||
{
|
||||
var blobName = Guid.NewGuid().ToString();
|
||||
var storage = new Container(ContainerName, ConnectionString);
|
||||
await storage.Save(blobName, Guid.NewGuid());
|
||||
var exists = await storage.Exists(blobName);
|
||||
|
||||
Assert.IsTrue(exists);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task ExistsNo()
|
||||
{
|
||||
var blobName = Guid.NewGuid().ToString();
|
||||
var storage = new Container(ContainerName, ConnectionString);
|
||||
|
||||
var exists = await storage.Exists(blobName);
|
||||
|
||||
Assert.IsFalse(exists);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task GetBlockReference()
|
||||
{
|
||||
var name = string.Format("{0}.bin", Guid.NewGuid());
|
||||
var storage = new Container(ContainerName, ConnectionString);
|
||||
await storage.Save(name, new Helper());
|
||||
|
||||
var block = storage.GetBlockReference(name);
|
||||
Assert.IsNotNull(block);
|
||||
Assert.IsTrue(block.Exists());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task GetPageReference()
|
||||
{
|
||||
var random = new Random();
|
||||
var bytes = new byte[1024];
|
||||
random.NextBytes(bytes);
|
||||
|
||||
var name = string.Format("{0}.bin", Guid.NewGuid());
|
||||
var storage = new Container(ContainerName, ConnectionString);
|
||||
var blob = storage.Reference.GetPageBlobReference(name);
|
||||
await blob.CreateAsync(1024);
|
||||
await blob.UploadFromByteArrayAsync(bytes, 0, bytes.Length);
|
||||
|
||||
var page = storage.GetPageReference(name);
|
||||
Assert.IsNotNull(page);
|
||||
Assert.IsTrue(page.Exists());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task RoundTripObject()
|
||||
{
|
||||
var helper = new Helper()
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
};
|
||||
|
||||
var blobName = Guid.NewGuid().ToString();
|
||||
var storage = new Container(ContainerName, ConnectionString);
|
||||
|
||||
await storage.Save(blobName, helper);
|
||||
var returned = await storage.Get<Helper>(blobName);
|
||||
|
||||
Assert.IsNotNull(returned);
|
||||
Assert.AreEqual(helper.Id, returned.Id);
|
||||
|
||||
var properties = await storage.Properties(blobName);
|
||||
Assert.IsNotNull(properties);
|
||||
Assert.AreEqual("application/json", properties.ContentType);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task RoundTripText()
|
||||
{
|
||||
var data = Guid.NewGuid().ToString();
|
||||
var blobName = Guid.NewGuid().ToString();
|
||||
var storage = new Container(ContainerName, ConnectionString);
|
||||
|
||||
await storage.Save(blobName, data);
|
||||
var returned = await storage.GetText(blobName);
|
||||
|
||||
Assert.IsNotNull(returned);
|
||||
Assert.AreEqual(data, returned);
|
||||
|
||||
var properties = await storage.Properties(blobName);
|
||||
Assert.IsNotNull(properties);
|
||||
Assert.AreEqual("text/plain", properties.ContentType);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task JsonContentType()
|
||||
{
|
||||
var helper = new Helper()
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
};
|
||||
|
||||
var blobName = Guid.NewGuid().ToString();
|
||||
var storage = new Container(ContainerName, ConnectionString);
|
||||
|
||||
await storage.Save(blobName, helper);
|
||||
var returned = await storage.Properties(blobName);
|
||||
|
||||
Assert.IsNotNull(returned);
|
||||
Assert.AreEqual("application/json", returned.ContentType);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task RoundTripBytes()
|
||||
{
|
||||
var random = new Random();
|
||||
var bytes = new byte[1024];
|
||||
random.NextBytes(bytes);
|
||||
|
||||
var blobName = Guid.NewGuid().ToString();
|
||||
var storage = new Container(ContainerName, ConnectionString);
|
||||
|
||||
await storage.Save(blobName, bytes);
|
||||
var returned = await storage.Get(blobName);
|
||||
|
||||
Assert.IsNotNull(returned);
|
||||
Assert.AreEqual(bytes.Length, returned.Length);
|
||||
Assert.AreEqual(bytes, returned);
|
||||
|
||||
var properties = await storage.Properties(blobName);
|
||||
Assert.IsNotNull(properties);
|
||||
Assert.AreEqual("application/octet-stream", properties.ContentType);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task RoundTripStream()
|
||||
{
|
||||
var random = new Random();
|
||||
var bytes = new byte[1024];
|
||||
random.NextBytes(bytes);
|
||||
|
||||
var blobName = Guid.NewGuid().ToString();
|
||||
var storage = new Container(ContainerName, ConnectionString);
|
||||
|
||||
await storage.Save(blobName, bytes);
|
||||
using (var returned = await storage.Stream(blobName) as MemoryStream)
|
||||
{
|
||||
var stored = returned.ToArray();
|
||||
|
||||
Assert.IsNotNull(stored);
|
||||
Assert.AreEqual(bytes.Length, stored.Length);
|
||||
Assert.AreEqual(bytes, stored);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task BytesDefaultContentType()
|
||||
{
|
||||
var random = new Random();
|
||||
var bytes = new byte[1024];
|
||||
random.NextBytes(bytes);
|
||||
|
||||
var blobName = Guid.NewGuid().ToString();
|
||||
var storage = new Container(ContainerName, ConnectionString);
|
||||
|
||||
await storage.Save(blobName, bytes);
|
||||
var returned = await storage.Properties(blobName);
|
||||
|
||||
Assert.IsNotNull(returned);
|
||||
Assert.AreEqual(bytes.Length, returned.Length);
|
||||
Assert.AreEqual("application/octet-stream", returned.ContentType);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task BytesContentType()
|
||||
{
|
||||
var random = new Random();
|
||||
var bytes = new byte[1024];
|
||||
random.NextBytes(bytes);
|
||||
|
||||
var blobName = Guid.NewGuid().ToString();
|
||||
var storage = new Container(ContainerName, ConnectionString);
|
||||
|
||||
await storage.Save(blobName, bytes, "application/pdf");
|
||||
var returned = await storage.Properties(blobName);
|
||||
|
||||
Assert.IsNotNull(returned);
|
||||
Assert.AreEqual(bytes.Length, returned.Length);
|
||||
Assert.AreEqual("application/pdf", returned.ContentType);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Delete()
|
||||
{
|
||||
var helper = new Helper()
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
};
|
||||
|
||||
var blobName = Guid.NewGuid().ToString();
|
||||
var storage = new Container(ContainerName, ConnectionString);
|
||||
|
||||
await storage.Save(blobName, helper);
|
||||
await storage.Delete(blobName);
|
||||
|
||||
Assert.That(() => storage.Get<Helper>(blobName), Throws.TypeOf<StorageException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task List()
|
||||
{
|
||||
var random = new Random();
|
||||
var bytes = new byte[16];
|
||||
random.NextBytes(bytes);
|
||||
var count = random.Next(1, 32);
|
||||
var storage = new Container(ContainerName, ConnectionString);
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var blobName = Guid.NewGuid().ToString();
|
||||
await storage.Save(blobName, bytes);
|
||||
}
|
||||
|
||||
var blobs = await storage.List();
|
||||
Assert.AreEqual(count, blobs.Count());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task SnapShotPageBlob()
|
||||
{
|
||||
var random = new Random();
|
||||
var bytes = new byte[1024];
|
||||
random.NextBytes(bytes);
|
||||
|
||||
var name = string.Format("{0}.bin", Guid.NewGuid());
|
||||
var storage = new Container(ContainerName, ConnectionString);
|
||||
var blob = storage.Reference.GetPageBlobReference(name);
|
||||
await blob.CreateAsync(1024);
|
||||
await blob.UploadFromByteArrayAsync(bytes, 0, bytes.Length);
|
||||
|
||||
var snapshot = await storage.Snapshot(name);
|
||||
Assert.IsTrue(snapshot.IsSnapshot);
|
||||
|
||||
var returned = storage.GetPageReference(snapshot.Name, snapshot.SnapshotTime);
|
||||
Assert.IsNotNull(returned);
|
||||
Assert.IsTrue(returned.IsSnapshot);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task SnapShotBlockBlob()
|
||||
{
|
||||
var random = new Random();
|
||||
var bytes = new byte[16];
|
||||
random.NextBytes(bytes);
|
||||
|
||||
var name = string.Format("{0}.bin", Guid.NewGuid());
|
||||
var storage = new Container(ContainerName, ConnectionString);
|
||||
await storage.Save(name, bytes);
|
||||
|
||||
var snapshot = await storage.Snapshot(name);
|
||||
Assert.IsTrue(snapshot.IsSnapshot);
|
||||
|
||||
var returned = storage.GetPageReference(snapshot.Name, snapshot.SnapshotTime);
|
||||
Assert.IsNotNull(returned);
|
||||
Assert.IsTrue(returned.IsSnapshot);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task SnapShoAndDelete()
|
||||
{
|
||||
var random = new Random();
|
||||
var bytes = new byte[16];
|
||||
random.NextBytes(bytes);
|
||||
|
||||
var name = string.Format("{0}.bin", Guid.NewGuid());
|
||||
var storage = new Container(ContainerName, ConnectionString);
|
||||
await storage.Save(name, bytes);
|
||||
|
||||
var snapshot = await storage.Snapshot(name);
|
||||
await storage.Delete(name);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task SnapShoAndDeleteSafe()
|
||||
{
|
||||
var random = new Random();
|
||||
var bytes = new byte[16];
|
||||
random.NextBytes(bytes);
|
||||
|
||||
var name = string.Format("{0}.bin", Guid.NewGuid());
|
||||
var storage = new Container(ContainerName, ConnectionString);
|
||||
await storage.Save(name, bytes);
|
||||
|
||||
var snapshot = await storage.Snapshot(name);
|
||||
|
||||
Assert.That(() => storage.Delete(name, false), Throws.TypeOf<StorageException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task SnapshotNonExistant()
|
||||
{
|
||||
var blob = Guid.NewGuid().ToString();
|
||||
var storage = new Container(ContainerName, ConnectionString);
|
||||
Assert.IsNull(await storage.Snapshot(blob));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task DontLoseContentType()
|
||||
{
|
||||
var cache = "public, max-age=31536000";
|
||||
var contentType = "text/guid";
|
||||
var blobName = Guid.NewGuid().ToString();
|
||||
var storage = new Container(ContainerName, ConnectionString);
|
||||
|
||||
await storage.Save(blobName, Guid.NewGuid().ToString(), contentType);
|
||||
await storage.SetCacheControl(blobName);
|
||||
var returned = await storage.Properties(blobName);
|
||||
|
||||
Assert.IsNotNull(returned);
|
||||
Assert.AreEqual(cache, returned.CacheControl);
|
||||
Assert.AreEqual(contentType, returned.ContentType);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task DontLoseCacheControl()
|
||||
{
|
||||
var cache = "public, max-age=31536000";
|
||||
var contentType = "text/guid";
|
||||
var blobName = Guid.NewGuid().ToString();
|
||||
var storage = new Container(ContainerName, ConnectionString);
|
||||
|
||||
await storage.Save(blobName, Guid.NewGuid().ToString(), contentType);
|
||||
await storage.SetCacheControl(blobName);
|
||||
await storage.Save(blobName, Guid.NewGuid().ToString(), contentType);
|
||||
var returned = await storage.Properties(blobName);
|
||||
|
||||
Assert.IsNotNull(returned);
|
||||
Assert.AreEqual(cache, returned.CacheControl);
|
||||
Assert.AreEqual(contentType, returned.ContentType);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task SetCacheControlDefault()
|
||||
{
|
||||
var cache = "public, max-age=31536000";
|
||||
var blobName = Guid.NewGuid().ToString();
|
||||
var storage = new Container(ContainerName, ConnectionString);
|
||||
|
||||
await storage.Save(blobName, Guid.NewGuid().ToString());
|
||||
await storage.SetCacheControl(blobName);
|
||||
var returned = await storage.Properties(blobName);
|
||||
|
||||
Assert.IsNotNull(returned);
|
||||
Assert.AreEqual(cache, returned.CacheControl);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task SetCacheControlZero()
|
||||
{
|
||||
var cache = "public, max-age=31536000";
|
||||
var blobName = Guid.NewGuid().ToString();
|
||||
var storage = new Container(ContainerName, ConnectionString);
|
||||
|
||||
await storage.Save(blobName, Guid.NewGuid().ToString());
|
||||
await storage.SetCacheControl(blobName, 0);
|
||||
var returned = await storage.Properties(blobName);
|
||||
|
||||
Assert.IsNotNull(returned);
|
||||
Assert.AreEqual(cache, returned.CacheControl);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task SetCacheControl()
|
||||
{
|
||||
var cache = "public, max-age=1000";
|
||||
var blobName = Guid.NewGuid().ToString();
|
||||
var storage = new Container(ContainerName, ConnectionString);
|
||||
|
||||
await storage.Save(blobName, Guid.NewGuid().ToString());
|
||||
await storage.SetCacheControl(blobName, 1000);
|
||||
var returned = await storage.Properties(blobName);
|
||||
|
||||
Assert.IsNotNull(returned);
|
||||
Assert.AreEqual(cache, returned.CacheControl);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task CopyToFrom()
|
||||
{
|
||||
var random = new Random();
|
||||
var bytes = new byte[16];
|
||||
random.NextBytes(bytes);
|
||||
|
||||
var from = string.Format("{0}.bin", Guid.NewGuid());
|
||||
var to = string.Format("{0}.bin", Guid.NewGuid());
|
||||
var storage = new Container(ContainerName, ConnectionString);
|
||||
await storage.Save(from, bytes);
|
||||
|
||||
var uri = await storage.Copy(from, to);
|
||||
|
||||
Assert.IsNotNull(uri);
|
||||
|
||||
var exists = await storage.Exists(to);
|
||||
var data = await storage.Get(to);
|
||||
Assert.AreEqual(bytes, data);
|
||||
|
||||
await storage.Delete(from);
|
||||
await storage.Delete(to);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Copy()
|
||||
{
|
||||
var random = new Random();
|
||||
var bytes = new byte[16];
|
||||
random.NextBytes(bytes);
|
||||
|
||||
var toContainerName = 'a' + Guid.NewGuid().ToString().Replace("-", string.Empty);
|
||||
var toContainer = new Container(toContainerName, ConnectionString);
|
||||
await toContainer.CreateIfNotExists();
|
||||
|
||||
var from = string.Format("{0}.bin", Guid.NewGuid());
|
||||
var to = string.Format("{0}.bin", Guid.NewGuid());
|
||||
var storage = new Container(ContainerName, ConnectionString);
|
||||
await storage.Save(from, bytes);
|
||||
|
||||
var uri = await storage.Copy(from, toContainer, to);
|
||||
|
||||
Assert.IsNotNull(uri);
|
||||
|
||||
var exists = await toContainer.Exists(to);
|
||||
var data = await toContainer.Get(to);
|
||||
Assert.AreEqual(bytes, data);
|
||||
|
||||
await storage.Delete(from);
|
||||
await toContainer.Delete(to);
|
||||
await toContainer.Delete();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task CopyContainerName()
|
||||
{
|
||||
var random = new Random();
|
||||
var bytes = new byte[16];
|
||||
random.NextBytes(bytes);
|
||||
|
||||
var toContainerName = 'a' + Guid.NewGuid().ToString().Replace("-", string.Empty);
|
||||
var toContainer = new Container(toContainerName, ConnectionString);
|
||||
await toContainer.CreateIfNotExists();
|
||||
|
||||
var from = string.Format("{0}.bin", Guid.NewGuid());
|
||||
var to = string.Format("{0}.bin", Guid.NewGuid());
|
||||
var storage = new Container(ContainerName, ConnectionString);
|
||||
await storage.Save(from, bytes);
|
||||
|
||||
var uri = await storage.Copy(from, toContainerName, to);
|
||||
|
||||
Assert.IsNotNull(uri);
|
||||
|
||||
var exists = await toContainer.Exists(to);
|
||||
var data = await toContainer.Get(to);
|
||||
Assert.AreEqual(bytes, data);
|
||||
|
||||
await storage.Delete(from);
|
||||
await toContainer.Delete(to);
|
||||
await toContainer.Delete();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
namespace King.Azure.Integration.Test.Data
|
||||
{
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using King.Azure.Data;
|
||||
using NUnit.Framework;
|
||||
|
||||
[TestFixture]
|
||||
public class FileShareTests
|
||||
{
|
||||
private const string ConnectionString = "DefaultEndpointsProtocol=https;AccountName=kingdottest;AccountKey=DESdtrm9Rj+pOzS1XGIvnmuzMJN+mvOAlwy75CWJxWKPYmVNQyuSwhUG/UcAzb3/Q1c+pHdMxddvXBzDuwevxQ==;FileEndpoint=https://kingdottest.file.core.windows.net/";
|
||||
|
||||
[Test]
|
||||
public async Task CreateIfNotExists()
|
||||
{
|
||||
var random = new Random();
|
||||
var storage = new FileShare(string.Format("a{0}b", random.Next()), ConnectionString);
|
||||
var created = await storage.CreateIfNotExists();
|
||||
|
||||
Assert.IsTrue(created);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,163 @@
|
|||
namespace King.Service.Integration
|
||||
{
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using King.Azure.Data;
|
||||
using Microsoft.WindowsAzure.Storage;
|
||||
using Microsoft.WindowsAzure.Storage.Queue;
|
||||
using Newtonsoft.Json;
|
||||
using NUnit.Framework;
|
||||
|
||||
[TestFixture]
|
||||
public class QueueTests
|
||||
{
|
||||
private const string ConnectionString = "UseDevelopmentStorage=true;";
|
||||
private const string QueueName = "testing";
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
var storage = new StorageQueue(QueueName, ConnectionString);
|
||||
storage.CreateIfNotExists().Wait();
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
var storage = new StorageQueue(QueueName, ConnectionString);
|
||||
storage.Delete().Wait();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task CreateIfNotExists()
|
||||
{
|
||||
var name = 'a' + Guid.NewGuid().ToString().ToLowerInvariant().Replace('-', 'a');
|
||||
var storage = new StorageQueue(name, ConnectionString);
|
||||
var created = await storage.CreateIfNotExists();
|
||||
|
||||
Assert.IsTrue(created);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task ConstructorAccount()
|
||||
{
|
||||
var name = 'a' + Guid.NewGuid().ToString().ToLowerInvariant().Replace('-', 'a');
|
||||
var account = CloudStorageAccount.Parse(ConnectionString);
|
||||
var storage = new StorageQueue(name, account, TimeSpan.FromSeconds(34));
|
||||
var created = await storage.CreateIfNotExists();
|
||||
|
||||
Assert.IsTrue(created);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task RoundTrip()
|
||||
{
|
||||
var storage = new StorageQueue(QueueName, ConnectionString);
|
||||
|
||||
var msg = new CloudQueueMessage(Guid.NewGuid().ToByteArray());
|
||||
await storage.Send(msg);
|
||||
var returned = await storage.Get();
|
||||
|
||||
Assert.AreEqual(msg.AsBytes, returned.AsBytes);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task RoundTripMsgAsObj()
|
||||
{
|
||||
var storage = new StorageQueue(QueueName, ConnectionString);
|
||||
|
||||
var msg = new CloudQueueMessage(Guid.NewGuid().ToByteArray());
|
||||
await storage.Send((object)msg);
|
||||
var returned = await storage.Get();
|
||||
|
||||
Assert.AreEqual(msg.AsBytes, returned.AsBytes);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task RoundTripObject()
|
||||
{
|
||||
var storage = new StorageQueue(QueueName, ConnectionString);
|
||||
var expected = Guid.NewGuid();
|
||||
await storage.Send(expected);
|
||||
|
||||
var returned = await storage.Get();
|
||||
|
||||
var guid = JsonConvert.DeserializeObject<Guid>(returned.AsString);
|
||||
|
||||
Assert.AreEqual(expected, guid);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task ApproixmateMessageCount()
|
||||
{
|
||||
var random = new Random();
|
||||
var count = random.Next(1, 1000);
|
||||
var storage = new StorageQueue(QueueName, ConnectionString);
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
await storage.Send(Guid.NewGuid());
|
||||
}
|
||||
|
||||
var result = await storage.ApproixmateMessageCount();
|
||||
Assert.AreEqual(count, result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task ApproixmateMessageCountNone()
|
||||
{
|
||||
var storage = new StorageQueue(QueueName, ConnectionString);
|
||||
var result = await storage.ApproixmateMessageCount();
|
||||
Assert.AreEqual(0, result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Delete()
|
||||
{
|
||||
var storage = new StorageQueue(QueueName, ConnectionString);
|
||||
|
||||
var msg = new CloudQueueMessage(Guid.NewGuid().ToByteArray());
|
||||
await storage.Send(msg);
|
||||
var returned = await storage.Get();
|
||||
await storage.Delete(returned);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task RoundTripMany()
|
||||
{
|
||||
var random = new Random();
|
||||
var count = random.Next(1, 25);
|
||||
|
||||
var storage = new StorageQueue(QueueName, ConnectionString);
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var msg = new CloudQueueMessage(Guid.NewGuid().ToByteArray());
|
||||
await storage.Send(msg);
|
||||
}
|
||||
|
||||
var returned = await storage.GetMany(count);
|
||||
|
||||
Assert.AreEqual(count, returned.Count());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task GetManyNegative()
|
||||
{
|
||||
var random = new Random();
|
||||
var count = random.Next(1, 25);
|
||||
|
||||
var storage = new StorageQueue(QueueName, ConnectionString);
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var msg = new CloudQueueMessage(Guid.NewGuid().ToByteArray());
|
||||
await storage.Send(msg);
|
||||
}
|
||||
|
||||
var returned = await storage.GetMany(-1);
|
||||
|
||||
Assert.AreEqual(1, returned.Count());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,870 @@
|
|||
namespace King.Service.Integration
|
||||
{
|
||||
using King.Azure.Data;
|
||||
using Microsoft.WindowsAzure.Storage;
|
||||
using Microsoft.WindowsAzure.Storage.Table;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
[TestFixture]
|
||||
public class TableStorageTests
|
||||
{
|
||||
#region Members
|
||||
private readonly string ConnectionString = "UseDevelopmentStorage=true;";
|
||||
private ITableStorage storage = null;
|
||||
#endregion
|
||||
|
||||
public class Helper : TableEntity
|
||||
{
|
||||
public Guid Id
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public void Init()
|
||||
{
|
||||
var table = "testing";
|
||||
this.storage = new TableStorage(table, ConnectionString);
|
||||
storage.CreateIfNotExists().Wait();
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void Dispose()
|
||||
{
|
||||
storage.Delete().Wait();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task ConstructorAccount()
|
||||
{
|
||||
var name = 'a' + Guid.NewGuid().ToString().ToLowerInvariant().Replace('-', 'a');
|
||||
var account = CloudStorageAccount.Parse(ConnectionString);
|
||||
var storage = new TableStorage(name, account);
|
||||
var created = await storage.CreateIfNotExists();
|
||||
|
||||
Assert.IsTrue(created);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task CreateIfNotExists()
|
||||
{
|
||||
var table = 'a' + Guid.NewGuid().ToString().ToLowerInvariant().Replace('-', 'a');
|
||||
var storage = new TableStorage(table, ConnectionString);
|
||||
var created = await storage.CreateIfNotExists();
|
||||
|
||||
Assert.IsTrue(created);
|
||||
|
||||
await storage.Delete();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task CreateIfNotExistsAlreadyExists()
|
||||
{
|
||||
var table = 'a' + Guid.NewGuid().ToString().ToLowerInvariant().Replace('-', 'a');
|
||||
var storage = new TableStorage(table, ConnectionString);
|
||||
var created = await storage.CreateIfNotExists();
|
||||
|
||||
Assert.IsTrue(created);
|
||||
created = await storage.CreateIfNotExists();
|
||||
Assert.IsFalse(created);
|
||||
|
||||
await storage.Delete();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Create()
|
||||
{
|
||||
var table = 'a' + Guid.NewGuid().ToString().ToLowerInvariant().Replace('-', 'a');
|
||||
var storage = new TableStorage(table, ConnectionString);
|
||||
var created = await storage.Create();
|
||||
|
||||
Assert.IsTrue(created);
|
||||
|
||||
await storage.Delete();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Delete()
|
||||
{
|
||||
var table = 'a' + Guid.NewGuid().ToString().ToLowerInvariant().Replace('-', 'a');
|
||||
var storage = new TableStorage(table, ConnectionString);
|
||||
var created = await storage.Create();
|
||||
|
||||
Assert.IsTrue(created);
|
||||
|
||||
await storage.Delete();
|
||||
|
||||
created = await storage.Create();
|
||||
Assert.IsTrue(created);
|
||||
|
||||
await storage.Delete();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Insert()
|
||||
{
|
||||
var entity = new TableEntity()
|
||||
{
|
||||
PartitionKey = "partition",
|
||||
RowKey = "row",
|
||||
};
|
||||
var entities = new List<TableEntity>();
|
||||
entities.Add(entity);
|
||||
await storage.Insert(entities);
|
||||
|
||||
var returned = await storage.QueryByPartition<TableEntity>("partition");
|
||||
Assert.IsNotNull(returned);
|
||||
Assert.AreEqual(1, returned.Count());
|
||||
var e = returned.First();
|
||||
Assert.AreEqual(entity.PartitionKey, e.PartitionKey);
|
||||
Assert.AreEqual(entity.RowKey, e.RowKey);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task InsertOrReplace()
|
||||
{
|
||||
var entity = new TableEntity()
|
||||
{
|
||||
PartitionKey = "partition",
|
||||
RowKey = "row",
|
||||
};
|
||||
var entities = new List<TableEntity>();
|
||||
entities.Add(entity);
|
||||
await storage.InsertOrReplace(entity);
|
||||
|
||||
var returned = await storage.QueryByPartition<TableEntity>("partition");
|
||||
Assert.IsNotNull(returned);
|
||||
Assert.AreEqual(1, returned.Count());
|
||||
var e = returned.First();
|
||||
Assert.AreEqual(entity.PartitionKey, e.PartitionKey);
|
||||
Assert.AreEqual(entity.RowKey, e.RowKey);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task InsertBatch()
|
||||
{
|
||||
var random = new Random();
|
||||
var count = random.Next(1, 25);
|
||||
var partition = Guid.NewGuid().ToString();
|
||||
var entities = new List<TableEntity>(count);
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var entity = new TableEntity()
|
||||
{
|
||||
PartitionKey = partition,
|
||||
RowKey = Guid.NewGuid().ToString(),
|
||||
};
|
||||
entities.Add(entity);
|
||||
}
|
||||
await storage.Insert(entities);
|
||||
|
||||
var returned = await storage.QueryByPartition<TableEntity>(partition);
|
||||
Assert.IsNotNull(returned);
|
||||
Assert.AreEqual(count, returned.Count());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task InsertBatchMultiplePartitions()
|
||||
{
|
||||
var random = new Random();
|
||||
var count = random.Next(1, 25);
|
||||
var partition = Guid.NewGuid().ToString();
|
||||
var entities = new List<TableEntity>(count);
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var entity = new TableEntity()
|
||||
{
|
||||
PartitionKey = partition,
|
||||
RowKey = Guid.NewGuid().ToString(),
|
||||
};
|
||||
entities.Add(entity);
|
||||
|
||||
if (i % 2 == 0)
|
||||
{
|
||||
partition = Guid.NewGuid().ToString();
|
||||
}
|
||||
}
|
||||
await storage.Insert(entities);
|
||||
|
||||
var returned = await storage.Query<TableEntity>(new TableQuery<TableEntity>());
|
||||
Assert.IsNotNull(returned);
|
||||
Assert.AreEqual(count, returned.Count());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task InsertDictionaryBatch()
|
||||
{
|
||||
var random = new Random();
|
||||
var count = random.Next(1, 25);
|
||||
var partition = Guid.NewGuid().ToString();
|
||||
var entities = new List<IDictionary<string, object>>(count);
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var dic = new Dictionary<string, object>();
|
||||
dic.Add(TableStorage.PartitionKey, partition);
|
||||
dic.Add(TableStorage.RowKey, Guid.NewGuid());
|
||||
dic.Add("Extraa", DateTime.UtcNow);
|
||||
entities.Add(dic);
|
||||
}
|
||||
await storage.Insert(entities);
|
||||
|
||||
var query = new TableQuery();
|
||||
query.Where(TableQuery.GenerateFilterCondition(TableStorage.PartitionKey, QueryComparisons.Equal, partition));
|
||||
var returned = await storage.Query(query);
|
||||
|
||||
Assert.IsNotNull(returned);
|
||||
Assert.AreEqual(count, returned.Count());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task InsertDictionaryBatchMultiplePartitions()
|
||||
{
|
||||
var random = new Random();
|
||||
var count = random.Next(1, 25);
|
||||
var partition = Guid.NewGuid().ToString();
|
||||
var entities = new List<IDictionary<string, object>>(count);
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var dic = new Dictionary<string, object>();
|
||||
dic.Add(TableStorage.PartitionKey, partition);
|
||||
dic.Add(TableStorage.RowKey, Guid.NewGuid());
|
||||
dic.Add("Extraa", DateTime.UtcNow);
|
||||
entities.Add(dic);
|
||||
|
||||
if (i % 2 == 0)
|
||||
{
|
||||
partition = Guid.NewGuid().ToString();
|
||||
}
|
||||
}
|
||||
await storage.Insert(entities);
|
||||
|
||||
var returned = await storage.Query(new TableQuery());
|
||||
|
||||
Assert.IsNotNull(returned);
|
||||
Assert.AreEqual(count, returned.Count());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task InsertOrReplaceDictionary()
|
||||
{
|
||||
var p = Guid.NewGuid().ToString();
|
||||
var r = Guid.NewGuid().ToString();
|
||||
var entity = new Dictionary<string, object>();
|
||||
entity.Add(TableStorage.PartitionKey, p);
|
||||
entity.Add(TableStorage.RowKey, r);
|
||||
entity.Add("Id", Guid.NewGuid());
|
||||
await storage.InsertOrReplace(entity);
|
||||
|
||||
var e = await storage.QueryByPartitionAndRow<Helper>(p, r);
|
||||
Assert.IsNotNull(e);
|
||||
Assert.AreEqual(entity[TableStorage.PartitionKey], e.PartitionKey);
|
||||
Assert.AreEqual(entity[TableStorage.RowKey], e.RowKey);
|
||||
Assert.AreEqual(entity["Id"], e.Id);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task InsertOrReplaceDictionaryPartitionRowGuid()
|
||||
{
|
||||
var p = Guid.NewGuid();
|
||||
var r = Guid.NewGuid();
|
||||
var entity = new Dictionary<string, object>();
|
||||
entity.Add(TableStorage.PartitionKey, p);
|
||||
entity.Add(TableStorage.RowKey, r);
|
||||
entity.Add("Id", Guid.NewGuid());
|
||||
await storage.InsertOrReplace(entity);
|
||||
|
||||
var e = await storage.QueryByPartitionAndRow<Helper>(p.ToString(), r.ToString());
|
||||
Assert.IsNotNull(e);
|
||||
Assert.AreEqual(entity[TableStorage.PartitionKey].ToString(), e.PartitionKey);
|
||||
Assert.AreEqual(entity[TableStorage.RowKey].ToString(), e.RowKey);
|
||||
Assert.AreEqual(entity["Id"], e.Id);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task InsertOrReplaceDictionaryNoRow()
|
||||
{
|
||||
var p = Guid.NewGuid().ToString();
|
||||
var entity = new Dictionary<string, object>();
|
||||
entity.Add(TableStorage.PartitionKey, p);
|
||||
await storage.InsertOrReplace(entity);
|
||||
|
||||
var returned = await storage.QueryByPartition<TableEntity>(p);
|
||||
Assert.IsNotNull(returned);
|
||||
Assert.AreEqual(1, returned.Count());
|
||||
var e = returned.FirstOrDefault();
|
||||
Assert.AreEqual(entity[TableStorage.PartitionKey], e.PartitionKey);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task InsertOrReplaceDictionaryNoPartition()
|
||||
{
|
||||
var r = Guid.NewGuid().ToString();
|
||||
var entity = new Dictionary<string, object>();
|
||||
entity.Add(TableStorage.RowKey, r);
|
||||
await storage.InsertOrReplace(entity);
|
||||
|
||||
var returned = await storage.QueryByRow<TableEntity>(r);
|
||||
Assert.IsNotNull(returned);
|
||||
Assert.AreEqual(1, returned.Count());
|
||||
var e = returned.FirstOrDefault();
|
||||
Assert.AreEqual(entity[TableStorage.RowKey], e.RowKey);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task QueryByPartition()
|
||||
{
|
||||
var random = new Random();
|
||||
var count = random.Next(1, 25);
|
||||
var entities = new List<Helper>();
|
||||
var partition = Guid.NewGuid().ToString();
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var h = new Helper()
|
||||
{
|
||||
PartitionKey = partition,
|
||||
RowKey = Guid.NewGuid().ToString(),
|
||||
Id = Guid.NewGuid(),
|
||||
};
|
||||
entities.Add(h);
|
||||
}
|
||||
|
||||
await storage.Insert(entities);
|
||||
|
||||
var returned = await storage.QueryByPartition<Helper>(partition);
|
||||
Assert.IsNotNull(returned);
|
||||
Assert.AreEqual(count, returned.Count());
|
||||
foreach (var r in returned)
|
||||
{
|
||||
var exists = (from e in entities
|
||||
where e.RowKey == r.RowKey
|
||||
&& e.Id == r.Id
|
||||
select true).FirstOrDefault();
|
||||
Assert.IsTrue(exists);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task QueryByRow()
|
||||
{
|
||||
var random = new Random();
|
||||
var count = random.Next(1, 25);
|
||||
var entities = new List<Helper>();
|
||||
var rowKey = Guid.NewGuid().ToString();
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var h = new Helper()
|
||||
{
|
||||
PartitionKey = Guid.NewGuid().ToString(),
|
||||
RowKey = rowKey,
|
||||
Id = Guid.NewGuid(),
|
||||
};
|
||||
entities.Add(h);
|
||||
|
||||
await storage.InsertOrReplace(h);
|
||||
}
|
||||
|
||||
var returned = await storage.QueryByRow<Helper>(rowKey);
|
||||
Assert.IsNotNull(returned);
|
||||
Assert.AreEqual(count, returned.Count());
|
||||
foreach (var r in returned)
|
||||
{
|
||||
var exists = (from e in entities
|
||||
where e.RowKey == r.RowKey
|
||||
&& e.Id == r.Id
|
||||
select true).FirstOrDefault();
|
||||
Assert.IsTrue(exists);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task QueryByPartitionAndRow()
|
||||
{
|
||||
var random = new Random();
|
||||
var count = random.Next(1, 25);
|
||||
var entities = new List<Helper>();
|
||||
var partition = Guid.NewGuid().ToString();
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var h = new Helper()
|
||||
{
|
||||
PartitionKey = partition,
|
||||
RowKey = Guid.NewGuid().ToString(),
|
||||
Id = Guid.NewGuid(),
|
||||
};
|
||||
entities.Add(h);
|
||||
}
|
||||
|
||||
var z = new Helper()
|
||||
{
|
||||
PartitionKey = partition,
|
||||
RowKey = Guid.NewGuid().ToString(),
|
||||
Id = Guid.NewGuid(),
|
||||
};
|
||||
entities.Add(z);
|
||||
|
||||
await storage.Insert(entities);
|
||||
|
||||
var returned = await storage.QueryByPartitionAndRow<Helper>(z.PartitionKey, z.RowKey);
|
||||
Assert.IsNotNull(returned);
|
||||
Assert.AreEqual(z.Id, returned.Id);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task DeleteByPartition()
|
||||
{
|
||||
var random = new Random();
|
||||
var count = random.Next(1, 25);
|
||||
var entities = new List<Helper>();
|
||||
var partition = Guid.NewGuid().ToString();
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var h = new Helper()
|
||||
{
|
||||
PartitionKey = partition,
|
||||
RowKey = Guid.NewGuid().ToString(),
|
||||
Id = Guid.NewGuid(),
|
||||
};
|
||||
entities.Add(h);
|
||||
}
|
||||
|
||||
await storage.Insert(entities);
|
||||
await storage.DeleteByPartition(partition);
|
||||
|
||||
var returned = await storage.QueryByPartition<Helper>(partition);
|
||||
Assert.IsNotNull(returned);
|
||||
Assert.IsFalse(returned.Any());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task DeleteByPartitionPartitionNull()
|
||||
{
|
||||
await storage.DeleteByPartition(null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task DeleteByPartitionAndRow()
|
||||
{
|
||||
var h = new Helper()
|
||||
{
|
||||
PartitionKey = Guid.NewGuid().ToString(),
|
||||
RowKey = Guid.NewGuid().ToString(),
|
||||
Id = Guid.NewGuid(),
|
||||
};
|
||||
|
||||
await storage.InsertOrReplace(h);
|
||||
await storage.DeleteByPartitionAndRow(h.PartitionKey, h.RowKey);
|
||||
|
||||
var returned = await storage.QueryByPartitionAndRow<Helper>(h.PartitionKey, h.RowKey);
|
||||
Assert.IsNull(returned);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task DeleteEnity()
|
||||
{
|
||||
var h = new Helper()
|
||||
{
|
||||
PartitionKey = Guid.NewGuid().ToString(),
|
||||
RowKey = Guid.NewGuid().ToString(),
|
||||
Id = Guid.NewGuid(),
|
||||
};
|
||||
|
||||
await storage.InsertOrReplace(h);
|
||||
var result = await storage.Delete(h);
|
||||
Assert.IsNotNull(result);
|
||||
|
||||
var returned = await storage.QueryByPartitionAndRow<Helper>(h.PartitionKey, h.RowKey);
|
||||
Assert.IsNull(returned);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task DeleteEnities()
|
||||
{
|
||||
var random = new Random();
|
||||
var count = random.Next(1, 25);
|
||||
var entities = new List<ITableEntity>();
|
||||
var partition = Guid.NewGuid().ToString();
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
entities.Add(new Helper()
|
||||
{
|
||||
PartitionKey = partition,
|
||||
RowKey = Guid.NewGuid().ToString(),
|
||||
Id = Guid.NewGuid(),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
await storage.Insert(entities);
|
||||
|
||||
var result = await storage.Delete(entities);
|
||||
Assert.AreEqual(count, result.Count());
|
||||
|
||||
foreach (var e in entities)
|
||||
{
|
||||
var returned = await storage.QueryByPartitionAndRow<Helper>(e.PartitionKey, e.RowKey);
|
||||
Assert.IsNull(returned);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task DeleteMultipleBatches()
|
||||
{
|
||||
var random = new Random();
|
||||
var count = random.Next(1, 25);
|
||||
var partition = Guid.NewGuid().ToString();
|
||||
var entities = new List<TableEntity>(count);
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var entity = new TableEntity()
|
||||
{
|
||||
PartitionKey = partition,
|
||||
RowKey = Guid.NewGuid().ToString(),
|
||||
};
|
||||
entities.Add(entity);
|
||||
|
||||
if (i % 2 == 0)
|
||||
{
|
||||
partition = Guid.NewGuid().ToString();
|
||||
}
|
||||
}
|
||||
|
||||
await storage.Insert(entities);
|
||||
|
||||
var result = await storage.Delete(entities);
|
||||
Assert.AreEqual(count, result.Count());
|
||||
|
||||
var returned = await storage.Query(new TableQuery());
|
||||
|
||||
Assert.IsNotNull(returned);
|
||||
Assert.AreEqual(0, returned.Count());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task DeleteEntitiesNone()
|
||||
{
|
||||
var result = await storage.Delete(new List<TableEntity>());
|
||||
Assert.IsNull(result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task QueryByPartitionPartitionNull()
|
||||
{
|
||||
var returned = await storage.QueryByPartition<Helper>(null);
|
||||
Assert.IsNotNull(returned);
|
||||
Assert.IsFalse(returned.Any());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task QueryByRowPartitionNull()
|
||||
{
|
||||
var returned = await storage.QueryByRow<Helper>(null);
|
||||
Assert.IsNotNull(returned);
|
||||
Assert.IsFalse(returned.Any());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task QueryByPartitionAndRowPartitionNullRowNull()
|
||||
{
|
||||
var returned = await storage.QueryByPartitionAndRow<Helper>(null, null);
|
||||
Assert.IsNull(returned);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task DeleteByRow()
|
||||
{
|
||||
var random = new Random();
|
||||
var count = random.Next(1, 25);
|
||||
var rowKey = Guid.NewGuid().ToString();
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var h = new Helper()
|
||||
{
|
||||
PartitionKey = Guid.NewGuid().ToString(),
|
||||
RowKey = rowKey,
|
||||
Id = Guid.NewGuid(),
|
||||
};
|
||||
await storage.InsertOrReplace(h);
|
||||
}
|
||||
|
||||
await storage.DeleteByRow(rowKey);
|
||||
|
||||
var returned = await storage.QueryByRow<Helper>(rowKey);
|
||||
Assert.IsNotNull(returned);
|
||||
Assert.IsFalse(returned.Any());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task DeleteByRowRowNull()
|
||||
{
|
||||
await storage.DeleteByRow(null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task QueryByPartitionGreaterThan1000()
|
||||
{
|
||||
var random = new Random();
|
||||
var count = random.Next(1001, 1250);
|
||||
var entities = new List<Helper>();
|
||||
var partition = Guid.NewGuid().ToString();
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var h = new Helper()
|
||||
{
|
||||
PartitionKey = partition,
|
||||
RowKey = Guid.NewGuid().ToString(),
|
||||
Id = Guid.NewGuid(),
|
||||
};
|
||||
|
||||
entities.Add(h);
|
||||
}
|
||||
|
||||
await storage.Insert(entities);
|
||||
|
||||
var returned = await storage.QueryByPartition<Helper>(partition);
|
||||
Assert.IsNotNull(returned);
|
||||
Assert.AreEqual(count, returned.Count());
|
||||
foreach (var r in returned)
|
||||
{
|
||||
var exists = (from e in entities
|
||||
where e.RowKey == r.RowKey
|
||||
&& e.Id == r.Id
|
||||
select true).FirstOrDefault();
|
||||
Assert.IsTrue(exists);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task QueryGreaterThan1000()
|
||||
{
|
||||
var random = new Random();
|
||||
var count = random.Next(1001, 1250);
|
||||
var entities = new List<Helper>();
|
||||
var partition = Guid.NewGuid().ToString();
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var h = new Helper()
|
||||
{
|
||||
PartitionKey = partition,
|
||||
RowKey = Guid.NewGuid().ToString(),
|
||||
Id = Guid.NewGuid(),
|
||||
};
|
||||
|
||||
entities.Add(h);
|
||||
}
|
||||
|
||||
await storage.Insert(entities);
|
||||
|
||||
var query = new TableQuery();
|
||||
query.Where(TableQuery.GenerateFilterCondition(TableStorage.PartitionKey, QueryComparisons.Equal, partition));
|
||||
var returned = await storage.Query(query);
|
||||
Assert.IsNotNull(returned);
|
||||
Assert.AreEqual(count, returned.Count());
|
||||
foreach (var r in returned)
|
||||
{
|
||||
var exists = (from e in entities
|
||||
where e.RowKey == (string)r[TableStorage.RowKey]
|
||||
select true).FirstOrDefault();
|
||||
|
||||
Assert.IsTrue(exists);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Query()
|
||||
{
|
||||
var random = new Random();
|
||||
var count = random.Next(1, 25);
|
||||
var entities = new List<IDictionary<string, object>>();
|
||||
var partition = Guid.NewGuid().ToString();
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var dic = new Dictionary<string, object>();
|
||||
dic.Add(TableStorage.PartitionKey, partition);
|
||||
dic.Add(TableStorage.RowKey, Guid.NewGuid().ToString());
|
||||
dic.Add("Id", Guid.NewGuid());
|
||||
entities.Add(dic);
|
||||
}
|
||||
|
||||
await storage.Insert(entities);
|
||||
|
||||
var query = new TableQuery();
|
||||
query.Where(TableQuery.GenerateFilterCondition(TableStorage.PartitionKey, QueryComparisons.Equal, partition));
|
||||
var returned = await storage.Query(query);
|
||||
|
||||
Assert.IsNotNull(returned);
|
||||
Assert.AreEqual(count, returned.Count());
|
||||
foreach (var r in returned)
|
||||
{
|
||||
var exists = (from e in entities
|
||||
where (string)e[TableStorage.PartitionKey] == (string)r[TableStorage.PartitionKey]
|
||||
&& (string)e[TableStorage.RowKey] == (string)r[TableStorage.RowKey]
|
||||
&& (Guid)e["Id"] == (Guid)r["Id"]
|
||||
&& !string.IsNullOrWhiteSpace((string)r[TableStorage.ETag])
|
||||
&& DateTime.UtcNow.Date == ((DateTime)r[TableStorage.Timestamp]).Date
|
||||
select true).FirstOrDefault();
|
||||
Assert.IsTrue(exists);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task QueryFunction()
|
||||
{
|
||||
var random = new Random();
|
||||
var count = random.Next(2, 25);
|
||||
var entities = new List<IDictionary<string, object>>();
|
||||
var partition = Guid.NewGuid().ToString();
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var dic = new Dictionary<string, object>();
|
||||
dic.Add(TableStorage.PartitionKey, partition);
|
||||
dic.Add(TableStorage.RowKey, Guid.NewGuid().ToString());
|
||||
dic.Add("Id", Guid.NewGuid());
|
||||
entities.Add(dic);
|
||||
}
|
||||
for (var i = 0; i < 5; i++)//Invalid Range
|
||||
{
|
||||
var dic = new Dictionary<string, object>();
|
||||
dic.Add(TableStorage.PartitionKey, Guid.NewGuid().ToString());
|
||||
dic.Add(TableStorage.RowKey, Guid.NewGuid().ToString());
|
||||
dic.Add("Id", Guid.NewGuid());
|
||||
entities.Add(dic);
|
||||
}
|
||||
|
||||
storage.Insert(entities).Wait();
|
||||
|
||||
var returned = await storage.Query<Helper>(i => i.PartitionKey == partition);
|
||||
|
||||
Assert.IsNotNull(returned);
|
||||
Assert.AreEqual(count, returned.Count());
|
||||
foreach (var r in returned)
|
||||
{
|
||||
var exists = (from e in entities
|
||||
where r.RowKey == (string)e[TableStorage.RowKey]
|
||||
&& r.PartitionKey == (string)e[TableStorage.PartitionKey]
|
||||
select true).FirstOrDefault();
|
||||
|
||||
Assert.IsTrue(exists);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task QueryFunctionNone()
|
||||
{
|
||||
var entities = new List<IDictionary<string, object>>();
|
||||
|
||||
for (var i = 0; i < 5; i++)//Invalid Range
|
||||
{
|
||||
var dic = new Dictionary<string, object>();
|
||||
dic.Add(TableStorage.PartitionKey, Guid.NewGuid().ToString());
|
||||
dic.Add(TableStorage.RowKey, Guid.NewGuid().ToString());
|
||||
dic.Add("Id", Guid.NewGuid());
|
||||
entities.Add(dic);
|
||||
}
|
||||
|
||||
storage.Insert(entities).Wait();
|
||||
|
||||
var returned = await storage.Query<Helper>(i => i.PartitionKey == Guid.NewGuid().ToString());
|
||||
|
||||
Assert.IsNotNull(returned);
|
||||
Assert.AreEqual(0, returned.Count());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task QueryByPartitionDictionary()
|
||||
{
|
||||
var random = new Random();
|
||||
var count = random.Next(1, 25);
|
||||
var entities = new List<IDictionary<string, object>>();
|
||||
var partition = Guid.NewGuid().ToString();
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var dic = new Dictionary<string, object>();
|
||||
dic.Add(TableStorage.PartitionKey, partition);
|
||||
dic.Add(TableStorage.RowKey, Guid.NewGuid().ToString());
|
||||
dic.Add("Id", Guid.NewGuid());
|
||||
entities.Add(dic);
|
||||
}
|
||||
|
||||
await storage.Insert(entities);
|
||||
|
||||
var returned = await storage.QueryByPartition(partition);
|
||||
Assert.IsNotNull(returned);
|
||||
Assert.AreEqual(count, returned.Count());
|
||||
foreach (var r in returned)
|
||||
{
|
||||
var exists = (from e in entities
|
||||
where (string)e[TableStorage.RowKey] == (string)r[TableStorage.RowKey]
|
||||
&& (Guid)e["Id"] == (Guid)r["Id"]
|
||||
select true).FirstOrDefault();
|
||||
Assert.IsTrue(exists);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task QueryByRowDictionary()
|
||||
{
|
||||
var random = new Random();
|
||||
var count = random.Next(1, 25);
|
||||
var entities = new List<IDictionary<string, object>>();
|
||||
var rowKey = Guid.NewGuid().ToString();
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var dic = new Dictionary<string, object>();
|
||||
dic.Add(TableStorage.PartitionKey, Guid.NewGuid().ToString());
|
||||
dic.Add(TableStorage.RowKey, rowKey);
|
||||
dic.Add("Id", Guid.NewGuid());
|
||||
entities.Add(dic);
|
||||
|
||||
await storage.InsertOrReplace(dic);
|
||||
}
|
||||
|
||||
var returned = await storage.QueryByRow(rowKey);
|
||||
Assert.IsNotNull(returned);
|
||||
Assert.AreEqual(count, returned.Count());
|
||||
foreach (var r in returned)
|
||||
{
|
||||
var exists = (from e in entities
|
||||
where (string)e[TableStorage.RowKey] == (string)r[TableStorage.RowKey]
|
||||
&& (Guid)e["Id"] == (Guid)r["Id"]
|
||||
select true).FirstOrDefault();
|
||||
Assert.IsTrue(exists);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task QueryByPartitionAndRowDictionary()
|
||||
{
|
||||
var random = new Random();
|
||||
var count = random.Next(1, 25);
|
||||
var entities = new List<IDictionary<string, object>>();
|
||||
var partition = Guid.NewGuid().ToString();
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var dic = new Dictionary<string, object>();
|
||||
dic.Add(TableStorage.PartitionKey, partition);
|
||||
dic.Add(TableStorage.RowKey, Guid.NewGuid().ToString());
|
||||
dic.Add("Id", Guid.NewGuid());
|
||||
entities.Add(dic);
|
||||
}
|
||||
|
||||
var rowKey = Guid.NewGuid().ToString();
|
||||
var z = new Dictionary<string, object>();
|
||||
z.Add(TableStorage.PartitionKey, partition);
|
||||
z.Add(TableStorage.RowKey, rowKey);
|
||||
z.Add("Id", Guid.NewGuid());
|
||||
entities.Add(z);
|
||||
|
||||
await storage.Insert(entities);
|
||||
|
||||
var returned = await storage.QueryByPartitionAndRow(partition, rowKey);
|
||||
Assert.IsNotNull(returned);
|
||||
Assert.AreEqual(z["Id"], returned["Id"]);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<Description>King.Azure.Test Console Application</Description>
|
||||
<VersionPrefix>2.1.0</VersionPrefix>
|
||||
<TargetFramework>net462</TargetFramework>
|
||||
<AssemblyName>King.Azure.Test</AssemblyName>
|
||||
<OutputType>Exe</OutputType>
|
||||
<PackageId>King.Azure.Test</PackageId>
|
||||
<GenerateAssemblyTitleAttribute>false</GenerateAssemblyTitleAttribute>
|
||||
<GenerateAssemblyDescriptionAttribute>false</GenerateAssemblyDescriptionAttribute>
|
||||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
|
||||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
|
||||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
|
||||
<GenerateAssemblyCopyrightAttribute>false</GenerateAssemblyCopyrightAttribute>
|
||||
<GenerateAssemblyVersionAttribute>false</GenerateAssemblyVersionAttribute>
|
||||
<GenerateAssemblyFileVersionAttribute>false</GenerateAssemblyFileVersionAttribute>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\King.Azure\King.Azure.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Runtime" Version="4.1.0" />
|
||||
<PackageReference Include="System.Threading" Version="4.0.11" />
|
||||
<PackageReference Include="System.Threading.Tasks" Version="4.0.11" />
|
||||
<PackageReference Include="System.IO" Version="4.1.0" />
|
||||
<PackageReference Include="System.Linq" Version="4.1.0" />
|
||||
<PackageReference Include="System.Linq.Expressions" Version="4.1.0" />
|
||||
<PackageReference Include="NSubstitute" Version="2.0.2" />
|
||||
<PackageReference Include="NUnitLite" Version="3.6.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net462' ">
|
||||
<Reference Include="System" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,19 @@
|
|||
namespace King.Azure.Test
|
||||
{
|
||||
using NUnit.Common;
|
||||
using NUnitLite;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var writter = new ExtendedTextWrapper(Console.Out);
|
||||
new AutoRun(typeof(Program).GetTypeInfo().Assembly).Execute(args, writter, Console.In);
|
||||
|
||||
Console.WriteLine("Testing Completed.");
|
||||
Console.Read();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
[assembly: AssemblyTitle("King.Azure.Test")]
|
||||
[assembly: AssemblyDescription("King.Azure Tests")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("King.Azure.Unit.Test")]
|
||||
[assembly: AssemblyCopyright("Copyright © Jef King 2017")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
[assembly: AssemblyVersion("2.0.0.1")]
|
||||
[assembly: AssemblyFileVersion("2.0.0.1")]
|
||||
[assembly: Guid("91edff1c-a9c5-4f92-b3e9-781651577d75")]
|
|
@ -0,0 +1,29 @@
|
|||
namespace King.Azure.Unit.Test.Data
|
||||
{
|
||||
using King.Azure.Data;
|
||||
using NUnit.Framework;
|
||||
|
||||
[TestFixture]
|
||||
public class AzureStorageResourcesTests
|
||||
{
|
||||
private readonly string ConnectionString = "UseDevelopmentStorage=true;";
|
||||
|
||||
[Test]
|
||||
public void Constructor()
|
||||
{
|
||||
new AzureStorageResources(ConnectionString);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsIAzureStorageResources()
|
||||
{
|
||||
Assert.IsNotNull(new AzureStorageResources(ConnectionString) as IAzureStorageResources);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsAzureStorage()
|
||||
{
|
||||
Assert.IsNotNull(new AzureStorageResources(ConnectionString) as AzureStorage);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
namespace King.Azure.Unit.Test.Data
|
||||
{
|
||||
using System;
|
||||
using King.Azure.Data;
|
||||
using Microsoft.WindowsAzure.Storage;
|
||||
using NUnit.Framework;
|
||||
|
||||
[TestFixture]
|
||||
public class AzureStorageTests
|
||||
{
|
||||
const string ConnectionString = "UseDevelopmentStorage=true";
|
||||
|
||||
[Test]
|
||||
public void Constructor()
|
||||
{
|
||||
new AzureStorage(ConnectionString);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsIStorageAccount()
|
||||
{
|
||||
Assert.IsNotNull(new AzureStorage(ConnectionString) as IStorageAccount);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConstructorConnectionStringNull()
|
||||
{
|
||||
Assert.That(() => new AzureStorage((string)null), Throws.TypeOf<ArgumentNullException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConstructorAccountNull()
|
||||
{
|
||||
Assert.That(() => new AzureStorage((CloudStorageAccount)null), Throws.TypeOf<ArgumentNullException>());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,265 @@
|
|||
namespace King.Azure.Unit.Test.Data
|
||||
{
|
||||
using King.Azure.Data;
|
||||
using Microsoft.WindowsAzure.Storage;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
|
||||
[TestFixture]
|
||||
public class ContainerTests
|
||||
{
|
||||
private const string ConnectionString = "UseDevelopmentStorage=true;";
|
||||
|
||||
[Test]
|
||||
public void Constructor()
|
||||
{
|
||||
new Container("test", ConnectionString);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsIContainer()
|
||||
{
|
||||
Assert.IsNotNull(new Container("test", ConnectionString) as IContainer);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsAzureStorage()
|
||||
{
|
||||
Assert.IsNotNull(new Container("test", ConnectionString) as AzureStorage);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConstructorNameNull()
|
||||
{
|
||||
Assert.That(() => new Container(null, ConnectionString), Throws.TypeOf<ArgumentException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConstructorAccountNameNull()
|
||||
{
|
||||
Assert.That(() => new Container(null, CloudStorageAccount.Parse(ConnectionString)), Throws.TypeOf<ArgumentException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConstructorKeyNull()
|
||||
{
|
||||
Assert.That(() => new Container("test", (string)null), Throws.TypeOf<ArgumentNullException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DefaultCacheDuration()
|
||||
{
|
||||
Assert.AreEqual(31536000, Container.DefaultCacheDuration);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Name()
|
||||
{
|
||||
var name = Guid.NewGuid().ToString();
|
||||
var t = new Container(name, ConnectionString);
|
||||
Assert.AreEqual(name, t.Name);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsPublic()
|
||||
{
|
||||
var name = Guid.NewGuid().ToString();
|
||||
var t = new Container(name, ConnectionString, true);
|
||||
Assert.IsTrue(t.IsPublic);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Client()
|
||||
{
|
||||
var name = Guid.NewGuid().ToString();
|
||||
var t = new Container(name, ConnectionString);
|
||||
Assert.IsNotNull(t.Client);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Reference()
|
||||
{
|
||||
var name = Guid.NewGuid().ToString();
|
||||
var t = new Container(name, ConnectionString);
|
||||
Assert.IsNotNull(t.Reference);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DeleteBlobNameNull()
|
||||
{
|
||||
var c = new Container("test", ConnectionString);
|
||||
Assert.That(() => c.Delete(null), Throws.TypeOf<ArgumentException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ExistsBlobNameNull()
|
||||
{
|
||||
var c = new Container("test", ConnectionString);
|
||||
Assert.That(() => c.Exists(null), Throws.TypeOf<ArgumentException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetBlobNameNull()
|
||||
{
|
||||
var c = new Container("test", ConnectionString);
|
||||
|
||||
Assert.That(() => c.Get<object>(null), Throws.TypeOf<ArgumentException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void StreamBlobNameNull()
|
||||
{
|
||||
var c = new Container("test", ConnectionString);
|
||||
|
||||
Assert.That(() => c.Stream(null), Throws.TypeOf<ArgumentException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SaveBlobNameNull()
|
||||
{
|
||||
var c = new Container("test", ConnectionString);
|
||||
|
||||
Assert.That(() => c.Save(null, new object()), Throws.TypeOf<ArgumentException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SaveObjectNull()
|
||||
{
|
||||
var c = new Container("test", ConnectionString);
|
||||
|
||||
Assert.That(() => c.Save(Guid.NewGuid().ToString(), (object)null), Throws.TypeOf<ArgumentNullException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetBytesBlobNameNull()
|
||||
{
|
||||
var c = new Container("test", ConnectionString);
|
||||
|
||||
Assert.That(() => c.Get(null), Throws.TypeOf<ArgumentException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetTextBlobNameNull()
|
||||
{
|
||||
var c = new Container("test", ConnectionString);
|
||||
|
||||
Assert.That(() => c.GetText(null), Throws.TypeOf<ArgumentException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SnapShotBlobNameNull()
|
||||
{
|
||||
var c = new Container("test", ConnectionString);
|
||||
|
||||
Assert.That(() => c.Snapshot(null), Throws.TypeOf<ArgumentException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SaveBytesBlobNameNull()
|
||||
{
|
||||
var random = new Random();
|
||||
var bytes = new byte[1024];
|
||||
random.NextBytes(bytes);
|
||||
|
||||
var c = new Container("test", ConnectionString);
|
||||
|
||||
Assert.That(() => c.Save(null, bytes), Throws.TypeOf<ArgumentException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SaveTextBlobNameNull()
|
||||
{
|
||||
var c = new Container("test", ConnectionString);
|
||||
|
||||
Assert.That(() => c.Save(null, Guid.NewGuid().ToString()), Throws.TypeOf<ArgumentException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SaveBytesNull()
|
||||
{
|
||||
var c = new Container("test", ConnectionString);
|
||||
|
||||
Assert.That(() => c.Save(Guid.NewGuid().ToString(), (byte[])null), Throws.TypeOf<ArgumentNullException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SaveTextNull()
|
||||
{
|
||||
var c = new Container("test", ConnectionString);
|
||||
|
||||
Assert.That(() => c.Save(Guid.NewGuid().ToString(), (string)null), Throws.TypeOf<ArgumentException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetBlockReferenceBlobNameNull()
|
||||
{
|
||||
var c = new Container("test", ConnectionString);
|
||||
|
||||
Assert.That(() => c.GetBlockReference(null), Throws.TypeOf<ArgumentException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetPageReferenceBlobNameNull()
|
||||
{
|
||||
var c = new Container("test", ConnectionString);
|
||||
|
||||
Assert.That(() => c.GetPageReference(null), Throws.TypeOf<ArgumentException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertiesBlobNameNull()
|
||||
{
|
||||
var c = new Container("test", ConnectionString);
|
||||
|
||||
Assert.That(() => c.Properties(null), Throws.TypeOf<ArgumentException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SetCacheControlBlobNameNull()
|
||||
{
|
||||
var c = new Container("test", ConnectionString);
|
||||
|
||||
Assert.That(() => c.SetCacheControl(null), Throws.TypeOf<ArgumentException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CopyFromToFromNull()
|
||||
{
|
||||
var c = new Container("test", ConnectionString);
|
||||
|
||||
Assert.That(() => c.Copy(null, Guid.NewGuid().ToString()), Throws.TypeOf<ArgumentException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CopyFromToToNull()
|
||||
{
|
||||
var c = new Container("test", ConnectionString);
|
||||
|
||||
Assert.That(() => c.Copy(Guid.NewGuid().ToString(), null), Throws.TypeOf<ArgumentException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CopyFromNull()
|
||||
{
|
||||
var c = new Container("test", ConnectionString);
|
||||
|
||||
Assert.That(() => c.Copy(null, c, Guid.NewGuid().ToString()), Throws.TypeOf<ArgumentException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CopyToNull()
|
||||
{
|
||||
var c = new Container("test", ConnectionString);
|
||||
|
||||
Assert.That(() => c.Copy(Guid.NewGuid().ToString(), c, null), Throws.TypeOf<ArgumentException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CopyTargetNull()
|
||||
{
|
||||
var c = new Container("test", ConnectionString);
|
||||
|
||||
Assert.That(() => c.Copy(Guid.NewGuid().ToString(), (IContainer)null, Guid.NewGuid().ToString()), Throws.TypeOf<ArgumentNullException>());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
namespace King.Azure.Unit.Test.Data
|
||||
{
|
||||
using System;
|
||||
using King.Azure.Data;
|
||||
using Microsoft.WindowsAzure.Storage;
|
||||
using NUnit.Framework;
|
||||
|
||||
[TestFixture]
|
||||
public class FileShareTests
|
||||
{
|
||||
private const string ConnectionString = "DefaultEndpointsProtocol=https;AccountName=kingazure;AccountKey=LQFXI8kFSh0TR0dk2bvukQZRxymByGn1amCiR8chpIZ+NkLHqx6IFMcApHGWQutKpWfPloJfNv3ySM+uOJ3f9g==;";
|
||||
|
||||
[Test]
|
||||
public void Constructor()
|
||||
{
|
||||
new FileShare("test", ConnectionString);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConstructorAccount()
|
||||
{
|
||||
new FileShare("test", CloudStorageAccount.Parse(ConnectionString));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsAzureStorage()
|
||||
{
|
||||
Assert.IsNotNull(new FileShare("test", ConnectionString) as AzureStorage);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsIFileShare()
|
||||
{
|
||||
Assert.IsNotNull(new FileShare("test", ConnectionString) as IFileShare);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConstructorNameNull()
|
||||
{
|
||||
Assert.That(() => new FileShare(null, ConnectionString), Throws.TypeOf<ArgumentException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConstructorAccountNameNull()
|
||||
{
|
||||
Assert.That(() => new FileShare(null, CloudStorageAccount.Parse(ConnectionString)), Throws.TypeOf<ArgumentException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Client()
|
||||
{
|
||||
var name = Guid.NewGuid().ToString();
|
||||
var t = new FileShare(name, ConnectionString);
|
||||
Assert.IsNotNull(t.Client);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Reference()
|
||||
{
|
||||
var name = Guid.NewGuid().ToString();
|
||||
var t = new FileShare(name, ConnectionString);
|
||||
Assert.IsNotNull(t.Reference);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Name()
|
||||
{
|
||||
var name = Guid.NewGuid().ToString();
|
||||
var t = new FileShare(name, ConnectionString);
|
||||
Assert.AreEqual(name, t.Name);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
namespace King.Azure.Unit.Test.Data
|
||||
{
|
||||
using King.Azure.Data;
|
||||
using Microsoft.WindowsAzure.Storage.Queue;
|
||||
using NSubstitute;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
[TestFixture]
|
||||
public class StorageQueuePollerTests
|
||||
{
|
||||
const string ConnectionString = "UseDevelopmentStorage=true";
|
||||
|
||||
[Test]
|
||||
public void Constructor()
|
||||
{
|
||||
new StorageQueuePoller<object>("queue", ConnectionString);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConstructorStorageQueueNull()
|
||||
{
|
||||
Assert.That(() => new StorageQueuePoller<object>(null), Throws.TypeOf<ArgumentNullException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsIStorageQueuePoller()
|
||||
{
|
||||
Assert.IsNotNull(new StorageQueuePoller<object>("queue", ConnectionString) as IStorageQueuePoller<object>);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Queue()
|
||||
{
|
||||
var queue = Substitute.For<IStorageQueue>();
|
||||
var poller = new StorageQueuePoller<object>(queue);
|
||||
var returned = poller.Queue;
|
||||
Assert.AreEqual(queue, returned);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Poll()
|
||||
{
|
||||
var msg = new CloudQueueMessage("data");
|
||||
var queue = Substitute.For<IStorageQueue>();
|
||||
queue.Get().Returns(Task.FromResult(msg));
|
||||
|
||||
var poller = new StorageQueuePoller<object>(queue);
|
||||
var returned = await poller.Poll();
|
||||
|
||||
Assert.IsNotNull(returned);
|
||||
|
||||
await queue.Received().Get();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task PollGetNull()
|
||||
{
|
||||
var queue = Substitute.For<IStorageQueue>();
|
||||
queue.Get().Returns(Task.FromResult<CloudQueueMessage>(null));
|
||||
|
||||
var poller = new StorageQueuePoller<object>(queue);
|
||||
var returned = await poller.Poll();
|
||||
|
||||
Assert.IsNull(returned);
|
||||
|
||||
await queue.Received().Get();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PollGetThrows()
|
||||
{
|
||||
var msg = new CloudQueueMessage("data");
|
||||
var queue = Substitute.For<IStorageQueue>();
|
||||
queue.Get().ReturnsForAnyArgs<object>(x => { throw new ApplicationException(); });
|
||||
|
||||
var poller = new StorageQueuePoller<object>(queue);
|
||||
|
||||
Assert.That(() => poller.Poll(), Throws.TypeOf<ApplicationException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task PollMany()
|
||||
{
|
||||
var msg = new CloudQueueMessage("data");
|
||||
var msgs = new List<CloudQueueMessage>(3);
|
||||
msgs.Add(msg);
|
||||
msgs.Add(msg);
|
||||
msgs.Add(msg);
|
||||
|
||||
var queue = Substitute.For<IStorageQueue>();
|
||||
queue.GetMany(3).Returns(Task.FromResult<IEnumerable<CloudQueueMessage>>(msgs));
|
||||
|
||||
var poller = new StorageQueuePoller<object>(queue);
|
||||
var returned = await poller.PollMany(3);
|
||||
|
||||
Assert.IsNotNull(returned);
|
||||
Assert.AreEqual(3, returned.Count());
|
||||
|
||||
await queue.Received().GetMany(3);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task PollGetManyNull()
|
||||
{
|
||||
var queue = Substitute.For<IStorageQueue>();
|
||||
queue.GetMany(3).Returns(Task.FromResult<IEnumerable<CloudQueueMessage>>(null));
|
||||
|
||||
var poller = new StorageQueuePoller<object>(queue);
|
||||
var returned = await poller.PollMany(3);
|
||||
|
||||
Assert.IsNull(returned);
|
||||
|
||||
await queue.Received().GetMany(3);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PollGetManyThrows()
|
||||
{
|
||||
var msg = new CloudQueueMessage("data");
|
||||
var queue = Substitute.For<IStorageQueue>();
|
||||
queue.GetMany().ReturnsForAnyArgs<object>(x => { throw new ApplicationException(); });
|
||||
|
||||
var poller = new StorageQueuePoller<object>(queue);
|
||||
|
||||
Assert.That(() => poller.PollMany(), Throws.TypeOf<ApplicationException>());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,197 @@
|
|||
namespace King.Azure.Unit.Test.Data
|
||||
{
|
||||
using King.Azure.Data;
|
||||
using NSubstitute;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
[TestFixture]
|
||||
public class StorageQueueShardsTests
|
||||
{
|
||||
private const string ConnectionString = "UseDevelopmentStorage=true;";
|
||||
|
||||
[Test]
|
||||
public void Constructor()
|
||||
{
|
||||
var sqs = new StorageQueueShards("test", ConnectionString, 2);
|
||||
Assert.AreEqual(2, sqs.Queues.Count());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConstructorConnectionNull()
|
||||
{
|
||||
Assert.That(() => new StorageQueueShards("test", null), Throws.TypeOf<ArgumentException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConstructorNameNull()
|
||||
{
|
||||
Assert.That(() => new StorageQueueShards(null, ConnectionString), Throws.TypeOf<ArgumentException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConstructorQueuesNull()
|
||||
{
|
||||
Assert.That(() => new StorageQueueShards(null), Throws.TypeOf<ArgumentNullException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConstructorQueuesEmpty()
|
||||
{
|
||||
Assert.That(() => new StorageQueueShards(new IStorageQueue[0]), Throws.TypeOf<ArgumentException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConstructorShardDefault()
|
||||
{
|
||||
var sqs = new StorageQueueShards("test", ConnectionString);
|
||||
Assert.AreEqual(2, sqs.Queues.Count());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsIQueueShardSender()
|
||||
{
|
||||
Assert.IsNotNull(new StorageQueueShards("test", ConnectionString) as IQueueShardSender<IStorageQueue>);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsIAzureStorage()
|
||||
{
|
||||
Assert.IsNotNull(new StorageQueueShards("test", ConnectionString) as IAzureStorage);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Name()
|
||||
{
|
||||
var name = Guid.NewGuid().ToString();
|
||||
var sqs = new StorageQueueShards(name, ConnectionString, 2);
|
||||
Assert.AreEqual(name, sqs.Name);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Queues()
|
||||
{
|
||||
var random = new Random();
|
||||
var i = (byte)random.Next(1, byte.MaxValue);
|
||||
var sqs = new StorageQueueShards("test", ConnectionString, i);
|
||||
Assert.IsNotNull(sqs.Queues);
|
||||
Assert.AreEqual(i, sqs.Queues.Count());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task CreateIfNotExists()
|
||||
{
|
||||
var random = new Random();
|
||||
var i = random.Next(1, byte.MaxValue);
|
||||
var qs = new List<IStorageQueue>();
|
||||
for (var j = 0; j < i; j++)
|
||||
{
|
||||
var q = Substitute.For<IStorageQueue>();
|
||||
q.CreateIfNotExists().Returns(Task.FromResult(true));
|
||||
qs.Add(q);
|
||||
}
|
||||
var sqs = new StorageQueueShards(qs.ToArray());
|
||||
|
||||
var success = await sqs.CreateIfNotExists();
|
||||
Assert.IsTrue(success);
|
||||
|
||||
foreach (var q in qs)
|
||||
{
|
||||
await q.Received().CreateIfNotExists();
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Delete()
|
||||
{
|
||||
var random = new Random();
|
||||
var i = random.Next(1, byte.MaxValue);
|
||||
var qs = new List<IStorageQueue>();
|
||||
for (var j = 0; j < i; j++)
|
||||
{
|
||||
var q = Substitute.For<IStorageQueue>();
|
||||
q.Delete().Returns(Task.FromResult(true));
|
||||
qs.Add(q);
|
||||
}
|
||||
var sqs = new StorageQueueShards(qs.ToArray());
|
||||
|
||||
await sqs.Delete();
|
||||
|
||||
foreach (var q in qs)
|
||||
{
|
||||
await q.Received().Delete();
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Save()
|
||||
{
|
||||
var random = new Random();
|
||||
var i = (byte)random.Next(1, byte.MaxValue);
|
||||
var index = random.Next(0, i);
|
||||
|
||||
var msg = new object();
|
||||
var qs = new List<IStorageQueue>();
|
||||
|
||||
for (var j = 0; j < i; j++)
|
||||
{
|
||||
var q = Substitute.For<IStorageQueue>();
|
||||
q.Send(msg).Returns(Task.CompletedTask);
|
||||
qs.Add(q);
|
||||
}
|
||||
|
||||
var sqs = new StorageQueueShards(qs);
|
||||
|
||||
await sqs.Save(msg, (byte)index);
|
||||
|
||||
for (var j = 0; j < i; j++)
|
||||
{
|
||||
if (j == index)
|
||||
{
|
||||
await qs[j].Received().Send(msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
await qs[j].DidNotReceive().Send(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Index()
|
||||
{
|
||||
var msg = new object();
|
||||
var q = Substitute.For<IStorageQueue>();
|
||||
|
||||
var qs = new List<IStorageQueue>();
|
||||
qs.Add(q);
|
||||
qs.Add(q);
|
||||
qs.Add(q);
|
||||
|
||||
var sqs = new StorageQueueShards(qs);
|
||||
|
||||
var index = sqs.Index(0);
|
||||
|
||||
Assert.IsTrue(0 <= index && 3 > index);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IndexBad([Values(0,255)] int val, [Values(0,0)] int expected)
|
||||
{
|
||||
var msg = new object();
|
||||
var q = Substitute.For<IStorageQueue>();
|
||||
|
||||
var qs = new List<IStorageQueue>();
|
||||
qs.Add(q);
|
||||
|
||||
var sqs = new StorageQueueShards(qs);
|
||||
|
||||
var index = sqs.Index((byte)val);
|
||||
|
||||
Assert.AreEqual(expected, index);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
namespace King.Azure.Unit.Test.Data
|
||||
{
|
||||
using System;
|
||||
using King.Azure.Data;
|
||||
using Microsoft.WindowsAzure.Storage;
|
||||
using Microsoft.WindowsAzure.Storage.Queue;
|
||||
using NUnit.Framework;
|
||||
|
||||
[TestFixture]
|
||||
public class StorageQueueTests
|
||||
{
|
||||
private const string ConnectionString = "UseDevelopmentStorage=true;";
|
||||
|
||||
[Test]
|
||||
public void Constructor()
|
||||
{
|
||||
new StorageQueue("test", ConnectionString, TimeSpan.FromSeconds(22));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IQueue()
|
||||
{
|
||||
Assert.IsNotNull(new StorageQueue("test", ConnectionString) as IStorageQueue);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConstructorTableNull()
|
||||
{
|
||||
Assert.That(() => new StorageQueue(null, ConnectionString), Throws.TypeOf<ArgumentException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConstructorAccountTableNull()
|
||||
{
|
||||
Assert.That(() => new StorageQueue(null, CloudStorageAccount.Parse(ConnectionString)), Throws.TypeOf<ArgumentException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConstructorKeyNull()
|
||||
{
|
||||
Assert.That(() => new StorageQueue("test", (string)null), Throws.TypeOf<ArgumentNullException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Name()
|
||||
{
|
||||
var name = Guid.NewGuid().ToString();
|
||||
var t = new StorageQueue(name, ConnectionString);
|
||||
Assert.AreEqual(name, t.Name);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Client()
|
||||
{
|
||||
var name = Guid.NewGuid().ToString();
|
||||
var t = new StorageQueue(name, ConnectionString);
|
||||
Assert.IsNotNull(t.Client);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Reference()
|
||||
{
|
||||
var name = Guid.NewGuid().ToString();
|
||||
var t = new StorageQueue(name, ConnectionString);
|
||||
Assert.IsNotNull(t.Reference);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DeleteNull()
|
||||
{
|
||||
var name = Guid.NewGuid().ToString();
|
||||
var t = new StorageQueue(name, ConnectionString);
|
||||
|
||||
Assert.That(() => t.Delete(null), Throws.TypeOf<ArgumentNullException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SaveMessageNull()
|
||||
{
|
||||
var name = Guid.NewGuid().ToString();
|
||||
var t = new StorageQueue(name, ConnectionString);
|
||||
|
||||
Assert.That(() => t.Send((CloudQueueMessage)null), Throws.TypeOf<ArgumentNullException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SaveNull()
|
||||
{
|
||||
var name = Guid.NewGuid().ToString();
|
||||
var t = new StorageQueue(name, ConnectionString);
|
||||
|
||||
Assert.That(() => t.Send((object)null), Throws.TypeOf<ArgumentNullException>());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
namespace King.Azure.Unit.Test.Data
|
||||
{
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using King.Azure.Data;
|
||||
using Microsoft.WindowsAzure.Storage.Queue;
|
||||
using Newtonsoft.Json;
|
||||
using NSubstitute;
|
||||
using NUnit.Framework;
|
||||
|
||||
[TestFixture]
|
||||
public class StorageQueuedMessageTests
|
||||
{
|
||||
public class Helper
|
||||
{
|
||||
public Guid Test
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Constructor()
|
||||
{
|
||||
var queue = Substitute.For<IStorageQueue>();
|
||||
new StorageQueuedMessage<object>(queue, new CloudQueueMessage("ship"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConstructorQueueNull()
|
||||
{
|
||||
var message = new CloudQueueMessage("ship");
|
||||
|
||||
Assert.That(() => new StorageQueuedMessage<object>(null, message), Throws.TypeOf<ArgumentNullException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConstructorMessageNull()
|
||||
{
|
||||
var queue = Substitute.For<IStorageQueue>();
|
||||
|
||||
Assert.That(() => new StorageQueuedMessage<object>(queue, null), Throws.TypeOf<ArgumentNullException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Complete()
|
||||
{
|
||||
var queue = Substitute.For<IStorageQueue>();
|
||||
var message = new CloudQueueMessage("ship");
|
||||
await queue.Delete(message);
|
||||
|
||||
var sqm = new StorageQueuedMessage<object>(queue, message);
|
||||
await sqm.Complete();
|
||||
|
||||
await queue.Received().Delete(message);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Abandon()
|
||||
{
|
||||
var queue = Substitute.For<IStorageQueue>();
|
||||
var message = new CloudQueueMessage("ship");
|
||||
|
||||
var sqm = new StorageQueuedMessage<object>(queue, message);
|
||||
await sqm.Abandon();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Data()
|
||||
{
|
||||
var expected = new Helper()
|
||||
{
|
||||
Test = Guid.NewGuid(),
|
||||
};
|
||||
var json = JsonConvert.SerializeObject(expected);
|
||||
var queue = Substitute.For<IStorageQueue>();
|
||||
var message = new CloudQueueMessage(json);
|
||||
|
||||
var sqm = new StorageQueuedMessage<Helper>(queue, message);
|
||||
var data = await sqm.Data();
|
||||
|
||||
Assert.IsNotNull(data);
|
||||
Assert.AreEqual(expected.Test, data.Test);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,358 @@
|
|||
namespace King.Azure.Unit.Test.Data
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using King.Azure.Data;
|
||||
using Microsoft.WindowsAzure.Storage;
|
||||
using Microsoft.WindowsAzure.Storage.Table;
|
||||
using NUnit.Framework;
|
||||
|
||||
[TestFixture]
|
||||
public class TableStorageTests
|
||||
{
|
||||
private const string ConnectionString = "UseDevelopmentStorage=true;";
|
||||
|
||||
[Test]
|
||||
public void Constructor()
|
||||
{
|
||||
new TableStorage("TestTable", ConnectionString);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsITableStorage()
|
||||
{
|
||||
Assert.IsNotNull(new TableStorage("TestTable", ConnectionString) as ITableStorage);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PartitionKey()
|
||||
{
|
||||
Assert.AreEqual("PartitionKey", TableStorage.PartitionKey);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RowKey()
|
||||
{
|
||||
Assert.AreEqual("RowKey", TableStorage.RowKey);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Timestamp()
|
||||
{
|
||||
Assert.AreEqual("Timestamp", TableStorage.Timestamp);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ETag()
|
||||
{
|
||||
Assert.AreEqual("ETag", TableStorage.ETag);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConstructorTableNull()
|
||||
{
|
||||
Assert.That(() => new TableStorage(null, ConnectionString), Throws.TypeOf<ArgumentException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConstructorAccountTableNull()
|
||||
{
|
||||
Assert.That(() => new TableStorage(null, CloudStorageAccount.Parse(ConnectionString)), Throws.TypeOf<ArgumentException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConstructorConnectionStringNull()
|
||||
{
|
||||
Assert.That(() => new TableStorage("TestTable", (string)null), Throws.TypeOf<ArgumentNullException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Name()
|
||||
{
|
||||
var name = Guid.NewGuid().ToString();
|
||||
var t = new TableStorage(name, ConnectionString);
|
||||
Assert.AreEqual(name, t.Name);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Client()
|
||||
{
|
||||
var name = Guid.NewGuid().ToString();
|
||||
var t = new TableStorage(name, ConnectionString);
|
||||
Assert.IsNotNull(t.Client);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Reference()
|
||||
{
|
||||
var name = Guid.NewGuid().ToString();
|
||||
var t = new TableStorage(name, ConnectionString);
|
||||
Assert.IsNotNull(t.Reference);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void InsertDictionaryNull()
|
||||
{
|
||||
var name = Guid.NewGuid().ToString();
|
||||
var t = new TableStorage(name, ConnectionString);
|
||||
|
||||
Assert.That(() => t.InsertOrReplace((IDictionary<string, object>)null), Throws.TypeOf<ArgumentNullException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void QueryFunctionNull()
|
||||
{
|
||||
var name = Guid.NewGuid().ToString();
|
||||
var t = new TableStorage(name, ConnectionString);
|
||||
|
||||
Assert.That(() => t.Query<TableEntity>(null, 1000), Throws.TypeOf<ArgumentNullException>());
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void QueryFunctionResultsNegative()
|
||||
{
|
||||
var name = Guid.NewGuid().ToString();
|
||||
var t = new TableStorage(name, ConnectionString);
|
||||
|
||||
Assert.That(() => t.Query<TableEntity>(i => i.PartitionKey == "hi", -100), Throws.TypeOf<InvalidOperationException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void QueryTableQueryNull()
|
||||
{
|
||||
var name = Guid.NewGuid().ToString();
|
||||
var t = new TableStorage(name, ConnectionString);
|
||||
|
||||
Assert.That(() => t.Query<TableEntity>(null), Throws.TypeOf<ArgumentNullException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void QueryDictionaryQueryNull()
|
||||
{
|
||||
var name = Guid.NewGuid().ToString();
|
||||
var t = new TableStorage(name, ConnectionString);
|
||||
|
||||
Assert.That(() => t.Query((TableQuery)null), Throws.TypeOf<ArgumentNullException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DeleteEntityNull()
|
||||
{
|
||||
var name = Guid.NewGuid().ToString();
|
||||
var t = new TableStorage(name, ConnectionString);
|
||||
|
||||
Assert.That(() => t.Delete((ITableEntity)null), Throws.TypeOf<ArgumentNullException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DeleteEntitiesNull()
|
||||
{
|
||||
var name = Guid.NewGuid().ToString();
|
||||
var t = new TableStorage(name, ConnectionString);
|
||||
|
||||
Assert.That(() => t.Delete((IEnumerable<ITableEntity>)null), Throws.TypeOf<ArgumentNullException>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BatchOne()
|
||||
{
|
||||
var items = new List<ITableEntity>();
|
||||
items.Add(new TableEntity());
|
||||
|
||||
var name = Guid.NewGuid().ToString();
|
||||
var t = new TableStorage(name, ConnectionString);
|
||||
|
||||
var batches = t.Batch(items);
|
||||
Assert.AreEqual(1, batches.Count());
|
||||
Assert.AreEqual(1, batches.First().Count());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BatchNone()
|
||||
{
|
||||
var items = new List<ITableEntity>();
|
||||
|
||||
var name = Guid.NewGuid().ToString();
|
||||
var t = new TableStorage(name, ConnectionString);
|
||||
|
||||
var batches = t.Batch(items);
|
||||
Assert.AreEqual(0, batches.Count());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BatchThousandsDifferentPartitions()
|
||||
{
|
||||
var random = new Random();
|
||||
var count = random.Next(2001, 10000);
|
||||
var items = new List<ITableEntity>();
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
items.Add(new TableEntity() { PartitionKey = Guid.NewGuid().ToString() });
|
||||
}
|
||||
|
||||
var name = Guid.NewGuid().ToString();
|
||||
var t = new TableStorage(name, ConnectionString);
|
||||
|
||||
var batches = t.Batch(items);
|
||||
Assert.AreEqual(count, batches.Count());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BatchThousands()
|
||||
{
|
||||
var random = new Random();
|
||||
var count = random.Next(2001, 10000);
|
||||
var partition = Guid.NewGuid().ToString();
|
||||
var items = new List<ITableEntity>();
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
items.Add(new TableEntity() { PartitionKey = partition });
|
||||
}
|
||||
|
||||
var name = Guid.NewGuid().ToString();
|
||||
var t = new TableStorage(name, ConnectionString);
|
||||
|
||||
var batches = t.Batch(items);
|
||||
Assert.AreEqual(Math.Ceiling(((double)count / TableStorage.MaimumxInsertBatch)), batches.Count());
|
||||
var resultCount = 0;
|
||||
foreach (var b in batches)
|
||||
{
|
||||
resultCount += b.Count();
|
||||
}
|
||||
Assert.AreEqual(count, resultCount);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChunkThousands()
|
||||
{
|
||||
var random = new Random();
|
||||
var count = random.Next(2001, 15000);
|
||||
var partition = Guid.NewGuid().ToString();
|
||||
var items = new List<ITableEntity>();
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
items.Add(new TableEntity() { PartitionKey = partition });
|
||||
}
|
||||
|
||||
var name = Guid.NewGuid().ToString();
|
||||
var t = new TableStorage(name, ConnectionString);
|
||||
|
||||
var batches = t.Chunk<ITableEntity>(items);
|
||||
Assert.AreEqual(Math.Ceiling(((double)count / TableStorage.MaimumxInsertBatch)), batches.Count());
|
||||
var resultCount = 0;
|
||||
foreach (var b in batches)
|
||||
{
|
||||
resultCount += b.Count();
|
||||
}
|
||||
Assert.AreEqual(count, resultCount);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChunkOne()
|
||||
{
|
||||
var items = new List<ITableEntity>();
|
||||
items.Add(new TableEntity());
|
||||
|
||||
var name = Guid.NewGuid().ToString();
|
||||
var t = new TableStorage(name, ConnectionString);
|
||||
|
||||
var batches = t.Chunk<ITableEntity>(items);
|
||||
Assert.AreEqual(1, batches.Count());
|
||||
Assert.AreEqual(1, batches.First().Count());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChunkNone()
|
||||
{
|
||||
var items = new List<ITableEntity>();
|
||||
|
||||
var name = Guid.NewGuid().ToString();
|
||||
var t = new TableStorage(name, ConnectionString);
|
||||
|
||||
var batches = t.Chunk<ITableEntity>(items);
|
||||
Assert.AreEqual(0, batches.Count());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BatchDictionaryOne()
|
||||
{
|
||||
var items = new List<IDictionary<string, object>>();
|
||||
var dic = new Dictionary<string, object>();
|
||||
dic.Add(TableStorage.PartitionKey, Guid.NewGuid().ToString());
|
||||
items.Add(dic);
|
||||
|
||||
var name = Guid.NewGuid().ToString();
|
||||
var t = new TableStorage(name, ConnectionString);
|
||||
|
||||
var batches = t.Batch(items);
|
||||
Assert.AreEqual(1, batches.Count());
|
||||
Assert.AreEqual(1, batches.First().Count());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BatchDictionaryNone()
|
||||
{
|
||||
var items = new List<IDictionary<string, object>>();
|
||||
|
||||
var name = Guid.NewGuid().ToString();
|
||||
var t = new TableStorage(name, ConnectionString);
|
||||
|
||||
var batches = t.Batch(items);
|
||||
Assert.AreEqual(0, batches.Count());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BatchDictionaryThousandsDifferentPartitions()
|
||||
{
|
||||
var random = new Random();
|
||||
var count = random.Next(2001, 10000);
|
||||
var items = new List<IDictionary<string, object>>();
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var dic = new Dictionary<string, object>();
|
||||
dic.Add(TableStorage.PartitionKey, Guid.NewGuid().ToString());
|
||||
items.Add(dic);
|
||||
}
|
||||
|
||||
var name = Guid.NewGuid().ToString();
|
||||
var t = new TableStorage(name, ConnectionString);
|
||||
|
||||
var batches = t.Batch(items);
|
||||
Assert.AreEqual(count, batches.Count());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BatchDictionaryThousands()
|
||||
{
|
||||
var random = new Random();
|
||||
var count = random.Next(2001, 10000);
|
||||
var partition = Guid.NewGuid().ToString();
|
||||
var items = new List<IDictionary<string, object>>();
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var dic = new Dictionary<string, object>();
|
||||
dic.Add(TableStorage.PartitionKey, partition);
|
||||
items.Add(dic);
|
||||
}
|
||||
|
||||
var name = Guid.NewGuid().ToString();
|
||||
var t = new TableStorage(name, ConnectionString);
|
||||
|
||||
var batches = t.Batch(items);
|
||||
Assert.AreEqual(Math.Ceiling(((double)count / TableStorage.MaimumxInsertBatch)), batches.Count());
|
||||
var resultCount = 0;
|
||||
foreach (var b in batches)
|
||||
{
|
||||
resultCount += b.Count();
|
||||
}
|
||||
Assert.AreEqual(count, resultCount);
|
||||
}
|
||||
}
|
||||
}
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 5.3 KiB |
|
@ -0,0 +1,43 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.26228.4
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B65D0744-3BE9-4B13-A7D4-1D30E2587DD9}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
README.md = README.md
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "King.Azure", "King.Azure\King.Azure.csproj", "{1DE3D5A9-273D-4758-813D-3CEA7DA096E2}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "King.Azure.Test", "King.Azure.Test\King.Azure.Test.csproj", "{91EDFF1C-A9C5-4F92-B3E9-781651577D75}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|x64 = Debug|x64
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|x64 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{1DE3D5A9-273D-4758-813D-3CEA7DA096E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1DE3D5A9-273D-4758-813D-3CEA7DA096E2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1DE3D5A9-273D-4758-813D-3CEA7DA096E2}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{1DE3D5A9-273D-4758-813D-3CEA7DA096E2}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{1DE3D5A9-273D-4758-813D-3CEA7DA096E2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1DE3D5A9-273D-4758-813D-3CEA7DA096E2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1DE3D5A9-273D-4758-813D-3CEA7DA096E2}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{1DE3D5A9-273D-4758-813D-3CEA7DA096E2}.Release|x64.Build.0 = Release|Any CPU
|
||||
{91EDFF1C-A9C5-4F92-B3E9-781651577D75}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{91EDFF1C-A9C5-4F92-B3E9-781651577D75}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{91EDFF1C-A9C5-4F92-B3E9-781651577D75}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{91EDFF1C-A9C5-4F92-B3E9-781651577D75}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{91EDFF1C-A9C5-4F92-B3E9-781651577D75}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{91EDFF1C-A9C5-4F92-B3E9-781651577D75}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{91EDFF1C-A9C5-4F92-B3E9-781651577D75}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{91EDFF1C-A9C5-4F92-B3E9-781651577D75}.Release|x64.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,56 @@
|
|||
namespace King.Azure.Data
|
||||
{
|
||||
using Microsoft.WindowsAzure.Storage;
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// Azure Storage
|
||||
/// </summary>
|
||||
public class AzureStorage : IStorageAccount
|
||||
{
|
||||
#region Members
|
||||
/// <summary>
|
||||
/// Cloud Storage Account
|
||||
/// </summary>
|
||||
private readonly CloudStorageAccount account;
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="connectionString">Connection String</param>
|
||||
public AzureStorage(string connectionString)
|
||||
: this(CloudStorageAccount.Parse(connectionString))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="account">Storage Account</param>
|
||||
public AzureStorage(CloudStorageAccount account)
|
||||
{
|
||||
if (null == account)
|
||||
{
|
||||
throw new ArgumentNullException("account");
|
||||
}
|
||||
|
||||
this.account = account;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// Cloud Storage Account
|
||||
/// </summary>
|
||||
public CloudStorageAccount Account
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.account;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,142 @@
|
|||
namespace King.Azure.Data
|
||||
{
|
||||
using Microsoft.WindowsAzure.Storage.Blob;
|
||||
using Microsoft.WindowsAzure.Storage.Queue;
|
||||
using Microsoft.WindowsAzure.Storage.Table;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
/// <summary>
|
||||
/// Azure Storage Resources
|
||||
/// </summary>
|
||||
public class AzureStorageResources : AzureStorage, IAzureStorageResources
|
||||
{
|
||||
#region Constructors
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="connectionString">Storage Account</param>
|
||||
public AzureStorageResources(string connectionString)
|
||||
: base(connectionString)
|
||||
{
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
/// <summary>
|
||||
/// List Table Names
|
||||
/// </summary>
|
||||
/// <returns>Table Names</returns>
|
||||
public virtual async Task<IEnumerable<string>> TableNames()
|
||||
{
|
||||
TableContinuationToken token = null;
|
||||
var names = new List<string>();
|
||||
|
||||
var client = base.Account.CreateCloudTableClient();
|
||||
|
||||
do
|
||||
{
|
||||
var segments = await client.ListTablesSegmentedAsync(token);
|
||||
names.AddRange(segments.Results.Select(s => s.Name));
|
||||
token = segments.ContinuationToken;
|
||||
}
|
||||
while (null != token);
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List Tables
|
||||
/// </summary>
|
||||
/// <returns>Tables</returns>
|
||||
public virtual async Task<IEnumerable<ITableStorage>> Tables()
|
||||
{
|
||||
var tables = new List<ITableStorage>();
|
||||
|
||||
var names = await this.TableNames();
|
||||
foreach (var name in names)
|
||||
{
|
||||
tables.Add(new TableStorage(name, base.Account));
|
||||
}
|
||||
|
||||
return tables;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List Container Names
|
||||
/// </summary>
|
||||
/// <returns>Container Names</returns>
|
||||
public virtual async Task<IEnumerable<string>> ContainerNames()
|
||||
{
|
||||
BlobContinuationToken token = null;
|
||||
var names = new List<string>();
|
||||
|
||||
var client = base.Account.CreateCloudBlobClient();
|
||||
|
||||
do
|
||||
{
|
||||
var segments = await client.ListContainersSegmentedAsync(token);
|
||||
names.AddRange(segments.Results.Select(s => s.Name));
|
||||
token = segments.ContinuationToken;
|
||||
}
|
||||
while (null != token);
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List Containers
|
||||
/// </summary>
|
||||
/// <returns>Containers</returns>
|
||||
public virtual async Task<IEnumerable<IContainer>> Containers()
|
||||
{
|
||||
var containers = new List<IContainer>();
|
||||
var names = await this.ContainerNames();
|
||||
foreach (var name in names)
|
||||
{
|
||||
containers.Add(new Container(name, base.Account));
|
||||
}
|
||||
|
||||
return containers;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List Queue Names
|
||||
/// </summary>
|
||||
/// <returns>Queue Names</returns>
|
||||
public virtual async Task<IEnumerable<string>> QueueNames()
|
||||
{
|
||||
QueueContinuationToken token = null;
|
||||
var names = new List<string>();
|
||||
|
||||
var client = base.Account.CreateCloudQueueClient();
|
||||
|
||||
do
|
||||
{
|
||||
var segments = await client.ListQueuesSegmentedAsync(token);
|
||||
names.AddRange(segments.Results.Select(s => s.Name));
|
||||
token = segments.ContinuationToken;
|
||||
}
|
||||
while (null != token);
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List Queues
|
||||
/// </summary>
|
||||
/// <returns>Queues</returns>
|
||||
public virtual async Task<IEnumerable<IStorageQueue>> Queues()
|
||||
{
|
||||
var queues = new List<IStorageQueue>();
|
||||
var names = await this.QueueNames();
|
||||
foreach (var name in names)
|
||||
{
|
||||
queues.Add(new StorageQueue(name, base.Account));
|
||||
}
|
||||
|
||||
return queues;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,561 @@
|
|||
namespace King.Azure.Data
|
||||
{
|
||||
using Microsoft.WindowsAzure.Storage;
|
||||
using Microsoft.WindowsAzure.Storage.Blob;
|
||||
using Microsoft.WindowsAzure.Storage.RetryPolicies;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
/// <summary>
|
||||
/// Blob Container
|
||||
/// </summary>
|
||||
public class Container : AzureStorage, IContainer
|
||||
{
|
||||
#region Members
|
||||
/// <summary>
|
||||
/// Default Cache Duration
|
||||
/// </summary>
|
||||
public const uint DefaultCacheDuration = 31536000;
|
||||
|
||||
/// <summary>
|
||||
/// Client
|
||||
/// </summary>
|
||||
private readonly CloudBlobClient client;
|
||||
|
||||
/// <summary>
|
||||
/// Reference
|
||||
/// </summary>
|
||||
private readonly CloudBlobContainer reference;
|
||||
|
||||
/// <summary>
|
||||
/// Is Public
|
||||
/// </summary>
|
||||
private readonly bool isPublic = false;
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
/// <summary>
|
||||
/// Container Constructor
|
||||
/// </summary>
|
||||
/// <param name="name">Name</param>
|
||||
/// <param name="connectionString">Connection String</param>
|
||||
/// <param name="isPublic">Is Public</param>
|
||||
/// <param name="location">Location Mode</param>
|
||||
public Container(string name, string connectionString, bool isPublic = false, LocationMode location = LocationMode.PrimaryThenSecondary)
|
||||
: this(name, CloudStorageAccount.Parse(connectionString), isPublic, location)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Container Constructor
|
||||
/// </summary>
|
||||
/// <param name="name">Name</param>
|
||||
/// <param name="account">Storage Account</param>
|
||||
/// <param name="isPublic">Is Public</param>
|
||||
/// <param name="location">Location Mode</param>
|
||||
public Container(string name, CloudStorageAccount account, bool isPublic = false, LocationMode location = LocationMode.PrimaryThenSecondary)
|
||||
: base(account)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
throw new ArgumentException("name");
|
||||
}
|
||||
|
||||
this.client = this.Account.CreateCloudBlobClient();
|
||||
this.client.DefaultRequestOptions.LocationMode = location;
|
||||
this.reference = this.client.GetContainerReference(name);
|
||||
this.isPublic = isPublic;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// Name
|
||||
/// </summary>
|
||||
public virtual string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.reference.Name;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is Public
|
||||
/// </summary>
|
||||
public virtual bool IsPublic
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.isPublic;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Client
|
||||
/// </summary>
|
||||
public virtual CloudBlobClient Client
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.client;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reference
|
||||
/// </summary>
|
||||
public virtual CloudBlobContainer Reference
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.reference;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
/// <summary>
|
||||
/// Create If Not Exists
|
||||
/// </summary>
|
||||
/// <returns>Created</returns>
|
||||
public virtual async Task<bool> CreateIfNotExists()
|
||||
{
|
||||
var result = await this.reference.CreateIfNotExistsAsync();
|
||||
if (result)
|
||||
{
|
||||
var permissions = new BlobContainerPermissions()
|
||||
{
|
||||
PublicAccess = this.isPublic ? BlobContainerPublicAccessType.Blob : BlobContainerPublicAccessType.Off
|
||||
};
|
||||
|
||||
await this.reference.SetPermissionsAsync(permissions);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete Container
|
||||
/// </summary>
|
||||
/// <returns>Task</returns>
|
||||
public virtual async Task Delete()
|
||||
{
|
||||
await this.reference.DeleteAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete from Blob Storage
|
||||
/// </summary>
|
||||
/// <param name="blobName">Blob Name</param>
|
||||
/// <param name="deleteHistory">Delete History (Snapshots)</param>
|
||||
/// <returns>Object</returns>
|
||||
public virtual async Task Delete(string blobName, bool deleteHistory = true)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(blobName))
|
||||
{
|
||||
throw new ArgumentException("blobName");
|
||||
}
|
||||
|
||||
var delSnapshots = deleteHistory ? DeleteSnapshotsOption.IncludeSnapshots : DeleteSnapshotsOption.None;
|
||||
var blob = this.GetBlockReference(blobName);
|
||||
await blob.DeleteAsync(delSnapshots, AccessCondition.GenerateEmptyCondition(), new BlobRequestOptions(), new OperationContext());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Blob Exists
|
||||
/// </summary>
|
||||
/// <param name="blobName">Blob Name</param>
|
||||
/// <returns>bool</returns>
|
||||
public virtual async Task<bool> Exists(string blobName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(blobName))
|
||||
{
|
||||
throw new ArgumentException("blobName");
|
||||
}
|
||||
|
||||
var blob = this.GetBlockReference(blobName);
|
||||
return await blob.ExistsAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save Object as Json to Blob Storage
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type</typeparam>
|
||||
/// <param name="blobName">Blob Name</param>
|
||||
/// <param name="obj">Object</param>
|
||||
/// <returns>Task</returns>
|
||||
public virtual async Task Save(string blobName, object obj)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(blobName))
|
||||
{
|
||||
throw new ArgumentException("blobName");
|
||||
}
|
||||
if (null == obj)
|
||||
{
|
||||
throw new ArgumentNullException("obj");
|
||||
}
|
||||
|
||||
var json = JsonConvert.SerializeObject(obj);
|
||||
|
||||
await this.Save(blobName, json, "application/json");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save Text
|
||||
/// </summary>
|
||||
/// <param name="blobName">Blob Name</param>
|
||||
/// <param name="text">Text</param>
|
||||
/// <param name="contentType">Content Type</param>
|
||||
/// <returns>Task</returns>
|
||||
public virtual async Task Save(string blobName, string text, string contentType = "text/plain")
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(blobName))
|
||||
{
|
||||
throw new ArgumentException("blobName");
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
throw new ArgumentException("text");
|
||||
}
|
||||
|
||||
var blob = this.GetBlockReference(blobName);
|
||||
var cacheProperties = await this.Properties(blobName);
|
||||
|
||||
await blob.UploadTextAsync(text);
|
||||
|
||||
await this.Set(blob, cacheProperties, contentType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set Properties on Blob
|
||||
/// </summary>
|
||||
/// <param name="blob">Blob</param>
|
||||
/// <param name="cached">Cached Properties</param>
|
||||
/// <param name="type">Content Type</param>
|
||||
/// <param name="cacheControl">Cache Control</param>
|
||||
/// <param name="disposition">Content Disposition</param>
|
||||
/// <param name="encoding">Content Encoding</param>
|
||||
/// <param name="language">Content Language</param>
|
||||
/// <returns></returns>
|
||||
public virtual async Task Set(CloudBlockBlob blob, BlobProperties cached, string type = null, string cacheControl = null, string disposition = null, string encoding = null, string language = null)
|
||||
{
|
||||
await blob.FetchAttributesAsync();
|
||||
|
||||
if (null != cached)
|
||||
{
|
||||
blob.Properties.CacheControl = cached.CacheControl;
|
||||
blob.Properties.ContentDisposition = cached.ContentDisposition;
|
||||
blob.Properties.ContentEncoding = cached.ContentEncoding;
|
||||
blob.Properties.ContentLanguage = cached.ContentLanguage;
|
||||
blob.Properties.ContentType = cached.ContentType;
|
||||
}
|
||||
|
||||
blob.Properties.CacheControl = string.IsNullOrWhiteSpace(cacheControl) ? blob.Properties.CacheControl : cacheControl;
|
||||
blob.Properties.ContentDisposition = string.IsNullOrWhiteSpace(disposition) ? blob.Properties.ContentDisposition : disposition;
|
||||
blob.Properties.ContentEncoding = string.IsNullOrWhiteSpace(encoding) ? blob.Properties.ContentEncoding : encoding;
|
||||
blob.Properties.ContentLanguage = string.IsNullOrWhiteSpace(language) ? blob.Properties.ContentLanguage : language;
|
||||
blob.Properties.ContentType = string.IsNullOrWhiteSpace(type) ? blob.Properties.ContentType : type;
|
||||
|
||||
await blob.SetPropertiesAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get Object from Blob Storage
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type</typeparam>
|
||||
/// <param name="blobName">Blob Name</param>
|
||||
/// <returns>Object</returns>
|
||||
public virtual async Task<T> Get<T>(string blobName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(blobName))
|
||||
{
|
||||
throw new ArgumentException("blobName");
|
||||
}
|
||||
|
||||
var json = await this.GetText(blobName);
|
||||
return JsonConvert.DeserializeObject<T>(json);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get Bytes
|
||||
/// </summary>
|
||||
/// <param name="blobName">Blob Name</param>
|
||||
/// <returns>bytes</returns>
|
||||
public virtual async Task<byte[]> Get(string blobName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(blobName))
|
||||
{
|
||||
throw new ArgumentException("blobName");
|
||||
}
|
||||
|
||||
var blob = this.GetBlockReference(blobName);
|
||||
await blob.FetchAttributesAsync();
|
||||
|
||||
var bytes = new byte[blob.Properties.Length];
|
||||
await blob.DownloadToByteArrayAsync(bytes, 0);
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get Bytes
|
||||
/// </summary>
|
||||
/// <param name="blobName">Blob Name</param>
|
||||
/// <returns>Text</returns>
|
||||
public virtual async Task<string> GetText(string blobName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(blobName))
|
||||
{
|
||||
throw new ArgumentException("blobName");
|
||||
}
|
||||
|
||||
var blob = this.GetBlockReference(blobName);
|
||||
return await blob.DownloadTextAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save Bytes
|
||||
/// </summary>
|
||||
/// <param name="blobName">Blob Name</param>
|
||||
/// <param name="bytes">bytes</param>
|
||||
/// <returns>Task</returns>
|
||||
public virtual async Task Save(string blobName, byte[] bytes, string contentType = "application/octet-stream")
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(blobName))
|
||||
{
|
||||
throw new ArgumentException("blobName");
|
||||
}
|
||||
if (null == bytes)
|
||||
{
|
||||
throw new ArgumentNullException("bytes");
|
||||
}
|
||||
|
||||
var blob = this.GetBlockReference(blobName);
|
||||
var cached = await this.Properties(blobName);
|
||||
|
||||
await blob.UploadFromByteArrayAsync(bytes, 0, bytes.Length);
|
||||
|
||||
await this.Set(blob, cached, contentType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Blob Properties
|
||||
/// </summary>
|
||||
/// <param name="blobName">Blob Name</param>
|
||||
/// <returns>Blob Container Properties</returns>
|
||||
public virtual async Task<BlobProperties> Properties(string blobName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(blobName))
|
||||
{
|
||||
throw new ArgumentException("blobName");
|
||||
}
|
||||
|
||||
var exists = await this.Exists(blobName);
|
||||
if (exists)
|
||||
{
|
||||
var blob = this.GetBlockReference(blobName);
|
||||
await blob.FetchAttributesAsync();
|
||||
return blob.Properties;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set Cache Control
|
||||
/// </summary>
|
||||
/// <param name="blobName">Blob Name</param>
|
||||
/// <param name="cacheDuration">Cache Duration (Default 1 year)</param>
|
||||
/// <returns>Task</returns>
|
||||
public virtual async Task SetCacheControl(string blobName, uint cacheDuration = DefaultCacheDuration)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(blobName))
|
||||
{
|
||||
throw new ArgumentException("blobName");
|
||||
}
|
||||
|
||||
cacheDuration = cacheDuration < 1 ? DefaultCacheDuration : cacheDuration;
|
||||
|
||||
var blob = this.GetBlockReference(blobName);
|
||||
await this.Set(blob, null, null, string.Format("public, max-age={0}", cacheDuration));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get Reference
|
||||
/// </summary>
|
||||
/// <param name="blobName">Blob Name</param>
|
||||
/// <param name="snapshot">Snapshot time</param>
|
||||
/// <returns>Cloud Block Blob</returns>
|
||||
public virtual CloudBlockBlob GetBlockReference(string blobName, DateTimeOffset? snapshot = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(blobName))
|
||||
{
|
||||
throw new ArgumentException("blobName");
|
||||
}
|
||||
|
||||
return this.reference.GetBlockBlobReference(blobName, snapshot);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get Reference
|
||||
/// </summary>
|
||||
/// <param name="blobName">Blob Name</param>
|
||||
/// <param name="snapshot">Snapshot time</param>
|
||||
/// <returns>Cloud Block Blob</returns>
|
||||
public virtual CloudPageBlob GetPageReference(string blobName, DateTimeOffset? snapshot = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(blobName))
|
||||
{
|
||||
throw new ArgumentException("blobName");
|
||||
}
|
||||
|
||||
return this.reference.GetPageBlobReference(blobName, snapshot);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get Stream
|
||||
/// </summary>
|
||||
/// <param name="blobName">Blob Name</param>
|
||||
/// <returns>Stream</returns>
|
||||
public virtual async Task<Stream> Stream(string blobName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(blobName))
|
||||
{
|
||||
throw new ArgumentException("blobName");
|
||||
}
|
||||
|
||||
var properties = await this.Properties(blobName);
|
||||
var blob = this.GetBlockReference(blobName);
|
||||
var stream = new MemoryStream();
|
||||
await blob.DownloadRangeToStreamAsync(stream, 0, properties.Length);
|
||||
stream.Position = 0;
|
||||
return stream;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List Blobs
|
||||
/// </summary>
|
||||
/// <param name="prefix">Prefix</param>
|
||||
/// <param name="useFlatBlobListing">Use Flat Blob Listing</param>
|
||||
/// <returns>Blobs</returns>
|
||||
public async Task<IEnumerable<IListBlobItem>> List(string prefix = null, bool useFlatBlobListing = true, BlobListingDetails details = BlobListingDetails.All, int? maxResults = int.MaxValue)
|
||||
{
|
||||
BlobContinuationToken token = null;
|
||||
var blobs = new List<IListBlobItem>();
|
||||
var options = new BlobRequestOptions();
|
||||
var operationContext = new OperationContext();
|
||||
|
||||
do
|
||||
{
|
||||
var segments = await this.reference.ListBlobsSegmentedAsync(prefix, useFlatBlobListing, details, maxResults, token, options, operationContext);
|
||||
blobs.AddRange(segments.Results);
|
||||
token = segments.ContinuationToken;
|
||||
}
|
||||
while (null != token);
|
||||
|
||||
return blobs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create Snapshot
|
||||
/// </summary>
|
||||
/// <param name="blobName">Blob Name</param>
|
||||
/// <returns>Task</returns>
|
||||
public virtual async Task<ICloudBlob> Snapshot(string blobName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(blobName))
|
||||
{
|
||||
throw new ArgumentException("blobName");
|
||||
}
|
||||
|
||||
var options = new BlobRequestOptions
|
||||
{
|
||||
LocationMode = LocationMode.PrimaryOnly,
|
||||
};
|
||||
|
||||
var blobs = await this.List(blobName);
|
||||
var blob = blobs.FirstOrDefault();
|
||||
var block = blob as CloudBlockBlob;
|
||||
if (null != block)
|
||||
{
|
||||
return await block.CreateSnapshotAsync(null, null, options, null);
|
||||
}
|
||||
var page = blob as CloudPageBlob;
|
||||
if (null != page)
|
||||
{
|
||||
return await page.CreateSnapshotAsync(null, null, options, null);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy From Blob to Blob
|
||||
/// </summary>
|
||||
/// <param name="from">From</param>
|
||||
/// <param name="to">To</param>
|
||||
/// <returns>Blob Uri</returns>
|
||||
public virtual async Task<string> Copy(string from, string to)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(from))
|
||||
{
|
||||
throw new ArgumentException("Source blob address");
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(to))
|
||||
{
|
||||
throw new ArgumentException("Target blob address");
|
||||
}
|
||||
|
||||
var source = this.GetBlockReference(from);
|
||||
var target = this.GetBlockReference(to);
|
||||
return await target.StartCopyAsync(source);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy from, to seperate container/blob
|
||||
/// </summary>
|
||||
/// <param name="from">From</param>
|
||||
/// <param name="target">Target</param>
|
||||
/// <param name="to">To</param>
|
||||
/// <returns>Blob Uri</returns>
|
||||
public virtual async Task<string> Copy(string from, string target, string to)
|
||||
{
|
||||
return await this.Copy(from, new Container(target, this.Account), to);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy from, to seperate container/blob
|
||||
/// </summary>
|
||||
/// <param name="from">From</param>
|
||||
/// <param name="target">Target</param>
|
||||
/// <param name="to">To</param>
|
||||
/// <returns>Blob Uri</returns>
|
||||
public virtual async Task<string> Copy(string from, IContainer target, string to)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(from))
|
||||
{
|
||||
throw new ArgumentException("from");
|
||||
}
|
||||
if (null == target)
|
||||
{
|
||||
throw new ArgumentNullException("target");
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(to))
|
||||
{
|
||||
throw new ArgumentException("to");
|
||||
}
|
||||
|
||||
var source = this.GetBlockReference(from);
|
||||
var targetBlockBlob = target.GetBlockReference(to);
|
||||
return await targetBlockBlob.StartCopyAsync(source);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
namespace King.Azure.Data
|
||||
{
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.WindowsAzure.Storage;
|
||||
using Microsoft.WindowsAzure.Storage.File;
|
||||
|
||||
/// <summary>
|
||||
/// File Share
|
||||
/// </summary>
|
||||
public class FileShare : AzureStorage, IFileShare
|
||||
{
|
||||
#region Members
|
||||
/// <summary>
|
||||
/// Client
|
||||
/// </summary>
|
||||
private readonly CloudFileClient client;
|
||||
|
||||
/// <summary>
|
||||
/// Reference
|
||||
/// </summary>
|
||||
private readonly CloudFileShare reference;
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
/// <summary>
|
||||
/// File Share Constructor
|
||||
/// </summary>
|
||||
/// <param name="name">Name</param>
|
||||
/// <param name="connectionString">Connection String</param>
|
||||
public FileShare(string name, string connectionString)
|
||||
: base(connectionString)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
throw new ArgumentException("name");
|
||||
}
|
||||
|
||||
this.client = this.Account.CreateCloudFileClient();
|
||||
this.reference = this.client.GetShareReference(name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// File Share Constructor
|
||||
/// </summary>
|
||||
/// <param name="name">Name</param>
|
||||
/// <param name="account">Storage Account</param>
|
||||
public FileShare(string name, CloudStorageAccount account)
|
||||
: base(account)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
throw new ArgumentException("name");
|
||||
}
|
||||
|
||||
this.client = this.Account.CreateCloudFileClient();
|
||||
this.reference = this.client.GetShareReference(name);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// Name
|
||||
/// </summary>
|
||||
public virtual string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.reference.Name;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Client
|
||||
/// </summary>
|
||||
public virtual CloudFileClient Client
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.client;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reference
|
||||
/// </summary>
|
||||
public virtual CloudFileShare Reference
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.reference;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
/// <summary>
|
||||
/// Create If Not Exists
|
||||
/// </summary>
|
||||
/// <returns>Created</returns>
|
||||
public virtual async Task<bool> CreateIfNotExists()
|
||||
{
|
||||
return await this.reference.CreateIfNotExistsAsync();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,688 @@
|
|||
namespace King.Azure.Data
|
||||
{
|
||||
using Microsoft.WindowsAzure.Storage;
|
||||
using Microsoft.WindowsAzure.Storage.Blob;
|
||||
using Microsoft.WindowsAzure.Storage.File;
|
||||
using Microsoft.WindowsAzure.Storage.Queue;
|
||||
using Microsoft.WindowsAzure.Storage.Table;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
#region IAccount
|
||||
/// <summary>
|
||||
/// Azure Storage Account
|
||||
/// </summary>
|
||||
public interface IStorageAccount
|
||||
{
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// Cloud Storage Account
|
||||
/// </summary>
|
||||
CloudStorageAccount Account
|
||||
{
|
||||
get;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IStorageClient
|
||||
/// <summary>
|
||||
/// Storage Client Interface
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public interface IStorageClient<T>
|
||||
{
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// Storage Client
|
||||
/// </summary>
|
||||
T Client
|
||||
{
|
||||
get;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IStorageReference
|
||||
/// <summary>
|
||||
/// Storage Reference Interface
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public interface IStorageReference<T>
|
||||
{
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// Storage Reference
|
||||
/// </summary>
|
||||
T Reference
|
||||
{
|
||||
get;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ITableStorage
|
||||
/// <summary>
|
||||
/// Table Storage Interface
|
||||
/// </summary>
|
||||
public interface ITableStorage : IAzureStorage, IStorageReference<CloudTable>, IStorageClient<CloudTableClient>
|
||||
{
|
||||
#region Methods
|
||||
/// <summary>
|
||||
/// Create Table
|
||||
/// </summary>
|
||||
/// <param name="tableName">Table Name</param>
|
||||
Task<bool> Create();
|
||||
|
||||
/// <summary>
|
||||
/// Insert or update the record in table
|
||||
/// </summary>
|
||||
/// <param name="entity">Entity</param>
|
||||
Task<TableResult> InsertOrReplace(ITableEntity entity);
|
||||
|
||||
/// <summary>
|
||||
/// Insert Batch
|
||||
/// </summary>
|
||||
/// <param name="entities"></param>
|
||||
Task<IEnumerable<TableResult>> Insert(IEnumerable<ITableEntity> entities);
|
||||
|
||||
/// <summary>
|
||||
/// Insert Or Replace Entity (Dictionary)
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Specify: PartitionKey, RowKey and ETag
|
||||
/// </remarks>
|
||||
/// <param name="entity">Entity</param>
|
||||
/// <returns>Result</returns>
|
||||
Task<TableResult> InsertOrReplace(IDictionary<string, object> entity);
|
||||
|
||||
/// <summary>
|
||||
/// Insert Batch
|
||||
/// </summary>
|
||||
/// <param name="entities">Entities</param>
|
||||
Task<IEnumerable<TableResult>> Insert(IEnumerable<IDictionary<string, object>> entities);
|
||||
|
||||
/// <summary>
|
||||
/// Query By Partition
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="partition"></param>
|
||||
/// <returns></returns>
|
||||
Task<IEnumerable<T>> QueryByPartition<T>(string partition)
|
||||
where T : ITableEntity, new();
|
||||
|
||||
/// <summary>
|
||||
/// Query By Partition
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Without providing the partion this query may not perform well.
|
||||
/// </remarks>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="rowKey"></param>
|
||||
/// <returns></returns>
|
||||
Task<IEnumerable<T>> QueryByRow<T>(string rowKey)
|
||||
where T : ITableEntity, new();
|
||||
|
||||
/// <summary>
|
||||
/// Query By Partition
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="rowKey"></param>
|
||||
/// <returns></returns>
|
||||
Task<T> QueryByPartitionAndRow<T>(string partitionKey, string rowKey)
|
||||
where T : ITableEntity, new();
|
||||
|
||||
/// <summary>
|
||||
/// Query
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type</typeparam>
|
||||
/// <param name="query">Table Query</param>
|
||||
/// <returns>Results</returns>
|
||||
Task<IEnumerable<T>> Query<T>(TableQuery<T> query)
|
||||
where T : ITableEntity, new();
|
||||
|
||||
/// <summary>
|
||||
/// Query by Expression
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Table Entity</typeparam>
|
||||
/// <param name="predicate">Predicate</param>
|
||||
/// <param name="maxResults">Max Result</param>
|
||||
/// <returns></returns>
|
||||
Task<IEnumerable<T>> Query<T>(Func<T, bool> predicate, int maxResults = int.MaxValue)
|
||||
where T : ITableEntity, new();
|
||||
|
||||
/// <summary>
|
||||
/// Query By Partition
|
||||
/// </summary>
|
||||
/// <param name="partitionKey"></param>
|
||||
/// <returns>Entities</returns>
|
||||
Task<IEnumerable<IDictionary<string, object>>> QueryByPartition(string partitionKey);
|
||||
|
||||
/// <summary>
|
||||
/// Query By Partition
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Without providing the partion this query may not perform well.
|
||||
/// </remarks>
|
||||
/// <param name="rowKey">Row Key</param>
|
||||
/// <returns>Entities</returns>
|
||||
Task<IEnumerable<IDictionary<string, object>>> QueryByRow(string rowKey);
|
||||
|
||||
/// <summary>
|
||||
/// Query By Partition and Row
|
||||
/// </summary>
|
||||
/// <param name="partitionKey">Partition Key</param>
|
||||
/// <param name="rowKey">Row</param>
|
||||
/// <returns></returns>
|
||||
Task<IDictionary<string, object>> QueryByPartitionAndRow(string partitionKey, string rowKey);
|
||||
|
||||
/// <summary>
|
||||
/// Generic Query
|
||||
/// </summary>
|
||||
/// <param name="query">Query</param>
|
||||
/// <returns>Entities</returns>
|
||||
Task<IEnumerable<IDictionary<string, object>>> Query(TableQuery query);
|
||||
|
||||
/// <summary>
|
||||
/// Delete By Partition
|
||||
/// </summary>
|
||||
/// <param name="partitionKey">Partition Key</param>
|
||||
/// <returns>Task</returns>
|
||||
Task DeleteByPartition(string partitionKey);
|
||||
|
||||
/// <summary>
|
||||
/// Delete By Row
|
||||
/// </summary>
|
||||
/// <param name="rowKey">Row Key</param>
|
||||
/// <returns>Task</returns>
|
||||
Task DeleteByRow(string rowKey);
|
||||
|
||||
/// <summary>
|
||||
/// Delete By Partition and Row
|
||||
/// </summary>
|
||||
/// <param name="partitionKey">Partition Key</param>
|
||||
/// <param name="rowKey"></param>
|
||||
/// <returns>Task</returns>
|
||||
Task DeleteByPartitionAndRow(string partitionKey, string row);
|
||||
|
||||
/// <summary>
|
||||
/// Delete Entity
|
||||
/// </summary>
|
||||
/// <param name="entity">Entity</param>
|
||||
/// <returns>Task</returns>
|
||||
Task<TableResult> Delete(ITableEntity entity);
|
||||
|
||||
/// <summary>
|
||||
/// Delete Entities
|
||||
/// </summary>
|
||||
/// <param name="entities">Entities</param>
|
||||
/// <returns>Table Results</returns>
|
||||
Task<IEnumerable<TableResult>> Delete(IEnumerable<ITableEntity> entities);
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IContainer
|
||||
/// <summary>
|
||||
/// Blob Container
|
||||
/// </summary>
|
||||
public interface IContainer : IAzureStorage, IStorageReference<CloudBlobContainer>, IStorageClient<CloudBlobClient>
|
||||
{
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// Is Public
|
||||
/// </summary>
|
||||
bool IsPublic
|
||||
{
|
||||
get;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
/// <summary>
|
||||
/// Blob Exists
|
||||
/// </summary>
|
||||
/// <param name="blobName">Blob Name</param>
|
||||
/// <returns>bool</returns>
|
||||
Task<bool> Exists(string blobName);
|
||||
|
||||
/// <summary>
|
||||
/// Delete from Blob Storage
|
||||
/// </summary>
|
||||
/// <param name="blobName">Blob Name</param>
|
||||
/// <param name="deleteHistory">Delete History (Snapshots)</param>
|
||||
/// <returns>Object</returns>
|
||||
Task Delete(string blobName, bool deleteHistory = true);
|
||||
|
||||
/// <summary>
|
||||
/// Save Object as Json to Blob Storage
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type</typeparam>
|
||||
/// <param name="blobName">Blob Name</param>
|
||||
/// <param name="obj">Object</param>
|
||||
/// <returns>Task</returns>
|
||||
Task Save(string blobName, object obj);
|
||||
|
||||
/// <summary>
|
||||
/// Get Object from Blob Storage
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type</typeparam>
|
||||
/// <param name="blobName">Blob Name</param>
|
||||
/// <returns>Object</returns>
|
||||
Task<T> Get<T>(string blobName);
|
||||
|
||||
/// <summary>
|
||||
/// Stream Blob
|
||||
/// </summary>
|
||||
/// <param name="blobName">Blob Name</param>
|
||||
/// <returns>Stream</returns>
|
||||
Task<Stream> Stream(string blobName);
|
||||
|
||||
/// <summary>
|
||||
/// Get Reference
|
||||
/// </summary>
|
||||
/// <param name="blobName">Blob Name</param>
|
||||
/// <param name="snapshot">Snapshot time</param>
|
||||
/// <returns>Cloud Blob</returns>
|
||||
CloudBlockBlob GetBlockReference(string blobName, DateTimeOffset? snapshot = null);
|
||||
|
||||
/// <summary>
|
||||
/// Get Reference
|
||||
/// </summary>
|
||||
/// <param name="blobName">Blob Name</param>
|
||||
/// <param name="snapshot">Snapshot time</param>
|
||||
/// <returns>Cloud Blob</returns>
|
||||
CloudPageBlob GetPageReference(string blobName, DateTimeOffset? snapshot = null);
|
||||
|
||||
/// <summary>
|
||||
/// Save Binary Data
|
||||
/// </summary>
|
||||
/// <param name="blobName">Blob Name</param>
|
||||
/// <param name="bytes">Bytes</param>
|
||||
/// <param name="contentType">Content Type</param>
|
||||
/// <returns>Task</returns>
|
||||
Task Save(string blobName, byte[] bytes, string contentType = "application/octet-stream");
|
||||
|
||||
/// <summary>
|
||||
/// Save Text
|
||||
/// </summary>
|
||||
/// <param name="blobName">Blob Name</param>
|
||||
/// <param name="text">Text</param>
|
||||
/// <param name="contentType">Content Type</param>
|
||||
/// <returns>Task</returns>
|
||||
Task Save(string blobName, string text, string contentType = "text/plain");
|
||||
|
||||
/// <summary>
|
||||
/// Get Binary Data
|
||||
/// </summary>
|
||||
/// <param name="blobName">Blob Name</param>
|
||||
/// <returns>Bytes</returns>
|
||||
Task<byte[]> Get(string blobName);
|
||||
|
||||
/// <summary>
|
||||
/// Get Bytes
|
||||
/// </summary>
|
||||
/// <param name="blobName">Blob Name</param>
|
||||
/// <returns>Text</returns>
|
||||
Task<string> GetText(string blobName);
|
||||
|
||||
/// <summary>
|
||||
/// Blob Properties
|
||||
/// </summary>
|
||||
/// <param name="blobName">Blob Name</param>
|
||||
/// <returns>Blob Container Properties</returns>
|
||||
Task<BlobProperties> Properties(string blobName);
|
||||
|
||||
/// <summary>
|
||||
/// Set Cache Control
|
||||
/// </summary>
|
||||
/// <param name="blobName">Blob Name</param>
|
||||
/// <param name="cacheDuration">Cache Duration (Default 1 year)</param>
|
||||
/// <returns>Task</returns>
|
||||
Task SetCacheControl(string blobName, uint cacheDuration = Container.DefaultCacheDuration);
|
||||
|
||||
/// <summary>
|
||||
/// List Blobs
|
||||
/// </summary>
|
||||
/// <param name="prefix">Prefix</param>
|
||||
/// <param name="useFlatBlobListing">Use Flat Blob Listing</param>
|
||||
/// <returns>Blobs</returns>
|
||||
Task<IEnumerable<IListBlobItem>> List(string prefix = null, bool useFlatBlobListing = true, BlobListingDetails details = BlobListingDetails.All, int? maxResults = int.MaxValue);
|
||||
|
||||
/// <summary>
|
||||
/// Create Snapshot
|
||||
/// </summary>
|
||||
/// <param name="blobName">Blob Name</param>
|
||||
/// <returns>Task</returns>
|
||||
Task<ICloudBlob> Snapshot(string blobName);
|
||||
|
||||
/// <summary>
|
||||
/// Copy from, to seperate container/blob
|
||||
/// </summary>
|
||||
/// <param name="from">From</param>
|
||||
/// <param name="target">Target</param>
|
||||
/// <param name="to">To</param>
|
||||
/// <returns>Blob Uri</returns>
|
||||
Task<string> Copy(string from, IContainer target, string to);
|
||||
|
||||
/// <summary>
|
||||
/// Copy from, to seperate container/blob
|
||||
/// </summary>
|
||||
/// <param name="from">From</param>
|
||||
/// <param name="target">Target</param>
|
||||
/// <param name="to">To</param>
|
||||
/// <returns>Blob Uri</returns>
|
||||
Task<string> Copy(string from, string target, string to);
|
||||
|
||||
/// <summary>
|
||||
/// Copy From Blob to Blob
|
||||
/// </summary>
|
||||
/// <param name="from">From</param>
|
||||
/// <param name="to">To</param>
|
||||
/// <returns>Blob Uri</returns>
|
||||
Task<string> Copy(string from, string to);
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IQueueObject
|
||||
/// <summary>
|
||||
/// Queue Object Interface
|
||||
/// </summary>
|
||||
public interface IQueueObject
|
||||
{
|
||||
#region Methods
|
||||
/// <summary>
|
||||
/// Save Specific Message to Queue
|
||||
/// </summary>
|
||||
/// <param name="obj">Object</param>
|
||||
/// <returns>Task</returns>
|
||||
Task Send(object obj);
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IQueue<T>
|
||||
/// <summary>
|
||||
/// IQueue
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public interface IQueue<T>
|
||||
{
|
||||
#region Methods
|
||||
/// <summary>
|
||||
/// Get Cloud Queue Message
|
||||
/// </summary>
|
||||
/// <returns>Message</returns>
|
||||
Task<T> Get();
|
||||
|
||||
/// <summary>
|
||||
/// Delete Message from Queue
|
||||
/// </summary>
|
||||
/// <param name="message">Message</param>
|
||||
/// <returns>Task</returns>
|
||||
Task Delete(T message);
|
||||
|
||||
/// <summary>
|
||||
/// Save Message to Queue
|
||||
/// </summary>
|
||||
/// <param name="message">Message</param>
|
||||
/// <returns>Task</returns>
|
||||
Task Send(T message);
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IStorageQueue
|
||||
/// <summary>
|
||||
/// Storage Queue Interface
|
||||
/// </summary>
|
||||
public interface IStorageQueue : IQueue<CloudQueueMessage>, IQueueObject, IAzureStorage, IStorageReference<CloudQueue>, IStorageClient<CloudQueueClient>, IQueueCount
|
||||
{
|
||||
#region Methods
|
||||
/// <summary>
|
||||
/// Get Many Cloud Queue Message
|
||||
/// </summary>
|
||||
/// <returns>Messages</returns>
|
||||
Task<IEnumerable<CloudQueueMessage>> GetMany(int messageCount = 5);
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IQueueCount
|
||||
/// <summary>
|
||||
/// Queue Count
|
||||
/// </summary>
|
||||
public interface IQueueCount
|
||||
{
|
||||
#region Methods
|
||||
/// <summary>
|
||||
/// Approixmate Message Count
|
||||
/// </summary>
|
||||
/// <returns>Message Count</returns>
|
||||
Task<long?> ApproixmateMessageCount();
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IAzureStorage
|
||||
/// <summary>
|
||||
/// Azure Storage
|
||||
/// </summary>
|
||||
public interface IAzureStorage
|
||||
{
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// Name
|
||||
/// </summary>
|
||||
string Name
|
||||
{
|
||||
get;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
/// <summary>
|
||||
/// Create If Not Exists
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Task<bool> CreateIfNotExists();
|
||||
|
||||
/// <summary>
|
||||
/// Delete Item
|
||||
/// </summary>
|
||||
/// <returns>Task</returns>
|
||||
Task Delete();
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IProcessor
|
||||
/// <summary>
|
||||
/// IProcessor
|
||||
/// </summary>
|
||||
public interface IProcessor<T>
|
||||
{
|
||||
#region Methods
|
||||
/// <summary>
|
||||
/// Process Data
|
||||
/// </summary>
|
||||
/// <param name="data">Data to Process</param>
|
||||
/// <returns>Successful</returns>
|
||||
Task<bool> Process(T data);
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IPoller
|
||||
/// <summary>
|
||||
/// Store Poller Interface
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Dequeue Type</typeparam>
|
||||
public interface IPoller<T>
|
||||
{
|
||||
#region Methods
|
||||
/// <summary>
|
||||
/// Poll for Queued Message
|
||||
/// </summary>
|
||||
/// <returns>Queued Item</returns>
|
||||
Task<IQueued<T>> Poll();
|
||||
|
||||
/// <summary>
|
||||
/// Poll for Queued Message
|
||||
/// </summary>
|
||||
/// <returns>Queued Item</returns>
|
||||
Task<IEnumerable<IQueued<T>>> PollMany(int messageCount = 5);
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IStorageQueuePoller
|
||||
/// <summary>
|
||||
/// Storage Queue Poller Interface
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Dequeue Type</typeparam>
|
||||
public interface IStorageQueuePoller<T> : IPoller<T>
|
||||
{
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// Storage Queue
|
||||
/// </summary>
|
||||
IStorageQueue Queue
|
||||
{
|
||||
get;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IQueued
|
||||
/// <summary>
|
||||
/// IQueued
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public interface IQueued<T>
|
||||
{
|
||||
#region Methods
|
||||
/// <summary>
|
||||
/// Delete Message
|
||||
/// </summary>
|
||||
/// <returns>Task</returns>
|
||||
Task Complete();
|
||||
|
||||
/// <summary>
|
||||
/// Abandon Message
|
||||
/// </summary>
|
||||
/// <returns>Task</returns>
|
||||
Task Abandon();
|
||||
|
||||
/// <summary>
|
||||
/// Data
|
||||
/// </summary>
|
||||
/// <returns>Data</returns>
|
||||
Task<T> Data();
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IAzureStorageResources
|
||||
/// <summary>
|
||||
/// Azure Storage Resources Interface
|
||||
/// </summary>
|
||||
public interface IAzureStorageResources
|
||||
{
|
||||
#region Methods
|
||||
/// <summary>
|
||||
/// List Table Names
|
||||
/// </summary>
|
||||
/// <returns>Table Names</returns>
|
||||
Task<IEnumerable<string>> TableNames();
|
||||
|
||||
/// <summary>
|
||||
/// List Tables
|
||||
/// </summary>
|
||||
/// <returns>Tables</returns>
|
||||
Task<IEnumerable<ITableStorage>> Tables();
|
||||
|
||||
/// <summary>
|
||||
/// List Container Names
|
||||
/// </summary>
|
||||
/// <returns>Container Names</returns>
|
||||
Task<IEnumerable<string>> ContainerNames();
|
||||
|
||||
/// <summary>
|
||||
/// List Containers
|
||||
/// </summary>
|
||||
/// <returns>Containers</returns>
|
||||
Task<IEnumerable<IContainer>> Containers();
|
||||
|
||||
/// <summary>
|
||||
/// List Queue Names
|
||||
/// </summary>
|
||||
/// <returns>Queue Names</returns>
|
||||
Task<IEnumerable<string>> QueueNames();
|
||||
|
||||
/// <summary>
|
||||
/// List Queues
|
||||
/// </summary>
|
||||
/// <returns>Queues</returns>
|
||||
Task<IEnumerable<IStorageQueue>> Queues();
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IFileShare
|
||||
/// <summary>
|
||||
/// File Share Interface
|
||||
/// </summary>
|
||||
public interface IFileShare : IStorageReference<CloudFileShare>, IStorageClient<CloudFileClient>
|
||||
{
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region QueueShardSender
|
||||
/// <summary>
|
||||
/// Queue Shard Sender
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public interface IQueueShardSender<T>
|
||||
{
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// Queues
|
||||
/// </summary>
|
||||
IReadOnlyCollection<T> Queues
|
||||
{
|
||||
get;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
/// <summary>
|
||||
/// Queue Message to shard, 0 means at random
|
||||
/// </summary>
|
||||
/// <param name="obj">message</param>
|
||||
/// <param name="shardTarget">Shard Target</param>
|
||||
/// <returns>Task</returns>
|
||||
Task Save(object obj, byte shardTarget = 0);
|
||||
|
||||
/// <summary>
|
||||
/// Create all queues
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Task<bool> CreateIfNotExists();
|
||||
|
||||
/// <summary>
|
||||
/// Delete all queues
|
||||
/// </summary>
|
||||
/// <returns>Task</returns>
|
||||
Task Delete();
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<Description>King.Azure.Data Class Library</Description>
|
||||
<AssemblyTitle>King.Azure</AssemblyTitle>
|
||||
<VersionPrefix>2.0.19</VersionPrefix>
|
||||
<Authors>Jef King</Authors>
|
||||
<TargetFrameworks>netcoreapp1.0;netstandard1.3;net45;net451;net452;net46;net461;net462</TargetFrameworks>
|
||||
<AssemblyName>King.Azure</AssemblyName>
|
||||
<PackageId>King.Azure</PackageId>
|
||||
<PackageTags>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</PackageTags>
|
||||
<PackageReleaseNotes>Updated Dependancies.</PackageReleaseNotes>
|
||||
<PackageIconUrl>https://raw.githubusercontent.com/jefking/King.Azure/master/icon.png</PackageIconUrl>
|
||||
<PackageProjectUrl>https://github.com/jefking/King.Azure</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/jefking/King.Azure/blob/master/LICENSE</PackageLicenseUrl>
|
||||
<PackageTargetFallback Condition=" '$(TargetFramework)' == 'netcoreapp1.0' ">$(PackageTargetFallback);portable-net451+win8</PackageTargetFallback>
|
||||
<PackageTargetFallback Condition=" '$(TargetFramework)' == 'netstandard1.3' ">$(PackageTargetFallback);portable-net451+win8</PackageTargetFallback>
|
||||
<GenerateAssemblyTitleAttribute>false</GenerateAssemblyTitleAttribute>
|
||||
<GenerateAssemblyDescriptionAttribute>false</GenerateAssemblyDescriptionAttribute>
|
||||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
|
||||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
|
||||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
|
||||
<GenerateAssemblyCopyrightAttribute>false</GenerateAssemblyCopyrightAttribute>
|
||||
<GenerateAssemblyVersionAttribute>false</GenerateAssemblyVersionAttribute>
|
||||
<GenerateAssemblyFileVersionAttribute>false</GenerateAssemblyFileVersionAttribute>
|
||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||
<ApplicationIcon>icon.ico</ApplicationIcon>
|
||||
<RepositoryUrl>https://github.com/jefking/King.Azure</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|netcoreapp1.0|AnyCPU'">
|
||||
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
|
||||
<TreatSpecificWarningsAsErrors />
|
||||
<DefineConstants>TRACE;RELEASE;NETCOREAPP1_0</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="WindowsAzure.Storage" Version="8.1.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net45' ">
|
||||
<Reference Include="System" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net451' ">
|
||||
<Reference Include="System" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net452' ">
|
||||
<Reference Include="System" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net46' ">
|
||||
<Reference Include="System" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net461' ">
|
||||
<Reference Include="System" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net462' ">
|
||||
<Reference Include="System" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,15 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
[assembly: AssemblyTitle("King.Azure")]
|
||||
[assembly: AssemblyDescription("Azure Storage Simplified")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("King.Azure")]
|
||||
[assembly: AssemblyCopyright("Copyright © Jef King 2017")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
[assembly: Guid("4e7ae8fe-0ee3-44a4-bae7-2ea46062c10e")]
|
||||
[assembly: AssemblyVersion("2.0.0.11")]
|
||||
[assembly: AssemblyFileVersion("2.0.0.11")]
|
|
@ -0,0 +1,211 @@
|
|||
namespace King.Azure.Data
|
||||
{
|
||||
using Microsoft.WindowsAzure.Storage;
|
||||
using Microsoft.WindowsAzure.Storage.Queue;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
/// <summary>
|
||||
/// Storage Queue
|
||||
/// </summary>
|
||||
public class StorageQueue : AzureStorage, IStorageQueue
|
||||
{
|
||||
#region Members
|
||||
/// <summary>
|
||||
/// Cloud Queue Client
|
||||
/// </summary>
|
||||
private readonly CloudQueueClient client;
|
||||
|
||||
/// <summary>
|
||||
/// Cloud Reference
|
||||
/// </summary>
|
||||
private readonly CloudQueue reference;
|
||||
|
||||
/// <summary>
|
||||
/// Visibility Timeout
|
||||
/// </summary>
|
||||
protected readonly TimeSpan? visibilityTimeout = null;
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="name">Name</param>
|
||||
/// <param name="connectionStringKey">Connection String</param>
|
||||
public StorageQueue(string name, string connectionString, TimeSpan? visibilityTimeout = null)
|
||||
: base(connectionString)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
throw new ArgumentException("name");
|
||||
}
|
||||
|
||||
this.client = base.Account.CreateCloudQueueClient();
|
||||
this.reference = client.GetQueueReference(name);
|
||||
this.visibilityTimeout = visibilityTimeout;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="name">Name</param>
|
||||
/// <param name="account">Storage Account</param>
|
||||
public StorageQueue(string name, CloudStorageAccount account, TimeSpan? visibilityTimeout = null)
|
||||
: base(account)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
throw new ArgumentException("name");
|
||||
}
|
||||
|
||||
this.client = base.Account.CreateCloudQueueClient();
|
||||
this.reference = client.GetQueueReference(name);
|
||||
this.visibilityTimeout = visibilityTimeout;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// Table Name
|
||||
/// </summary>
|
||||
public virtual string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.reference.Name;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cloud Queue Client
|
||||
/// </summary>
|
||||
public virtual CloudQueueClient Client
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.client;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cloud Reference
|
||||
/// </summary>
|
||||
public virtual CloudQueue Reference
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.reference;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
/// <summary>
|
||||
/// Create If Not Exists
|
||||
/// </summary>
|
||||
/// <returns>Created</returns>
|
||||
public virtual async Task<bool> CreateIfNotExists()
|
||||
{
|
||||
return await this.reference.CreateIfNotExistsAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete Queue
|
||||
/// </summary>
|
||||
/// <returns>Task</returns>
|
||||
public virtual async Task Delete()
|
||||
{
|
||||
await this.reference.DeleteAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get Cloud Queue Message
|
||||
/// </summary>
|
||||
/// <returns>Message</returns>
|
||||
public virtual async Task<CloudQueueMessage> Get()
|
||||
{
|
||||
return await this.reference.GetMessageAsync(this.visibilityTimeout, null, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Approixmate Message Count
|
||||
/// </summary>
|
||||
/// <returns>Message Count</returns>
|
||||
public virtual async Task<long?> ApproixmateMessageCount()
|
||||
{
|
||||
await this.reference.FetchAttributesAsync();
|
||||
return this.reference.ApproximateMessageCount;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get Many Cloud Queue Message
|
||||
/// </summary>
|
||||
/// <param name="messageCount">Message Count</param>
|
||||
/// <returns>Messages</returns>
|
||||
public virtual async Task<IEnumerable<CloudQueueMessage>> GetMany(int messageCount = 5)
|
||||
{
|
||||
if (0 >= messageCount)
|
||||
{
|
||||
messageCount = 1;
|
||||
}
|
||||
|
||||
return await this.reference.GetMessagesAsync(messageCount, this.visibilityTimeout, null, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save Message to Queue
|
||||
/// </summary>
|
||||
/// <param name="message">Message</param>
|
||||
/// <returns>Task</returns>
|
||||
public virtual async Task Send(CloudQueueMessage message)
|
||||
{
|
||||
if (null == message)
|
||||
{
|
||||
throw new ArgumentNullException("message");
|
||||
}
|
||||
|
||||
await this.reference.AddMessageAsync(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save Model to queue, as json
|
||||
/// </summary>
|
||||
/// <param name="obj">object</param>
|
||||
/// <returns>Task</returns>
|
||||
public virtual async Task Send(object obj)
|
||||
{
|
||||
if (null == obj)
|
||||
{
|
||||
throw new ArgumentNullException("obj");
|
||||
}
|
||||
|
||||
if (obj is CloudQueueMessage)
|
||||
{
|
||||
await this.Send(obj as CloudQueueMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
await this.Send(new CloudQueueMessage(JsonConvert.SerializeObject(obj)));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete Message from Queue
|
||||
/// </summary>
|
||||
/// <param name="message">Message</param>
|
||||
/// <returns>Task</returns>
|
||||
public virtual async Task Delete(CloudQueueMessage message)
|
||||
{
|
||||
if (null == message)
|
||||
{
|
||||
throw new ArgumentNullException("message");
|
||||
}
|
||||
|
||||
await this.reference.DeleteMessageAsync(message);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
namespace King.Azure.Data
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
/// <summary>
|
||||
/// Queue Poller
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type</typeparam>
|
||||
public class StorageQueuePoller<T> : IStorageQueuePoller<T>
|
||||
{
|
||||
#region Members
|
||||
/// <summary>
|
||||
/// Queue
|
||||
/// </summary>
|
||||
protected readonly IStorageQueue queue = null;
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
/// <summary>
|
||||
/// Default Constructor
|
||||
/// </summary>
|
||||
/// <param name="queueName">Queue Name</param>
|
||||
/// <param name="connectionString">Connection String</param>
|
||||
public StorageQueuePoller(string queueName, string connectionString)
|
||||
: this(new StorageQueue(queueName, connectionString))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for Mocking
|
||||
/// </summary>
|
||||
/// <param name="queue">Queue</param>
|
||||
public StorageQueuePoller(IStorageQueue queue)
|
||||
{
|
||||
if (null == queue)
|
||||
{
|
||||
throw new ArgumentNullException("queue");
|
||||
}
|
||||
|
||||
this.queue = queue;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// Storage Queue
|
||||
/// </summary>
|
||||
public virtual IStorageQueue Queue
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.queue;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
/// <summary>
|
||||
/// Poll for Queued Message
|
||||
/// </summary>
|
||||
/// <returns>Queued Item</returns>
|
||||
public virtual async Task<IQueued<T>> Poll()
|
||||
{
|
||||
var msg = await this.queue.Get();
|
||||
return null == msg ? null : new StorageQueuedMessage<T>(this.queue, msg);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Poll for Queued Message
|
||||
/// </summary>
|
||||
/// <returns>Queued Item</returns>
|
||||
public virtual async Task<IEnumerable<IQueued<T>>> PollMany(int messageCount = 5)
|
||||
{
|
||||
messageCount = 0 >= messageCount ? 5 : messageCount;
|
||||
|
||||
var msgs = await this.queue.GetMany(messageCount);
|
||||
if (null == msgs || !msgs.Any())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return msgs.Where(m => m != null).Select(m => new StorageQueuedMessage<T>(this.queue, m));
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,159 @@
|
|||
namespace King.Azure.Data
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
/// <summary>
|
||||
/// Queue Shard Sender
|
||||
/// </summary>
|
||||
public class StorageQueueShards : IQueueShardSender<IStorageQueue>, IAzureStorage
|
||||
{
|
||||
#region Members
|
||||
/// <summary>
|
||||
/// Queues
|
||||
/// </summary>
|
||||
protected readonly IEnumerable<IStorageQueue> queues;
|
||||
|
||||
/// <summary>
|
||||
/// Base of the Name
|
||||
/// </summary>
|
||||
protected readonly string baseName;
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="name">Name</param>
|
||||
/// <param name="connection">Connection</param>
|
||||
/// <param name="shardCount">Shard Count</param>
|
||||
public StorageQueueShards(string name, string connection, byte shardCount = 2)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
throw new ArgumentException("name");
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(connection))
|
||||
{
|
||||
throw new ArgumentException("connection");
|
||||
}
|
||||
|
||||
this.baseName = name;
|
||||
shardCount = shardCount > 0 ? shardCount : (byte)2;
|
||||
|
||||
var qs = new IStorageQueue[shardCount];
|
||||
for (var i = 0; i < shardCount; i++)
|
||||
{
|
||||
var n = string.Format("{0}{1}", this.baseName, i);
|
||||
qs[i] = new StorageQueue(n, connection);
|
||||
}
|
||||
|
||||
this.queues = new ReadOnlyCollection<IStorageQueue>(qs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for mocking
|
||||
/// </summary>
|
||||
/// <param name="queues">Queues</param>
|
||||
public StorageQueueShards(IEnumerable<IStorageQueue> queues)
|
||||
{
|
||||
if (null == queues)
|
||||
{
|
||||
throw new ArgumentNullException("queue");
|
||||
}
|
||||
if (0 == queues.Count())
|
||||
{
|
||||
throw new ArgumentException("Queues length is 0.");
|
||||
}
|
||||
|
||||
this.queues = queues;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// Queues
|
||||
/// </summary>
|
||||
public virtual IReadOnlyCollection<IStorageQueue> Queues
|
||||
{
|
||||
get
|
||||
{
|
||||
return new ReadOnlyCollection<IStorageQueue>(this.queues.ToList());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Name
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.baseName;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
/// <summary>
|
||||
/// Queue Message
|
||||
/// </summary>
|
||||
/// <param name="obj">Message</param>
|
||||
/// <param name="shardTarget">Shard Target</param>
|
||||
/// <returns>Created</returns>
|
||||
public virtual async Task<bool> CreateIfNotExists()
|
||||
{
|
||||
var success = true;
|
||||
foreach (var q in this.queues)
|
||||
{
|
||||
success &= await q.CreateIfNotExists();
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete all queues
|
||||
/// </summary>
|
||||
/// <returns>Task</returns>
|
||||
public virtual async Task Delete()
|
||||
{
|
||||
foreach (var q in this.queues)
|
||||
{
|
||||
await q.Delete();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queue Message to shard, 0 means at random
|
||||
/// </summary>
|
||||
/// <param name="obj">Message</param>
|
||||
/// <param name="shardTarget">Shard Target</param>
|
||||
/// <returns>Task</returns>
|
||||
public virtual async Task Save(object obj, byte shardTarget = 0)
|
||||
{
|
||||
var index = this.Index(shardTarget);
|
||||
var q = this.queues.ElementAt(index);
|
||||
await q.Send(obj);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine index of queues to interact with
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Specifically broken out for testing safety
|
||||
/// </remarks>
|
||||
/// <param name="shardTarget">Shard Target</param>
|
||||
/// <returns>Index</returns>
|
||||
public virtual byte Index(byte shardTarget)
|
||||
{
|
||||
var random = new Random();
|
||||
var count = this.queues.Count();
|
||||
return shardTarget == 0 || shardTarget > count ? (byte)random.Next(0, count) : shardTarget;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
namespace King.Azure.Data
|
||||
{
|
||||
using Microsoft.WindowsAzure.Storage.Queue;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
/// <summary>
|
||||
/// Queued Message
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type</typeparam>
|
||||
public class StorageQueuedMessage<T> : IQueued<T>
|
||||
{
|
||||
#region Members
|
||||
/// <summary>
|
||||
/// Storage Queue
|
||||
/// </summary>
|
||||
protected readonly IStorageQueue queue = null;
|
||||
|
||||
/// <summary>
|
||||
/// Cloud Queue Message
|
||||
/// </summary>
|
||||
protected readonly CloudQueueMessage message = null;
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
/// <summary>
|
||||
/// Default Constructor
|
||||
/// </summary>
|
||||
/// <param name="queue">Queue</param>
|
||||
/// <param name="message">Cloud Queue Message</param>
|
||||
public StorageQueuedMessage(IStorageQueue queue, CloudQueueMessage message)
|
||||
{
|
||||
if (null == queue)
|
||||
{
|
||||
throw new ArgumentNullException("queue");
|
||||
}
|
||||
if (null == message)
|
||||
{
|
||||
throw new ArgumentNullException("message");
|
||||
}
|
||||
|
||||
this.queue = queue;
|
||||
this.message = message;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
/// <summary>
|
||||
/// Delete Message
|
||||
/// </summary>
|
||||
/// <returns>Task</returns>
|
||||
public virtual async Task Complete()
|
||||
{
|
||||
await this.queue.Delete(this.message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Abandon Message
|
||||
/// </summary>
|
||||
/// <returns>Task</returns>
|
||||
public virtual async Task Abandon()
|
||||
{
|
||||
await Task.Factory.StartNew(() => { }); //No Abandon?
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Data
|
||||
/// </summary>
|
||||
/// <returns>Data</returns>
|
||||
public virtual async Task<T> Data()
|
||||
{
|
||||
return await Task.Factory.StartNew(() => JsonConvert.DeserializeObject<T>(this.message.AsString));
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,556 @@
|
|||
namespace King.Azure.Data
|
||||
{
|
||||
using Microsoft.WindowsAzure.Storage;
|
||||
using Microsoft.WindowsAzure.Storage.RetryPolicies;
|
||||
using Microsoft.WindowsAzure.Storage.Table;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
/// <summary>
|
||||
/// Table Storage
|
||||
/// </summary>
|
||||
public class TableStorage : AzureStorage, ITableStorage
|
||||
{
|
||||
#region Members
|
||||
/// <summary>
|
||||
/// Partition Key
|
||||
/// </summary>
|
||||
public const string PartitionKey = "PartitionKey";
|
||||
|
||||
/// <summary>
|
||||
/// Row Key
|
||||
/// </summary>
|
||||
public const string RowKey = "RowKey";
|
||||
|
||||
/// <summary>
|
||||
/// Timestamp
|
||||
/// </summary>
|
||||
public const string Timestamp = "Timestamp";
|
||||
|
||||
/// <summary>
|
||||
/// ETag
|
||||
/// </summary>
|
||||
public const string ETag = "ETag";
|
||||
|
||||
/// <summary>
|
||||
/// Maximum Insert Batch
|
||||
/// </summary>
|
||||
public const int MaimumxInsertBatch = 100;
|
||||
|
||||
/// <summary>
|
||||
/// Table Client
|
||||
/// </summary>
|
||||
private readonly CloudTableClient client;
|
||||
|
||||
/// <summary>
|
||||
/// Table
|
||||
/// </summary>
|
||||
private readonly CloudTable reference;
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
/// <summary>
|
||||
/// Table Storage
|
||||
/// </summary>
|
||||
/// <param name="tableName">Table Name</param>
|
||||
/// <param name="connectionString">Connection String</param>
|
||||
/// <param name="location">Location Mode</param>
|
||||
public TableStorage(string tableName, string connectionString, LocationMode location = LocationMode.PrimaryThenSecondary)
|
||||
: this(tableName, CloudStorageAccount.Parse(connectionString), location)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Table Storage
|
||||
/// </summary>
|
||||
/// <param name="tableName">Table Name</param>
|
||||
/// <param name="account">Storage Account</param>
|
||||
/// <param name="location">Location Mode</param>
|
||||
public TableStorage(string tableName, CloudStorageAccount account, LocationMode location = LocationMode.PrimaryThenSecondary)
|
||||
: base(account)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(tableName))
|
||||
{
|
||||
throw new ArgumentException("tableName");
|
||||
}
|
||||
|
||||
this.client = base.Account.CreateCloudTableClient();
|
||||
this.client.DefaultRequestOptions.LocationMode = location;
|
||||
|
||||
this.reference = client.GetTableReference(tableName);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// Table Name
|
||||
/// </summary>
|
||||
public virtual string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.reference.Name;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Table Client
|
||||
/// </summary>
|
||||
public virtual CloudTableClient Client
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.client;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Table
|
||||
/// </summary>
|
||||
public virtual CloudTable Reference
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.reference;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Create Table
|
||||
/// <summary>
|
||||
/// Create If Not Exists
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public virtual async Task<bool> CreateIfNotExists()
|
||||
{
|
||||
return await this.reference.CreateIfNotExistsAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create Table
|
||||
/// </summary>
|
||||
/// <param name="tableName">Table Name</param>
|
||||
public virtual async Task<bool> Create()
|
||||
{
|
||||
return await this.reference.CreateIfNotExistsAsync();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Delete
|
||||
/// <summary>
|
||||
/// Delete Table
|
||||
/// </summary>
|
||||
/// <param name="tableName"></param>
|
||||
public virtual async Task Delete()
|
||||
{
|
||||
await this.reference.DeleteAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete By Partition
|
||||
/// </summary>
|
||||
/// <param name="partitionKey">Partition Key</param>
|
||||
/// <returns>Task</returns>
|
||||
public virtual async Task DeleteByPartition(string partitionKey)
|
||||
{
|
||||
var entities = await this.QueryByPartition<TableEntity>(partitionKey);
|
||||
if (null != entities && entities.Any())
|
||||
{
|
||||
await this.Delete(entities);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete By Row
|
||||
/// </summary>
|
||||
/// <param name="rowKey">Row Key</param>
|
||||
/// <returns>Task</returns>
|
||||
public virtual async Task DeleteByRow(string rowKey)
|
||||
{
|
||||
var entities = await this.QueryByRow<TableEntity>(rowKey);
|
||||
if (null != entities && entities.Any())
|
||||
{
|
||||
foreach (var entity in entities)
|
||||
{
|
||||
await this.Delete(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete By Partition and Row
|
||||
/// </summary>
|
||||
/// <param name="partitionKey">Partition Key</param>
|
||||
/// <param name="rowKey">Row Key</param>
|
||||
/// <returns>Task</returns>
|
||||
public virtual async Task DeleteByPartitionAndRow(string partitionKey, string rowKey)
|
||||
{
|
||||
var entity = await this.QueryByPartitionAndRow<TableEntity>(partitionKey, rowKey);
|
||||
|
||||
if (null != entity)
|
||||
{
|
||||
await this.Delete(entity);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete Entity
|
||||
/// </summary>
|
||||
/// <param name="entity">Entity</param>
|
||||
/// <returns>Task</returns>
|
||||
public virtual async Task<TableResult> Delete(ITableEntity entity)
|
||||
{
|
||||
if (null == entity)
|
||||
{
|
||||
throw new ArgumentNullException("entity");
|
||||
}
|
||||
|
||||
return await this.reference.ExecuteAsync(TableOperation.Delete(entity));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete Entities
|
||||
/// </summary>
|
||||
/// <param name="entities">Entities</param>
|
||||
/// <returns>Table Results</returns>
|
||||
public virtual async Task<IEnumerable<TableResult>> Delete(IEnumerable<ITableEntity> entities)
|
||||
{
|
||||
if (null == entities)
|
||||
{
|
||||
throw new ArgumentNullException("entities");
|
||||
}
|
||||
if (!entities.Any())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var result = new List<TableResult>();
|
||||
|
||||
foreach (var batch in this.Batch(entities))
|
||||
{
|
||||
var batchOperation = new TableBatchOperation();
|
||||
batch.ToList().ForEach(e => batchOperation.Delete(e));
|
||||
var r = await this.reference.ExecuteBatchAsync(batchOperation);
|
||||
result.AddRange(r);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Save Data
|
||||
/// <summary>
|
||||
/// Insert or update the record in table
|
||||
/// </summary>
|
||||
/// <param name="entity">Entity</param>
|
||||
public virtual async Task<TableResult> InsertOrReplace(ITableEntity entity)
|
||||
{
|
||||
return await this.reference.ExecuteAsync(TableOperation.InsertOrReplace(entity));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Insert Batch
|
||||
/// </summary>
|
||||
/// <param name="entities">Entities</param>
|
||||
public virtual async Task<IEnumerable<TableResult>> Insert(IEnumerable<ITableEntity> entities)
|
||||
{
|
||||
var result = new List<TableResult>();
|
||||
|
||||
foreach (var batch in this.Batch(entities))
|
||||
{
|
||||
var batchOperation = new TableBatchOperation();
|
||||
batch.ToList().ForEach(e => batchOperation.InsertOrReplace(e));
|
||||
var r = await this.reference.ExecuteBatchAsync(batchOperation);
|
||||
result.AddRange(r);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Insert Or Replace Entity (Dictionary)
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Specify: PartitionKey, RowKey and ETag
|
||||
/// </remarks>
|
||||
/// <param name="entity">Entity</param>
|
||||
/// <returns>Result</returns>
|
||||
public virtual async Task<TableResult> InsertOrReplace(IDictionary<string, object> entity)
|
||||
{
|
||||
if (null == entity)
|
||||
{
|
||||
throw new ArgumentNullException("data");
|
||||
}
|
||||
|
||||
var properties = new Dictionary<string, EntityProperty>();
|
||||
entity.Keys.Where(k => k != PartitionKey && k != RowKey && k != ETag).ToList().ForEach(key => properties.Add(key, EntityProperty.CreateEntityPropertyFromObject(entity[key])));
|
||||
|
||||
var partitionKey = entity.Keys.Contains(PartitionKey) ? entity[PartitionKey].ToString() : string.Empty;
|
||||
var rowKey = entity.Keys.Contains(RowKey) ? entity[RowKey].ToString() : string.Empty;
|
||||
var etag = entity.Keys.Contains(ETag) ? entity[ETag].ToString() : null;
|
||||
var dynamicEntity = new DynamicTableEntity(partitionKey, rowKey, etag, properties);
|
||||
|
||||
return await this.InsertOrReplace(dynamicEntity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Insert Batch
|
||||
/// </summary>
|
||||
/// <param name="entities">Entities</param>
|
||||
public virtual async Task<IEnumerable<TableResult>> Insert(IEnumerable<IDictionary<string, object>> entities)
|
||||
{
|
||||
var result = new List<TableResult>();
|
||||
|
||||
foreach (var batch in this.Batch(entities))
|
||||
{
|
||||
var batchOperation = new TableBatchOperation();
|
||||
|
||||
foreach (var entity in batch)
|
||||
{
|
||||
var properties = new Dictionary<string, EntityProperty>();
|
||||
entity.Keys.Where(k => k != PartitionKey && k != RowKey && k != ETag).ToList().ForEach(key => properties.Add(key, EntityProperty.CreateEntityPropertyFromObject(entity[key])));
|
||||
|
||||
var partitionKey = entity.Keys.Contains(PartitionKey) ? entity[PartitionKey].ToString() : string.Empty;
|
||||
var rowKey = entity.Keys.Contains(RowKey) ? entity[RowKey].ToString() : string.Empty;
|
||||
var etag = entity.Keys.Contains(ETag) ? entity[ETag].ToString() : null;
|
||||
|
||||
batchOperation.InsertOrMerge(new DynamicTableEntity(partitionKey, rowKey, etag, properties));
|
||||
}
|
||||
|
||||
var r = await this.reference.ExecuteBatchAsync(batchOperation);
|
||||
result.AddRange(r);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Query Object
|
||||
/// <summary>
|
||||
/// Query By Partition
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Return Type</typeparam>
|
||||
/// <param name="partitionKey"></param>
|
||||
/// <returns>Entities</returns>
|
||||
public virtual async Task<IEnumerable<T>> QueryByPartition<T>(string partitionKey)
|
||||
where T : ITableEntity, new()
|
||||
{
|
||||
var query = new TableQuery<T>().Where(TableQuery.GenerateFilterCondition(PartitionKey, QueryComparisons.Equal, partitionKey));
|
||||
return await this.Query<T>(query);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Query By Partition
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Without providing the partion this query may not perform well.
|
||||
/// </remarks>
|
||||
/// <typeparam name="T">Return Type</typeparam>
|
||||
/// <param name="rowKey">Row Key</param>
|
||||
/// <returns>Entities</returns>
|
||||
public virtual async Task<IEnumerable<T>> QueryByRow<T>(string rowKey)
|
||||
where T : ITableEntity, new()
|
||||
{
|
||||
var query = new TableQuery<T>().Where(TableQuery.GenerateFilterCondition(RowKey, QueryComparisons.Equal, rowKey));
|
||||
return await this.Query<T>(query);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Query By Partition and Row
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Return Type</typeparam>
|
||||
/// <param name="partitionKey">Partition Key</param>
|
||||
/// <param name="rowKey">Row</param>
|
||||
/// <returns></returns>
|
||||
public virtual async Task<T> QueryByPartitionAndRow<T>(string partitionKey, string rowKey)
|
||||
where T : ITableEntity, new()
|
||||
{
|
||||
var partitionFilter = TableQuery.GenerateFilterCondition(PartitionKey, QueryComparisons.Equal, partitionKey);
|
||||
var rowFilter = TableQuery.GenerateFilterCondition(RowKey, QueryComparisons.Equal, rowKey);
|
||||
var filter = TableQuery.CombineFilters(partitionFilter, TableOperators.And, rowFilter);
|
||||
var query = new TableQuery<T>().Where(filter);
|
||||
|
||||
var result = await this.Query<T>(query);
|
||||
return result.FirstOrDefault();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Query by Expression
|
||||
/// </summary>
|
||||
/// <remarks>Filtering is done on client; can be expensive</remarks>
|
||||
/// <typeparam name="T">Return Type</typeparam>
|
||||
/// <param name="predicate">Predicate</param>
|
||||
/// <param name="maxResults">Max Result</param>
|
||||
/// <returns></returns>
|
||||
public virtual async Task<IEnumerable<T>> Query<T>(Func<T, bool> predicate, int maxResults = int.MaxValue)
|
||||
where T : ITableEntity, new()
|
||||
{
|
||||
if (null == predicate)
|
||||
{
|
||||
throw new ArgumentNullException("predicate");
|
||||
}
|
||||
if (0 >= maxResults)
|
||||
{
|
||||
throw new InvalidOperationException("maxResults: must be above 0.");
|
||||
}
|
||||
|
||||
var items = await this.Query<T>(new TableQuery<T>());
|
||||
|
||||
return items.Where(predicate).Take(maxResults);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Query
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Return Type</typeparam>
|
||||
/// <param name="query">Table Query</param>
|
||||
/// <returns>Results</returns>
|
||||
public virtual async Task<IEnumerable<T>> Query<T>(TableQuery<T> query)
|
||||
where T : ITableEntity, new()
|
||||
{
|
||||
if (null == query)
|
||||
{
|
||||
throw new ArgumentNullException("query");
|
||||
}
|
||||
|
||||
var entities = new List<T>();
|
||||
TableContinuationToken token = null;
|
||||
|
||||
do
|
||||
{
|
||||
var queryResult = await this.reference.ExecuteQuerySegmentedAsync<T>(query, token);
|
||||
entities.AddRange(queryResult.Results);
|
||||
token = queryResult.ContinuationToken;
|
||||
}
|
||||
while (null != token);
|
||||
|
||||
return entities;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Query Dictionary
|
||||
/// <summary>
|
||||
/// Query By Partition
|
||||
/// </summary>
|
||||
/// <param name="partitionKey"></param>
|
||||
/// <returns>Entities</returns>
|
||||
public virtual async Task<IEnumerable<IDictionary<string, object>>> QueryByPartition(string partitionKey)
|
||||
{
|
||||
return await this.Query(new TableQuery().Where(TableQuery.GenerateFilterCondition(TableStorage.PartitionKey, QueryComparisons.Equal, partitionKey)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Query By Partition
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Without providing the partion this query may not perform well.
|
||||
/// </remarks>
|
||||
/// <param name="rowKey">Row Key</param>
|
||||
/// <returns>Entities</returns>
|
||||
public virtual async Task<IEnumerable<IDictionary<string, object>>> QueryByRow(string rowKey)
|
||||
{
|
||||
return await this.Query(new TableQuery().Where(TableQuery.GenerateFilterCondition(TableStorage.RowKey, QueryComparisons.Equal, rowKey)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Query By Partition and Row
|
||||
/// </summary>
|
||||
/// <param name="partitionKey">Partition Key</param>
|
||||
/// <param name="rowKey">Row</param>
|
||||
/// <returns></returns>
|
||||
public virtual async Task<IDictionary<string, object>> QueryByPartitionAndRow(string partitionKey, string rowKey)
|
||||
{
|
||||
var partitionFilter = TableQuery.GenerateFilterCondition(TableStorage.PartitionKey, QueryComparisons.Equal, partitionKey);
|
||||
var rowFilter = TableQuery.GenerateFilterCondition(TableStorage.RowKey, QueryComparisons.Equal, rowKey);
|
||||
var filter = TableQuery.CombineFilters(partitionFilter, TableOperators.And, rowFilter);
|
||||
var query = new TableQuery().Where(filter);
|
||||
|
||||
var result = await this.Query(query);
|
||||
return result.FirstOrDefault();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generic Query
|
||||
/// </summary>
|
||||
/// <param name="query">Query</param>
|
||||
/// <returns>Entities</returns>
|
||||
public virtual async Task<IEnumerable<IDictionary<string, object>>> Query(TableQuery query)
|
||||
{
|
||||
if (null == query)
|
||||
{
|
||||
throw new ArgumentNullException("query");
|
||||
}
|
||||
|
||||
var q = new TableQuery<DynamicTableEntity>()
|
||||
{
|
||||
FilterString = query.FilterString,
|
||||
SelectColumns = query.SelectColumns,
|
||||
TakeCount = query.TakeCount
|
||||
};
|
||||
|
||||
var entities = new List<DynamicTableEntity>();
|
||||
TableContinuationToken token = null;
|
||||
|
||||
do
|
||||
{
|
||||
var queryResult = await this.reference.ExecuteQuerySegmentedAsync<DynamicTableEntity>(q, token);
|
||||
entities.AddRange(queryResult.Results);
|
||||
token = queryResult.ContinuationToken;
|
||||
}
|
||||
while (null != token);
|
||||
|
||||
var results = new List<IDictionary<string, object>>();
|
||||
foreach (var e in entities)
|
||||
{
|
||||
var dic = new Dictionary<string, object>();
|
||||
foreach (var p in e.Properties)
|
||||
{
|
||||
dic.Add(p.Key, p.Value.PropertyAsObject);
|
||||
}
|
||||
dic.Add(TableStorage.PartitionKey, e.PartitionKey);
|
||||
dic.Add(TableStorage.RowKey, e.RowKey);
|
||||
dic.Add(TableStorage.ETag, e.ETag);
|
||||
dic.Add(TableStorage.Timestamp, e.Timestamp.DateTime);
|
||||
results.Add(dic);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Additional Methods
|
||||
/// <summary>
|
||||
/// Break Entities into batches
|
||||
/// </summary>
|
||||
/// <param name="entities">Entities</param>
|
||||
/// <returns>Batches</returns>
|
||||
public virtual IEnumerable<IEnumerable<ITableEntity>> Batch(IEnumerable<ITableEntity> entities)
|
||||
{
|
||||
return entities.GroupBy(en => en.PartitionKey).SelectMany(e => this.Chunk<ITableEntity>(e));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Break Entities into batches
|
||||
/// </summary>
|
||||
/// <param name="entities">Entities</param>
|
||||
/// <returns>Batches</returns>
|
||||
public virtual IEnumerable<IEnumerable<IDictionary<string, object>>> Batch(IEnumerable<IDictionary<string, object>> entities)
|
||||
{
|
||||
return entities.GroupBy(en => en[PartitionKey]).SelectMany(e => this.Chunk<IDictionary<string, object>>(e));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Chunk data into smaller blocks
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type</typeparam>
|
||||
/// <param name="entities">Entities</param>
|
||||
/// <returns>Chunks</returns>
|
||||
public virtual IEnumerable<IEnumerable<T>> Chunk<T>(IEnumerable<T> entities)
|
||||
{
|
||||
return entities.Select((x, i) => new { Index = i, Value = x }).GroupBy(x => x.Index / TableStorage.MaimumxInsertBatch).Select(x => x.Select(v => v.Value));
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 5.3 KiB |
42
LICENSE
42
LICENSE
|
@ -1,21 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE
|
||||
MIT License
|
||||
|
||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE
|
22
README.md
22
README.md
|
@ -1,3 +1,19 @@
|
|||
# Contributing
|
||||
|
||||
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.
|
||||
# Contributing
|
||||
|
||||
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.
|
||||
|
||||
## Azure Storage Simplified
|
||||
- Queues/Blobs/Tables/File Shares
|
||||
- Azure Storage Resources
|
||||
- Dependancy Injection
|
||||
- Mockable for testing
|
||||
- Prefer async calls
|
||||
- Plugs into the [King.Service](https://github.com/jefking/King.Service) task framework
|
||||
|
||||
## [NuGet](https://www.nuget.org/packages/King.Azure)
|
||||
```
|
||||
PM> Install-Package King.Azure
|
||||
```
|
||||
|
||||
### [Wiki](https://github.com/jefking/King.Azure/wiki)
|
||||
View the wiki to learn how to use this.
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 5.3 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 3.3 KiB |
Загрузка…
Ссылка в новой задаче