如何通过可空字段查询 Azure Table 存储?

How to query Azure Table storage by nullable fields?

我有 table 个可以为 null 的字段:

public int? Order {get; set;}
public DateTime? StartDate {get; set;}
public DateTime? EndDate {get; set;}
public string Text {get; set;}

所有这些字段都可以是 NULL 值

查询记录时出现问题

使用此类查询时出现错误或 400(错误请求)或不支持的运算符(不支持 isnullorempty)

根据这个答案 不可能查询 NULL 值...

据我所知,您无法将 DateTime 设置为 null 或空。 所以,我建议您可以将日期时间存储为字符串,当您想要映射到视图模型时,您可以将字符串转换为 DateTime。

插入实体时,将 DateTime 转换为字符串:

    TableBatchOperation batchOperation = new TableBatchOperation();
    DateTime dts = DateTime.Now;
    DateTime dte = DateTime.UtcNow;

    // Create a customer entity and add it to the table.
    CustomerEntity customer1 = new CustomerEntity("Smith", "Jeff");
    customer1.Order = 1;
    customer1.StartDate = Convert.ToString(dts);
    customer1.EndDate = Convert.ToString(dte);
    customer1.Text = "text1";

    // Create another customer entity and add it to the table.
    CustomerEntity customer2 = new CustomerEntity("Smith", "Ben");
    customer2.Order = 2;
    customer2.StartDate = Convert.ToString(dts);
    customer2.EndDate = "";
    customer2.Text = "text2";

    CustomerEntity customer3 = new CustomerEntity("Smith", "Cai");
    customer3.Order = 3;
    customer3.StartDate = "";
    customer3.EndDate = "";
    customer3.Text = "text3";

    // Add both customer entities to the batch insert operation.
    batchOperation.Insert(customer1);
    batchOperation.Insert(customer2);
    batchOperation.Insert(customer3);

    // Execute the batch operation.
    table.ExecuteBatch(batchOperation);

实体如下:

public class CustomerEntity : TableEntity
        {
            public CustomerEntity(string lastName, string firstName)
            {
                this.PartitionKey = lastName;
                this.RowKey = firstName;
            }

            public CustomerEntity() { }

            public int? Order { get; set; }
            public string StartDate { get; set; }
            public string EndDate { get; set; }
            public string Text { get; set; }

            public DateTime? ConvertTime(string dateStr)
            {
                if (string.IsNullOrEmpty(dateStr))
                    return null;
                DateTime dt;
                var convert=DateTime.TryParse(dateStr, out dt);
                return dt;
            }
        }

显示或映射到模型时,可以使用ConvertTime方法判断列是否为空ConvertTime(entity.StartDate)如果为空,则显示为空,如果有值,则将字符串转换为日期时间。

            string orderhasvalue = TableQuery.GenerateFilterCondition("Order", QueryComparisons.NotEqual, null);
            string startdatehasvalue = TableQuery.GenerateFilterCondition("StartDate", QueryComparisons.NotEqual, null);
            string enddatehasvalue = TableQuery.GenerateFilterCondition("EndDate", QueryComparisons.NotEqual, null);
            string texthasvalue = TableQuery.GenerateFilterCondition("Text", QueryComparisons.NotEqual, null);
            string startdatenothasvalue = TableQuery.GenerateFilterCondition("StartDate", QueryComparisons.Equal, null);
            string enddatenothasvalue = TableQuery.GenerateFilterCondition("EndDate", QueryComparisons.Equal, null);

            TableQuery<CustomerEntity> query1 = new TableQuery<CustomerEntity>().Where(
                TableQuery.CombineFilters(
                    TableQuery.CombineFilters(
                        TableQuery.CombineFilters(
                            orderhasvalue, TableOperators.And, startdatehasvalue),
                    TableOperators.And, enddatehasvalue),
                TableOperators.And, texthasvalue)
            );
            TableQuery<CustomerEntity> query2 = new TableQuery<CustomerEntity>().Where(
                TableQuery.CombineFilters(
                    TableQuery.CombineFilters(
                        TableQuery.CombineFilters(
                            orderhasvalue, TableOperators.And, startdatehasvalue),
                    TableOperators.And, enddatenothasvalue),
                TableOperators.And, texthasvalue)
            );
            TableQuery<CustomerEntity> query3 = new TableQuery<CustomerEntity>().Where(
                TableQuery.CombineFilters(
                    TableQuery.CombineFilters(
                        TableQuery.CombineFilters(
                            orderhasvalue, TableOperators.And, startdatenothasvalue),
                    TableOperators.And, enddatenothasvalue),
                TableOperators.And, texthasvalue)
            );

            // Print the fields for each customer.
            foreach (CustomerEntity entity in table.ExecuteQuery(query2))
            {

                Console.WriteLine("{0}, {1}\t{2}\t{3}\t{4}\t{5}", entity.PartitionKey, entity.RowKey,
                    entity.Order, entity.ConvertTime(entity.StartDate), entity.ConvertTime(entity.EndDate), entity.Text);
            }

