Steph Locke
Steph Locke

Reputation: 6146

Batch processor for Azure Table Storage that handles classes inheriting TableEntity

I have a number of different Azure Tables I'd like to write to that have some core attributes and some varying ones. I'd like to be write in batches to minimise costs, but I'm struggling to get the types right in the processor function to facilitate this.

What types or additional code should be used to make this function handle the exemplar classes?

Batch process code and repro

using System;
using System.Collections.Generic;
using Microsoft.WindowsAzure.Storage.Table;

public class Program
{
    public static void Main()
    {
        Console.WriteLine("Hello World");
    }

    static void BatchInsertOrReplace<ITableEntity>(List<ITableEntity> items, CloudTable table)
    {
        // setup
        List<ITableEntity> batch = new List<T>();
        TableBatchOperation batchOperation = new TableBatchOperation();
        // n-1 batches
        foreach (var item in items)
        {
            batch.Add(item);
            batchOperation.InsertOrReplace(item);
            if (batch.Count == 100)
            {
                table.ExecuteBatch(batchOperation);
                batchOperation.Clear();
                batch.Clear();
            }
        }

        // nth batch
        if (batch.Any())
        {
            table.ExecuteBatch(batchOperation);
        }
    }

    // things I want to be able to push to table storage
    public class exampleClass : TableEntity
    {
        public DateTime rawDate { get; set; }
    }

    public class exampleClass_deriv1 : exampleClass
    {
        public string User { get; set; }
        public string Device { get; set; }
    }

    public class exampleClass_deriv2 : exampleClass
    {
        public string Person { get; set; }
        public string Machine { get; set; }
    }
}

Sample usage

The code where I'm retrieving entities from Azure Table storage and transforming them currently:

IEnumerable<exampleClass> recs = (from r in mytable
                                   where r.rawDate >= DateTime.Now
                                   select r);
var recsaccount = recs
    .Select(r => new exampleClass_deriv1
    {
        PartitionKey = r.PartitionKey,
        RowKey = r.RowKey,
        rawDate = r.rawDate,
        epochDate = i.epochDate,
        User = "Jim",
        Device = "Palmpilot"
    });
// Desired execution (which errors)
BatchInsertOrReplace(recsaccount.ToList(), mytable);

Simple coercions like (List<ITableEntity>)recsaccount.ToList() don't work

run.csx(63,30): error CS0030: Cannot convert type 'System.Collections.Generic.List<exampleClass_deriv2>' to 'System.Collections.Generic.List<ITableEntity>'

Update 1

Making a new List and putting items into it first:

List<ITableEntity> ins = new List<ITableEntity>();
foreach (var blah in recs.ToList())
{
   ITableEntity newOne = (ITableEntity)blah;
    ins.Add(newOne);
}
BatchInsertOrReplace(ins, mytable);

Results in errors like:

error CS0411: The type arguments for method 'BatchInsertOrReplace<T>(List<ITableEntity>, CloudTable)' cannot be inferred from the usage. Try specifying the type arguments explicitly.

Trying with type prefixes did not alter the situation.

Update 2 - using <T>

If I use <T> in the function definition like

static void BatchInsertOrReplace<T>(List<T> items, CloudTable table)
{
    List<T> batch = new List<T>(); // Alt of List<ITableEntity> batch = new List<ITableEntity>(); 
    TableBatchOperation batchOperation = new TableBatchOperation();
    foreach (var item in items)
    {
        batch.Add(item);
        batchOperation.InsertOrReplace(item);

        if (batch.Count == 100)
        {
            table.ExecuteBatch(batchOperation);
            batchOperation.Clear();
            batch.Clear();
        }
    }

    // Process last batch
    if (batch.Any())
    {
        table.ExecuteBatch(batchOperation);
    }
}

And then using the function like BatchInsertOrReplace<exampleClass_deriv2>( recsaccount.ToList(), webhitsAggAccount); result in messages about batch.Add and batchOperation.InsertOrReplace

run.csx(128,19): error CS1503: Argument 1: cannot convert from 'T' to 'Microsoft.WindowsAzure.Storage.Table.ITableEntity'
run.csx(129,40): error CS1503: Argument 1: cannot convert from 'T' to 'Microsoft.WindowsAzure.Storage.Table.ITableEntity'

This happens irrespective of whether batch is constructed as List<ITableEntity> or List<T>.

Upvotes: 0

Views: 232

Answers (1)

Dogu Arslan
Dogu Arslan

Reputation: 3384

You should make sure the function definition allows a variable type by making its signature use T but state that T inherits ITableEntity. This can be done with a where clause as part of the the first declaration of the function. Your batch function would become:

static void BatchInsertOrReplace<T>(List<T> items, CloudTable table) where T : ITableEntity
{
    List<ITableEntity> batch = new List<ITableEntity>();
    TableBatchOperation batchOperation = new TableBatchOperation();
    foreach (var item in items)
    {
        batch.Add(item);
        batchOperation.InsertOrReplace(item);

        if (batch.Count == 100)
        {
            table.ExecuteBatch(batchOperation);
            batchOperation.Clear();
            batch.Clear();
        }
    }

    // Process last batch
    if (batch.Any())
    {
        table.ExecuteBatch(batchOperation);
    }
}

Upvotes: 1

Related Questions