locked
Retrieving Entities using TableClient.Execute RRS feed

  • Question

  • How do I convert the DynamicTableEntity to a T?

     
    public ICollection<T> Get<T>(string tableName, string partitionKey) where T : EntityBase
            {
                CloudTable table = _tableClient.GetTableReference(tableName);
    
                TableQuery query = new TableQuery().Where(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, partitionKey));
                
                var entities = new Collection<T>();
    
                foreach (var item in table.ExecuteQuery(query))
                {
                    entities.Add(item as T);
                }
                
                return entities;
            } 
    In the above code entities.Add(item as T) adds null items as they are not of the same type.

    When I try

    TableQuery<T> query = new TableQuery<T>().Where(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, partitionKey));

    table.ExecuteQuery(query) will complain that "'T' must be a non-abstract type with a public parameterless constructor in order to use it as parameter 'TElement' in the generic type or method 'Microsoft.WindowsAzure.Storage.Table.CloudTable.ExecuteQuery<TElement>(Microsoft.WindowsAzure.Storage.Table.TableQuery<TElement>, Microsoft.WindowsAzure.Storage.Table.TableRequestOptions, Microsoft.WindowsAzure.Storage.OperationContext)'"

    My EntityBase has a Constructor that requires partition and rowkeys.

    Can someone help me resolve the issue?

    Friday, October 25, 2013 3:18 PM

Answers

  • Your table query is returning DynamicTableEntity objects instead of your custom table entity objects. The clue to this is that you had to cast to type T (which, as you pointed out, didn't work). Instead of using TableQuery, use TableQuery<T>.

    As you said, one of the requirements of being a custom table entity type that can get populated automatically is it must have a public parameter-less constructor. You'll need to add this to your class. The partition key and row key will be set through the properties (which are taken care of for you if your inherit from the TableEntity class).

    So, your code would look like this:

    public ICollection<T> Get<T>(string tableName, string partitionKey) where T : EntityBase, new()
    {
      CloudTable table = _tableClient.GetTableReference(tableName);
    
      TableQuery<T> query = new TableQuery<T>().Where(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, partitionKey));
    
      var entities = new Collection<T>();
    
      foreach (var item in table.ExecuteQuery(query))
      {
        entities.Add(item);
      }
    
      return entities;
    } 

    If you are set against having a public parameter-less constructor on your custom entity classes, you can use an entity resolver delegate to create instances of these classes. However, if you want this to work with multiple types of custom entity classes, you will have to figure out a generic way to instantiate them (without a parameter-less constructor).

    In this case, your code would look something like this:

    public static ICollection<T> Get<T>(string tableName, string partitionKey) where T : EntityBase
    {
      CloudTable table = _tableClient.GetTableReference(tableName);
    
      TableQuery query = new TableQuery().Where(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, partitionKey));
    
      var entities = new Collection<T>();
          
      EntityResolver<T> entityResolver = new EntityResolver<T>((string pKey, string rKey, DateTimeOffset timestamp, IDictionary<string, EntityProperty> properties, string etag) =>
        {
          T item = ...
          return item;
        });
    
      foreach (var item in table.ExecuteQuery(query, entityResolver))
      {
        entities.Add(item);
      }
    
      return entities;
    } 



    • Edited by FishDawg Friday, October 25, 2013 11:46 PM
    • Marked as answer by Isakavis Monday, October 28, 2013 3:35 PM
    Friday, October 25, 2013 11:38 PM

All replies

  • I think I have the answer: From WindowsAzure website.

    http://www.windowsazure.com/en-us/develop/net/how-to-guides/table-services/

    Look for sentence "Also, your entity type must expose a parameter-less constructor."

    Friday, October 25, 2013 6:06 PM
  • Your table query is returning DynamicTableEntity objects instead of your custom table entity objects. The clue to this is that you had to cast to type T (which, as you pointed out, didn't work). Instead of using TableQuery, use TableQuery<T>.

    As you said, one of the requirements of being a custom table entity type that can get populated automatically is it must have a public parameter-less constructor. You'll need to add this to your class. The partition key and row key will be set through the properties (which are taken care of for you if your inherit from the TableEntity class).

    So, your code would look like this:

    public ICollection<T> Get<T>(string tableName, string partitionKey) where T : EntityBase, new()
    {
      CloudTable table = _tableClient.GetTableReference(tableName);
    
      TableQuery<T> query = new TableQuery<T>().Where(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, partitionKey));
    
      var entities = new Collection<T>();
    
      foreach (var item in table.ExecuteQuery(query))
      {
        entities.Add(item);
      }
    
      return entities;
    } 

    If you are set against having a public parameter-less constructor on your custom entity classes, you can use an entity resolver delegate to create instances of these classes. However, if you want this to work with multiple types of custom entity classes, you will have to figure out a generic way to instantiate them (without a parameter-less constructor).

    In this case, your code would look something like this:

    public static ICollection<T> Get<T>(string tableName, string partitionKey) where T : EntityBase
    {
      CloudTable table = _tableClient.GetTableReference(tableName);
    
      TableQuery query = new TableQuery().Where(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, partitionKey));
    
      var entities = new Collection<T>();
          
      EntityResolver<T> entityResolver = new EntityResolver<T>((string pKey, string rKey, DateTimeOffset timestamp, IDictionary<string, EntityProperty> properties, string etag) =>
        {
          T item = ...
          return item;
        });
    
      foreach (var item in table.ExecuteQuery(query, entityResolver))
      {
        entities.Add(item);
      }
    
      return entities;
    } 



    • Edited by FishDawg Friday, October 25, 2013 11:46 PM
    • Marked as answer by Isakavis Monday, October 28, 2013 3:35 PM
    Friday, October 25, 2013 11:38 PM
  • Thanks for the follow up on my question.

    Even using EntityResolver it requires me to have a parameter less constructor.

    EntityResolver<T> entityResolver = new EntityResolver<T>((string pk, string rk, DateTimeOffset ts, IDictionary<string, EntityProperty> properties, string etag) =>
    {
    	T resolvedEntity = new T();
                    
    	resolvedEntity.PartitionKey = pk;
            resolvedEntity.RowKey = rk;
            resolvedEntity.Timestamp = ts;
            resolvedEntity.ETag = etag;
            resolvedEntity.ReadEntity(properties, null);
    
            return resolvedEntity;
    });
    

    Having a parameter less constructor on the custom entities leads people not to specify a keys. That's what I don't like about parameter less constructor.

    I also saw the team blog on EntityAdapter. That is interesting as it eliminates the need for TableEntity knowledge by my POCO. But I could not get the source code of the sample they have done in one of the Azure Channel 9 Sessions by Joe. I posted a thread looking for the source code. Thanks for you help that you are providing here as much.

     
    Saturday, October 26, 2013 5:42 PM
  • There are ways to instantiate an object other than requiring a parameter-less constructor. You could create a TableEntityFactory<T> abstract class with a virtual method that takes the PK and RK. The caller to your method must derive from this class and pass in an instance of it. This class could instantiate a custom entity of type T by calling a constructor that takes parameters.

    Another option is to use reflection so you can use a different constructor to instantiate the class. This assumes every type T has a constructor that takes the PK and RK.

    Saturday, October 26, 2013 7:03 PM