你能将azure表存储IQueryable(table.CreateQuery())公开为POCO吗?

时间:2015-10-16 04:06:15

标签: azure repository azure-storage azure-table-storage

我有一个包含30多个项目的大型应用程序,目前使用的是IRepository和POCO实体。我想知道是否有办法使用表存储而无需实现ITableEntity。我不想将azure存储块包导入到每个项目中,并将我的所有实体更改为使用ITableEntity。

实体适配器

我知道可以创建一个实体适配器(例如下面的那个),它在读取或编写单个实体时非常有效。但是,当尝试通过table.CreateQuery()公开IQueryable时,我无法使其工作。

public class AzureEntity
{
    public Guid Id { get; set; }
    public string PartitionKey { get; set; }
    public string RowKey { get; set; }
    public DateTimeOffset Timestamp { get; set; }
    public string ETag { get; set; }
}

internal class AzureStorageEntityAdapter<T> : ITableEntity where T : AzureEntity, new()
{
    #region Properties
    /// <summary>
    /// Gets or sets the entity's partition key
    /// </summary>
    public string PartitionKey
    {
        get { return InnerObject.PartitionKey; }
        set { InnerObject.PartitionKey = value; }
    }

    /// <summary>
    /// Gets or sets the entity's row key.
    /// </summary>
    public string RowKey
    {
        get { return InnerObject.RowKey; }
        set { InnerObject.RowKey = value; }
    }

    /// <summary>
    /// Gets or sets the entity's Timestamp.
    /// </summary>
    public DateTimeOffset Timestamp
    {
        get { return InnerObject.Timestamp; }
        set { InnerObject.Timestamp = value; }
    }

    /// <summary>
    /// Gets or sets the entity's current ETag.
    /// Set this value to '*' in order to blindly overwrite an entity as part of an update operation.
    /// </summary>
    public string ETag
    {
        get { return InnerObject.ETag; }
        set { InnerObject.ETag = value; }
    }

    /// <summary>
    /// Place holder for the original entity
    /// </summary>
    public T InnerObject { get; set; }
    #endregion

    #region Ctor
    public AzureStorageEntityAdapter()
    {
        // If you would like to work with objects that do not have a default Ctor you can use (T)Activator.CreateInstance(typeof(T));
        this.InnerObject = new T();
    }

    public AzureStorageEntityAdapter(T innerObject)
    {
        this.InnerObject = innerObject;
    }
    #endregion

    #region Methods

    public virtual void ReadEntity(IDictionary<string, EntityProperty> properties, OperationContext operationContext)
    {
        TableEntity.ReadUserObject(this.InnerObject, properties, operationContext);
    }

    public virtual IDictionary<string, EntityProperty> WriteEntity(OperationContext operationContext)
    {
        return TableEntity.WriteUserObject(this.InnerObject, operationContext);
    }

    #endregion
}

我希望能够做到这样......

public class TableStorageRepository : IRepository
{
    // snip...

    public IQueryable<T> FindAll<T>() where T : class, new()
    {
        CloudTable table = GetCloudTable<T>();
        return table.CreateQuery<AzureStorageEntityAdapter<T>>();
    }

    // snip...
}

这里的问题是CreateQuery创建了一个

IQueryable<AzureStorageEntityApater<T>>.

我无法看到如何获得所有'InnerObjects'的IQueryable。

是否有人知道是否可以通过某种方式公开IQueryable而不暴露ITableEntity?

1 个答案:

答案 0 :(得分:0)

这可能是您想要的,也可能不是,但可能会给您一些想法。

我创建了一个基础存储库,并为每个实体使用了一个单独的存储库,负责传递正确的CloudTable,分区/ rowkey /属性表达式和解析器。我基于DynamicTableEntity(它允许一些高级的东西,如动态属性)在我的实体(如小集合))。

public class BaseRepository
{
    protected async Task<DynamicTableEntity> GetAsync(CloudTable table, Expression<Func<DynamicTableEntity, bool>> filter)
    {
        var query = table.CreateQuery<DynamicTableEntity>().Where(filter).AsTableQuery();
        var segment = await query.ExecuteSegmentedAsync(null);
        return segment.Results.FirstOrDefault();
    }

