Michael Wilson
Michael Wilson

Reputation: 1297

SerializationException when returning List of Azure TableEntity objects

When returning a List of TableEntities using WebAPI, I'm getting a 500 Internal Server Error. When I look at the output in Visual Studio, I see that i'm getting these 'System.Runtime.Serialization.SerializationException'.

Here's my code in the Controller

 public IEnumerable<ContactEntity> Get()
    {
        var creds = new StorageCredentials("MyAccountName", "MyKey");
        var storageAccount = new CloudStorageAccount(creds, false);

        // Create the table client.
        var tableClient = storageAccount.CreateCloudTableClient();

        // Retrieve a reference to the table.
        var contactsTable = tableClient.GetTableReference("contacts");

        // Create the table if it doesn't exist.
        contactsTable.CreateIfNotExists();

        // Construct the query operation for all contact entities
        var query = new TableQuery<ContactEntity>();

        var items = contactsTable.ExecuteQuery<ContactEntity>(query).ToList();

        return items;
    }

This is my code for the Custom TableEntity

    [Serializable]
public class ContactEntity: TableEntity
{

    public ContactEntity(string firstName, string lastName)
    {
        this.PartitionKey = lastName;
        this.RowKey = firstName;
    }

    public ContactEntity() { }

    public string Email { get; set; }

}

In the Controller, If I manually create the List of Entities, I get NO Serialization issues.

public IEnumerable<ContactEntity> Get()
    {
        var creds = new StorageCredentials("MyAccountName", "MyKey");
        var storageAccount = new CloudStorageAccount(creds, false);

        // Create the table client.
        var tableClient = storageAccount.CreateCloudTableClient();

        // Retrieve a reference to the table.
        var contactsTable = tableClient.GetTableReference("contacts");

        // Create the table if it doesn't exist.
        contactsTable.CreateIfNotExists();

        // Construct the query operation for all contact entities
        var query = new TableQuery<ContactEntity>();

        var items = contactsTable.ExecuteQuery<ContactEntity>(query).ToList();

        var manualList = new List<ContactEntity>() { };

        manualList.Add(new ContactEntity { PartitionKey = items[0].PartitionKey, RowKey = items[0].RowKey, ETag = items[0].ETag, Timestamp = items[0].Timestamp });
        manualList.Add(new ContactEntity { PartitionKey = items[1].PartitionKey, RowKey = items[1].RowKey, ETag = items[1].ETag, Timestamp = items[1].Timestamp });


        return manualList;
    }

Any ideas why?

Here are the exceptions listed in the output window.

Exception thrown: 'System.Runtime.Serialization.SerializationException' in mscorlib.dll Exception thrown: 'System.Runtime.Serialization.SerializationException' in mscorlib.dll Exception thrown: 'System.Runtime.Serialization.SerializationException' in Newtonsoft.Json.dll Exception thrown: 'System.Runtime.Serialization.SerializationException' in Newtonsoft.Json.dll Exception thrown: 'System.Runtime.Serialization.SerializationException' in Newtonsoft.Json.dll Exception thrown: 'System.Runtime.Serialization.SerializationException' in Newtonsoft.Json.dll Exception thrown: 'System.Runtime.Serialization.SerializationException' in Newtonsoft.Json.dll Exception thrown: 'System.Runtime.Serialization.SerializationException' in Newtonsoft.Json.dll Exception thrown: 'System.Runtime.Serialization.SerializationException' in mscorlib.dll Exception thrown: 'System.Runtime.Serialization.SerializationException' in mscorlib.dll

Upvotes: 0

Views: 450

Answers (2)

Dogu Arslan
Dogu Arslan

Reputation: 3384

An additional alternative to JshL's suggestions, you could change your web api to return IEnumerable of json serialized DynamicTableEntities. And on the client side deserialize the json back to DynamicTableEntity and handle transforming that into your concrete class if you wanted to or just consume it as DynamicTableEntity.

You can have a look at DynamicTableEntity json serializer I implemented here: https://www.nuget.org/packages/DynamicTableEntityJsonSerializer/

Upvotes: 1

JoshL
JoshL

Reputation: 1716

Types that inherit from TableEntity cannot be serialized, due to internal delegate members, etc. See below:

TableEntity serialization

You have a few options:

  1. inherit from ITableEntity interface instead of TableEntity... you'll have to implement the core TableEntity behavior yourself (not too bad, generally) and this gives you the opportunity to implement serialization if you want, too

  2. change your Web API to output a separate type that contains only the entity properties you want to return, and implement a transform function for your entity type, something like this:

In the ContactEntity class:

public JObject ToJson()
{
    var jo = new JObject();

    jo["PartitionKey"] = PartitionKey;
    jo["RowKey"] = RowKey;
    jo["Email"] = Email;

    // etc

    return jo;
}

In your controller:

public IEnumerable<JObject> Get()
{
    // Create the table client.
    var tableClient = _account.CreateCloudTableClient();

    // Retrieve a reference to the table.
    var contactsTable = tableClient.GetTableReference("contacts");

    // Create the table if it doesn't exist.
    contactsTable.CreateIfNotExists();

    // Construct the query operation for all contact entities
    var query = new TableQuery<ContactEntity>();

    var items = contactsTable.ExecuteQuery<ContactEntity>(query).ToList();

    return items.Select(i => i.ToJson());
}

Note the switch from returning IEnumerable of ContactEntity to IEnumerable of JObject. You could also define a separate class and use that instead of JObject, if you prefer.

Hope that helps... good luck!

Upvotes: 1

Related Questions