Reputation: 157
I have a small problem. I am trying to do a migration from Cosmos db to table storage in Azure. The problem is that I haven't dealt with table storage for a long time. It turned out that a large part of the packages that I used before were already out of date. And the offical documentation does not provide the needed information.
Currently I use this method to add item :
public static async Task AddDomain(CloudTable table, DomainEntity domain)
{
TableOperation insertOperation = TableOperation.Insert((Microsoft.WindowsAzure.Storage.Table.ITableEntity)domain);
await table.ExecuteAsync(insertOperation);
}
and this function App to add Domain:
[FunctionName("AddDomain")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function,"post", Route = null)] HttpRequest req,
[Table("Domains")] CloudTable domainsTable,
ILogger log)
{
DomainEntity domain = CreateDomain();
await AddDomain(domainsTable, domain);
return new OkResult();
}
But when i run the function its throw Exception :
Error indexing method 'AddDomain'
and
System.InvalidOperationException at Microsoft.Azure.WebJobs.Extensions.Tables.TableAttributeBindingProvider.TryCreate.
I have same problems with other CRUD operations too. Any idea what is going on ?
Upvotes: 0
Views: 512
Reputation: 602
Here is how i did it last month
My service class
using Azure;
using Azure.Data.Tables;
using VPMS.FuncApp.EFCore.AuditSetup;
namespace VPMS.Persistence.AuditSetup;
internal class TableStorageService : ITableStorageService
{
private readonly TableServiceClient _tableServiceClient;
public TableStorageService(TableServiceClient tableServiceClient)
{
_tableServiceClient = tableServiceClient;
}
public async Task BulkInsert(string tableName, string partionKey, List<AuditTableStorageTable> audit)
{
var client = _tableServiceClient.GetTableClient(tableName);
List<TableTransactionAction> addEntitiesBatch = new List<TableTransactionAction>();
addEntitiesBatch.AddRange(audit.Select(e => new TableTransactionAction(TableTransactionActionType.Add, e)));
await client.SubmitTransactionAsync(addEntitiesBatch).ConfigureAwait(false);
}
public async Task CreateTableIfNotExistsAsync(string tableName)
{
var client = _tableServiceClient.GetTableClient(tableName);
await client.CreateIfNotExistsAsync();
}
}
My Function App
using Microsoft.Azure.WebJobs;
using Microsoft.EntityFrameworkCore;
using VPMS.FuncApp.EFCore;
using VPMS.FuncApp.EFCore.AuditSetup;
using VPMS.Persistence.AuditSetup;
namespace VPMS.FuncApp.ArchiveAppAuditLogs;
public sealed class ArchiveAppAuditLogTimerTrigger
{
private readonly VPMSDbContext _dbContext;
private readonly ITableStorageService _tableStorageService;
public ArchiveAppAuditLogTimerTrigger(VPMSDbContext dbContext, ITableStorageService tableStorageService)
{
_dbContext = dbContext;
_tableStorageService = tableStorageService;
}
[FunctionName(nameof(ArchiveAppAuditLogTimerTrigger))]
public async Task ArchiveAppAuditLogTrigger([TimerTrigger("%ArchiveAppAuditLogCron%")] TimerInfo myTimer)
{
var currentDate = DateTimeOffset.UtcNow.Date;
var auditTable = _dbContext.Set<Audit>();
await _tableStorageService.CreateTableIfNotExistsAsync(FuncAppConstants.AuditTableName);
var query = auditTable.Where(w => w.CreatedOn < currentDate);
int pageSize = 100;
bool hasMoreRecords = true;
for (int skip = 1; hasMoreRecords is true; skip++)
{
var recordsToSync = await query.Skip(0)
.Take(pageSize)
.ToListAsync();
hasMoreRecords = recordsToSync.Any();
if (hasMoreRecords is false) break;
var groupedAuditData = recordsToSync.GroupBy(b => b.TableName).ToList();
foreach (var groupedAuditDatem in groupedAuditData)
{
List<AuditTableStorageTable> auditStorageTableRecords = new();
foreach (var auditEntry in groupedAuditDatem.ToList()) auditStorageTableRecords.Add(AuditTableStorageTable.BuilData(auditEntry));
await _tableStorageService.BulkInsert(FuncAppConstants.AuditTableName, groupedAuditDatem.Key, auditStorageTableRecords);
}
auditTable.RemoveRange(recordsToSync);
await _dbContext.SaveChangesAsync();
}
}
}
My Azure function app startup class
using Azure.Identity;
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Azure;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using VPMS.FuncApp.EFCore;
using VPMS.FuncApp.Interfaces;
using VPMS.FuncApp.Services;
using VPMS.Persistence.AuditSetup;
using VPMS.SharedKernel.Interfaces;
[assembly: FunctionsStartup(typeof(VPMS.FuncApp.Startup))]
namespace VPMS.FuncApp;
public sealed class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
var configurations = builder.GetContext().Configuration;
builder.Services.AddAzureClients(builder =>
{
builder.AddTableServiceClient(configurations.GetValue<string>("AzureWebJobsStorage"));
builder.UseCredential(new DefaultAzureCredential());
});
}
}
The class the TableStorage inherits from
using Azure;
using Azure.Data.Tables;
using VPMS.Persistence.AuditSetup;
namespace VPMS.FuncApp.EFCore.AuditSetup;
public sealed class AuditTableStorageTable : ITableEntity
{
public string? UserId { get; set; }
public AuditType AuditType { get; set; }
public string TableName { get; set; } = null!;
public DateTimeOffset CreatedOn { get; set; }
public string? OldValues { get; set; }
public string? NewValues { get; set; }
public string? AffectedColumns { get; set; }
public string PrimaryKey { get; set; } = null!;
public Guid BatchId { get; set; }
// these comes from the implemented interface
public string PartitionKey { get; set; }
public string RowKey { get; set; }
public DateTimeOffset? Timestamp { get; set; }
public ETag ETag { get; set; }
public static AuditTableStorageTable BuilData(Audit audit)
{
return new()
{
PartitionKey = audit.TableName,
TableName = audit.TableName,
AffectedColumns = audit.AffectedColumns,
AuditType = audit.AuditType,
BatchId = audit.BatchId,
CreatedOn = audit.CreatedOn,
OldValues = audit.OldValues,
NewValues = audit.NewValues,
PrimaryKey = audit.PrimaryKey,
RowKey = Guid.NewGuid().ToString(),
UserId = audit.UserId,
};
}
}
Upvotes: 1