【问题标题】:How to query Azure Table storage by nullable fields?如何通过可空字段查询 Azure 表存储?
【发布时间】:2018-05-08 21:54:21
【问题描述】:

我有一个可以为空字段的表:

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

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

当我想查询记录时问题就开始了

  • Order、StartDate、EndDate 和 Text 不为 NULL 或

  • Order、StartDate 和 Text 不为 null 但 EndDate 为 null 或

  • Order 和 Text 不为空,但 StartDate 和 EndDate 为空

    Order.HasValue && StartDate.HasValue && EndDate.HasValue && !string.IsNullOrEmpty(Text) || Order.HasValue && StartDate.HasValue && !EndDate.HasValue && !string.IsNullOrEmpty(Text) || Order.HasValue && !StartDate.HasValue && !EndDate.HasValue && !string.IsNullOrEmpty(Text)

使用此类查询我得到错误或 400(错误请求)或不受支持的运算符(不支持 isnullorempty)

根据这个答案https://stackoverflow.com/a/4263091/3917754 不可能查询 NULL 值...

【问题讨论】:

  • 为什么要查询?只是显示它还是做任何其他操作?
  • @JoeyCai,我需要选择适用于我的 where 子句的记录。后来我只是将该记录映射到视图模型
  • 所以,我认为您可以将日期时间存储为字符串,您可以查询 null。

标签: c# .net azure azure-table-storage


【解决方案1】:

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

插入实体时,将 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)如果为null,则显示为null,如果有值,则将字符串转换为DateTime。

            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);
            }

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 可以存储在 Azure 表中的最小 .Net DateTime 值是 DateTime(1601, 1, 1)。但是 DateTime.MinValue 等于 new DateTime(0001, 01, 01)。不能这样存放。
    • :) 好吧,如果您不能使用 DateTime.Min,这确实很重要,主要的一点是您需要的只是一个默认值。所以使用 DateTime.Max 或 1601 .. 或你的生日作为你的默认值....
    【解决方案3】:

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

    OData 查询 = not (Id ne '')

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

    您可以在 azure table storage 文本编辑器中提供上述查询或使用以下是 C# 代码来获取数据

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

    List contentKeyEntities = new List(); TableQuerySegment partitionKeySegment = null; 而(partitionKeySegment == null || partitionKeySegment.ContinuationToken != null) { Task t1 = Task.Run(() => O365ReportingTable.ExecuteQuerySegmentedAsync(partitionKeysQuery, partitionKeySegment?.ContinuationToken)); t1.等待(); partitionKeySegment = t1.Result; contentKeyEntities.AddRange(partitionKeySegment.Results); Console.WriteLine(contentKeyEntities.Count); } 返回内容键实体;

    公共类 AuditLogEntity : TableEntity { #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
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-05-12
      • 2018-12-03
      • 1970-01-01
      • 2021-04-06
      • 1970-01-01
      • 2019-02-22
      • 2023-02-17
      相关资源
      最近更新 更多