Sandeep Phadke
Sandeep Phadke

Reputation: 882

Compile error using Azure Storage Client library 3.0 method executequery<T> where T is tableentity

I must be missing something obvious but the following fails with a compile error:

internal static IEnumerable<T> GetEntitiesWithCommaSeparatedRowKeys<T>(
string tableName, string partitionKey, 
string commaDelimitedStringOfRowKeys) where T: TableEntity
{
 ....
    TableQuery<T> entitiesQuery = new TableQuery<T>().Where(
                 TableQuery.CombineFilters(
                           TableQuery.GenerateFilterCondition("PartitionKey",
                           QueryComparisons.Equal, partitionKey),
                           TableOperators.And,
                           AzureHelper.GetFilterConditionForCommaDelimitedStringOfRowKeys(commaDelimitedStringOfRowKeys)
                           ));
    // compile error on this line
    IEnumerable<T> entities = table.ExecuteQuery<T>(entitiesQuery);
 ...
 }

The error I get is:

'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)'

TableEntity clearly has a public parameterless constructor and is non-abstract. The following is the object from metadata info when I hit F12 on TableEntity (just to ensure its resolving the TableEntity type correctly).

namespace Microsoft.WindowsAzure.Storage.Table
{

    public class TableEntity : ITableEntity
    {
        // Summary:
        //     Initializes a new instance of the Microsoft.WindowsAzure.Storage.Table.TableEntity
        //     class.
        public TableEntity();
        ...
    }
}

Any ideas anyone? FYI I'm using Azure Client library 3.0.1.0.

UPDATE: Added linked issue which solved a similar problem

Upvotes: 3

Views: 1452

Answers (1)

Sandeep Phadke
Sandeep Phadke

Reputation: 882

So turns out that if a method provides a constraint on type, that constraint must be also forwarded by any callers of that type.

I didn't know that.

In this case the definition for Table.ExecuteQuery looks like the following

 public IEnumerable<TElement> ExecuteQuery<TElement>(TableQuery<TElement> query,
         TableRequestOptions requestOptions = null,
         OperationContext operationContext = null) 
                where TElement : ITableEntity, new();

Therefore adding new() to my constraints for T fixes the issue.

So the final method declaration looks like

internal static IEnumerable<T> GetEntitiesWithCommaSeparatedRowKeys<T>(string tableName,
       string partitionKey,
       string commaDelimitedStringOfRowKeys) 
              where T : TableEntity , new() //This is new (pun intended :))

Found a related issue in one of the related links that showed up for this question.

I'm guessing the compiler could always look up the type constraints from when I actually make the call, but since TableEntity is public (and not sealed) I guess it could end up being a runtime issue.

Also interesting to note that my method is marked internal which should really enable the compiler to check against callers within the library.

Anyways, learnt something new :).

Upvotes: 10

Related Questions