您不能在 Azure Table 存储中查询空值,string 也不例外。写入 Azure table 时,具有空值的 属性 不会以表格形式存在,因此引用该 属性 的查询将始终 return 出乎意料的结果。作为解决方法,您可以做的是提供默认的非空值并查询这些值。如果 string 将值 "" 分配给 string,则允许​​您查询 string.Empty(不是 null,因为 "" 不是 null).对于 DateTime? 类型,同样的解决方法但不是 "",您可以分配一个模糊的默认值,即。 DateTime.MinValue(或MaxValue)如果属性的实际值为null,否则您可以将其转换为string并将空字符串指定为默认值但您需要支付来回转换的价格,所以我个人更愿意尽可能避免这种情况。

您可以在 Azure table 存储上查询字符串数据类型字段的空过滤器。

OData 查询 = 不是(Id ne '')

使用上面的 Odata 查询,您可以仅在字符串数据类型字段上过滤空数据。

您可以在 azure table storage Text Editor 中提供以上查询或使用下面是 C# 代码来获取数据

TableQuery partitionKeysQuery = new TableQuery() .Where("不是 (Id ne '')");

List contentKeyEntities = new List(); TableQuerySegment partitionKeySegment = null; while (partitionKeySegment == null || partitionKeySegment.ContinuationToken != null) { Task t1 = Task.Run(() => O365ReportingTable.ExecuteQuerySegmentedAsync(partitionKeysQuery, partitionKeySegment?.ContinuationToken)); t1.Wait(); partitionKeySegment = t1.Result; contentKeyEntities.AddRange(partitionKeySegment.Results); Console.WriteLine(contentKeyEntities.Count); } return contentKeyEntities;

public class 审计日志实体:表实体 { #region构造函数代码

    /// <summary>
    /// default constructor
    /// </summary>
    public AuditLogEntity() { }

    /// <summary>
    /// constructor for creating an AuditLogEntity
    /// </summary>
    /// <param name="partitionKey">partition key of the data</param>
    /// <param name="rowKey">row key for the row</param>
    public AuditLogEntity(string partitionKey, string rowKey, JObject content)
    {
        PartitionKey = partitionKey;
        RowKey = rowKey;
        Properties = ConvertToEntityProperty(content);
    }

    #endregion Constructor Code

    #region Public Properties

    /// <summary>
    /// additional properties for the entity
    /// </summary>
    public IDictionary<string, EntityProperty> Properties { get; set; }

    #endregion Public Properties

    #region Private Methods

    /// <summary>
    /// converts JObjects keys into properties
    /// </summary>
    /// <param name="content">JObject to convert</param>
    /// <returns>Dictionary with key value pairs of JOjbect keys</returns>
    private IDictionary<string, EntityProperty> ConvertToEntityProperty(JObject content)
    {
        IDictionary<string, EntityProperty> properties = new Dictionary<string, EntityProperty>();

        if (content != null)
        {
            foreach (JProperty prop in content.Properties())
            {
                properties.Add(prop.Name, new EntityProperty(prop.Value.ToString()));
            }
        }

        return properties;
    }

    /// <summary>
    /// overrides the base WriteEntry to dynamically write properties to table storage
    /// </summary>
    /// <param name="operationContext">operation being performed</param>
    /// <returns>dictionary of set properties</returns>
    public override IDictionary<string, EntityProperty> WriteEntity(OperationContext operationContext)
    {
        IDictionary<string, EntityProperty> results = base.WriteEntity(operationContext);

        foreach (string key in Properties.Keys)
        {
            results.Add(key, Properties[key]);
        }

        return results;
    }

    /// <summary>
    /// overridden base ReadEntry method to convert propeties coming from table storage into the properties from this object
    /// </summary>
    /// <param name="properties">properties read in from table storage</param>
    /// <param name="operationContext">operation being performed</param>
    public override void ReadEntity(IDictionary<string, EntityProperty> properties, OperationContext operationContext)
    {
        base.ReadEntity(properties, operationContext);
        Properties = properties;
    }

    #endregion Private Methods
}