    protected async Task<T> GetAsync<T>(CloudTable table, Expression<Func<DynamicTableEntity, bool>> filter, EntityResolver<T> resolver)
    {
        var query = table.CreateQuery<DynamicTableEntity>().Where(filter).Resolve(resolver);
        var segment = await query.ExecuteSegmentedAsync(null);
        return segment.Results.FirstOrDefault();
    }

    protected async Task<IEnumerable<DynamicTableEntity>> GetAllAsync(CloudTable table, Expression<Func<DynamicTableEntity, bool>> filter, int take = 1000)
    {
        if (take > 10000) take = 10000;
        if (take < 1) take = 1;

        var query = table.CreateQuery<DynamicTableEntity>().Where(filter).Take(take).AsTableQuery();
        var token = new TableContinuationToken();
        var results = new List<DynamicTableEntity>();
        while (token != null)
        {
            var segment = await query.ExecuteSegmentedAsync(token);
            results.AddRange(segment.Results);
            token = segment.ContinuationToken;
        }
        return results;
    }

    protected async Task<IEnumerable<T>> GetAllAsync<T>(CloudTable table, Expression<Func<DynamicTableEntity, bool>> filter, EntityResolver<T> resolver, int take = 1000)
    {
        if (take > 10000) take = 10000;
        if (take < 1) take = 1;

        var query = table.CreateQuery<DynamicTableEntity>().Where(filter).Take(take).Resolve(resolver);
        var token = new TableContinuationToken();
        var results = new List<T>();
        while (token != null)
        {
            var segment = await query.ExecuteSegmentedAsync(token);
            results.AddRange(segment.Results);
            token = segment.ContinuationToken;
        }
        return results;
    }

    protected async Task<int> InsertAsync(CloudTable table, DynamicTableEntity entity)
    {
        try
        {
            var result = await table.ExecuteAsync(TableOperation.Insert(entity));
            return result.HttpStatusCode;
        }
        catch (StorageException ex)
        {
            return ex.RequestInformation.HttpStatusCode;
        }
        catch (Exception ex)
        {
            return 500;
        }
    }

    protected async Task<int> ReplaceAsync(CloudTable table, DynamicTableEntity entity)
    {
        try
        {
            var result = await table.ExecuteAsync(TableOperation.Replace(entity));
            return result.HttpStatusCode;
        }
        catch (StorageException ex)
        {
            return ex.RequestInformation.HttpStatusCode;
        }
        catch (Exception ex)
        {
            return 500;
        }
    }

    protected async Task<int> DeleteAsync(CloudTable table, DynamicTableEntity entity)
    {
        try
        {
            var result = await table.ExecuteAsync(TableOperation.Delete(entity));
            return result.HttpStatusCode;
        }
        catch (StorageException ex)
        {
            return ex.RequestInformation.HttpStatusCode;
        }
        catch (Exception ex)
        {
            return 500;
        }
    }

    protected async Task<int> MergeAsync(CloudTable table, DynamicTableEntity entity)
    {
        try
        {
            var result = await table.ExecuteAsync(TableOperation.Merge(entity));
            return result.HttpStatusCode;
        }
        catch (StorageException ex)
        {
            return ex.RequestInformation.HttpStatusCode;
        }
        catch (Exception ex)
        {
            return 500;
        }
    }
}

从中继承的类的示例(你必须用你的想象力来填补空白 - 如果你想看到正确的实现,请告诉我)

// method
public Task<IEnumerable<T>> GetAllAsync<T>(string pk1, string pk2, EntityResolver<T> resolver, int take = 1000, Expression<Func<DynamicTableEntity, bool>> filterExpr = null)
    {
        var keysExpr = x => x.PartitionKey.Equals(string.Format("{0}_{1}", pk1, pk2);
        var queryExpr = filterExpr != null ? keysExpr.AndAlso(filterExpr) : keysExpr;
        return base.GetAllAsync<T>(CloudTableSelector.GetTable(), queryExpr, resolver, take);
    }

// call
var products = await ProductRepo.GetAllAsync<ProductOwnerViewDto>(orgType, orgId, ProductOwnerViewDto.GetResolver(), take, x => x.RowKey.CompareTo(fromId) > 0);

将所有实体包装在单独的回购中有点原始和痛苦,但我无法找到一种方法让我以这种方式查询表格,同时让我得到不同的投影(每个表有多个解析器。)

我找到了基于ITableEntity限制的解决方案(直到你需要动态属性然后你才搞砸了)。