227 lines
9.0 KiB
C#
227 lines
9.0 KiB
C#
using Microsoft.Azure.CosmosDB.Table;
|
|
using Microsoft.Azure.Storage;
|
|
using Serilog;
|
|
using System;
|
|
using System.ComponentModel.DataAnnotations.Schema;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using webapi.Domain.Events;
|
|
using webapi.Domain.SeedWork;
|
|
|
|
namespace webapi.Infrastructure.Repositories
|
|
{
|
|
public class BaseAzureRepository<T>: IBaseRepository<T> where T : Entity
|
|
{
|
|
private static CloudStorageAccount CreateStorageAccountFromConnectionString(string storageConnectionString)
|
|
{
|
|
CloudStorageAccount storageAccount;
|
|
try
|
|
{
|
|
storageAccount = CloudStorageAccount.Parse(storageConnectionString);
|
|
}
|
|
catch (FormatException)
|
|
{
|
|
Console.WriteLine("Invalid storage account information provided. Please confirm the AccountName and AccountKey are valid in the app.config file - then restart the application.");
|
|
throw;
|
|
}
|
|
catch (ArgumentException)
|
|
{
|
|
Console.WriteLine("Invalid storage account information provided. Please confirm the AccountName and AccountKey are valid in the app.config file - then restart the sample.");
|
|
Console.ReadLine();
|
|
throw;
|
|
}
|
|
|
|
return storageAccount;
|
|
}
|
|
|
|
protected readonly CloudTable _table;
|
|
protected readonly ILogger _log;
|
|
private readonly EventsHandlerFactory _eventsFactory;
|
|
|
|
public BaseAzureRepository(ILogger log, string storageConnectionString, EventsHandlerFactory eventsFactory)
|
|
{
|
|
_log = log;
|
|
_eventsFactory = eventsFactory;
|
|
//string storageConnectionString = ConfigurationManager.ConnectionStrings["AzureProdConnectionString"].ConnectionString; //AppSettings.LoadAppSettings().StorageConnectionString;
|
|
|
|
// Retrieve storage account information from connection string.
|
|
CloudStorageAccount storageAccount = CreateStorageAccountFromConnectionString(storageConnectionString);
|
|
|
|
// Create a table client for interacting with the table service
|
|
CloudTableClient tableClient = storageAccount.CreateCloudTableClient(
|
|
new TableConnectionPolicy() {
|
|
UseDirectMode = false
|
|
}); // new TableClientConfiguration());
|
|
|
|
var tableName = typeof(T).Name;
|
|
System.Attribute[] attrs = Attribute.GetCustomAttributes(typeof(T));
|
|
if (attrs != null && attrs.Any())
|
|
{
|
|
var tableAttr = (attrs.FirstOrDefault() as TableAttribute);
|
|
if (tableAttr != null)
|
|
tableName = tableAttr.Name;
|
|
}
|
|
// Create a table client for interacting with the table service
|
|
var table = tableClient.GetTableReference(tableName);
|
|
if (table.CreateIfNotExists())
|
|
{
|
|
//Console.WriteLine("Created Table named: {0}", tableName);
|
|
}
|
|
else
|
|
{
|
|
//Console.WriteLine("Table {0} already exists", tableName);
|
|
}
|
|
|
|
this._table = table;
|
|
}
|
|
|
|
public async Task DeleteEntityAsync(T deleteEntity)
|
|
{
|
|
try
|
|
{
|
|
if (deleteEntity == null)
|
|
{
|
|
throw new ArgumentNullException("deleteEntity");
|
|
}
|
|
|
|
if (deleteEntity.DomainEvents != null)
|
|
{
|
|
foreach (var evt in deleteEntity.DomainEvents)
|
|
{
|
|
//Process event
|
|
_eventsFactory.ResolveAndRun(evt);
|
|
}
|
|
}
|
|
|
|
TableOperation deleteOperation = TableOperation.Delete(deleteEntity);
|
|
TableResult result = await _table.ExecuteAsync(deleteOperation);
|
|
|
|
// Get the request units consumed by the current operation. RequestCharge of a TableResult is only applied to Azure CosmoS DB
|
|
//if (result.RequestCharge.HasValue)
|
|
//{
|
|
// Console.WriteLine("Request Charge of Delete Operation: " + result.RequestCharge);
|
|
//}
|
|
|
|
}
|
|
catch (StorageException e)
|
|
{
|
|
//Console.WriteLine(e.Message);
|
|
//Console.ReadLine();
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
public async Task<T> SaveChanges(T entity, bool skipEntitySaving = false)
|
|
{
|
|
try
|
|
{
|
|
if (entity.DomainEvents != null)
|
|
{
|
|
foreach (var evt in entity.DomainEvents)
|
|
{
|
|
//Process event
|
|
_eventsFactory.ResolveAndRun(evt);
|
|
}
|
|
}
|
|
|
|
if(skipEntitySaving)
|
|
{
|
|
return entity;
|
|
}
|
|
|
|
_log.Information($"Start SaveChanges");
|
|
TableOperation insertOrMergeOperation = TableOperation.InsertOrMerge(entity);
|
|
_log.Information($"Execute the operation.");
|
|
TableResult result = await _table.ExecuteAsync(insertOrMergeOperation);
|
|
_log.Information($"End SaveChanges");
|
|
// Get the request units consumed by the current operation. RequestCharge of a TableResult is only applied to Azure Cosmos DB
|
|
//if (result.RequestCharge.HasValue)
|
|
//{
|
|
// Console.WriteLine("Request Charge of InsertOrMerge Operation: " + result.RequestCharge);
|
|
//}
|
|
return result.Result as T;
|
|
}
|
|
catch (StorageException e)
|
|
{
|
|
_log.Error($"Error SaveChanges StorageException {e.Message} {e.StackTrace} {e.InnerException}", e.Message);
|
|
throw e;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
_log.Error($"Error SaveChanges Exception {e.Message} {e.StackTrace} {e.InnerException}", e.Message);
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
public void BatchOperation(T entity)
|
|
{
|
|
Console.WriteLine("******************** Start Batch Operation ********************");
|
|
var batchOperation = new TableBatchOperation();
|
|
for (var i = 2; i < 52; i++)
|
|
{
|
|
// I will create and add users and send them as one batch to the table.
|
|
//var batchUser = new User("Karlsbad", "Admin_" + i)
|
|
//{
|
|
// EMail = "alugili@gmail.com",
|
|
// LastLogin = DateTimeOffset.UtcNow
|
|
//};
|
|
|
|
batchOperation.Add(TableOperation.InsertOrMerge(entity)); // batchUser));
|
|
}
|
|
|
|
// Executing the operations or adding the users.
|
|
_table.ExecuteBatch(batchOperation);
|
|
Console.WriteLine("******************** End Batch Operation ********************");
|
|
|
|
}
|
|
|
|
public void ConcurrencyDemoDefaultPessimistic(CloudTable cloudTable)
|
|
{
|
|
Console.WriteLine("**************************** Start Demonstrate pessimistic concurrency ****************************");
|
|
|
|
//// Add new user to table.
|
|
//var firstUser = new User("Karlsruhe", "Operator")
|
|
//{
|
|
// EMail = "alugili@gmail.com",
|
|
// LastLogin = DateTimeOffset.UtcNow
|
|
//};
|
|
|
|
//var insertOrReplaceOperation = TableOperation.InsertOrReplace(firstUser);
|
|
//cloudTable.Execute(insertOrReplaceOperation);
|
|
//Console.WriteLine("Entity added. Original ETag = {0}", firstUser.ETag);
|
|
|
|
//// Someone else has changed the first user!
|
|
//var updatedFirstUser = new User("Karlsruhe", "Operator")
|
|
//{
|
|
// EMail = "bassam.alugili@hotmail.de",
|
|
// LastLogin = DateTimeOffset.UtcNow
|
|
//};
|
|
|
|
//insertOrReplaceOperation = TableOperation.InsertOrReplace(updatedFirstUser);
|
|
//cloudTable.Execute(insertOrReplaceOperation);
|
|
//Console.WriteLine("Entity updated. Updated ETag = {0}", updatedFirstUser.ETag);
|
|
|
|
//// Try updating first user. Etag is cached within firstUser and passed by default
|
|
//firstUser.LastLogin = DateTimeOffset.UtcNow;
|
|
|
|
//insertOrReplaceOperation = TableOperation.Merge(firstUser);
|
|
//try
|
|
//{
|
|
// Console.WriteLine("Trying to update Original entity");
|
|
// cloudTable.Execute(insertOrReplaceOperation);
|
|
//}
|
|
//catch (StorageException ex)
|
|
//{
|
|
// if (ex.RequestInformation.HttpStatusCode == (int)HttpStatusCode.PreconditionFailed)
|
|
// {
|
|
// Console.WriteLine("Error: Entity Tag is changed!");
|
|
// }
|
|
// else
|
|
// {
|
|
// throw;
|
|
// }
|
|
//}
|
|
Console.WriteLine("**************************** End Demonstrate pessimistic concurrency ****************************");
|
|
}
|
|
}
|
|
} |