简体   繁体   English

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

[英]How to query Azure Table storage by nullable fields?

I have table with nullable fields :我有可空字段的表:

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

All this fields can be with NULL value所有这些字段都可以为 NULL 值

Problem starts when I want to query records当我想查询记录时出现问题

  • where Order, StartDate, EndDate and Text aren't NULL or其中 Order、StartDate、EndDate 和 Text 不是 NULL 或

  • where Order, StartDate and Text aren't null but EndDate is null or其中 Order、StartDate 和 Text 不为 null 但 EndDate 为 null 或

  • where Order and Text aren't null but StartDate and EndDate are null其中 Order 和 Text 不为 null 但 StartDate 和 EndDate 为 null

    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)

Using such query I get error or 400 (bad request) or unsupported operator (isnullorempty isn't supported)使用这样的查询,我得到错误或 400(错误的请求)或不支持的运算符(不支持 isnullorempty)

According to this answer https://stackoverflow.com/a/4263091/3917754 it is impossible to query NULL values...根据这个答案https://stackoverflow.com/a/4263091/3917754不可能查询 NULL 值......

AFAIK, you could not set DateTime to null or empty. AFAIK,您不能将 DateTime 设置为 null 或为空。 So, I suggest that you could store your datetime as string and when you want to map to view model you could convert string to DateTime.因此,我建议您可以将日期时间存储为字符串,当您想映射到视图模型时,您可以将字符串转换为日期时间。

When you insert the entity, convert DateTime to string:插入实体时,将 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);

The Entity is as below:实体如下:

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

When you show it or map it to model, you could use ConvertTime method to judge if the column is null with ConvertTime(entity.StartDate) .当您显示它或将其映射到模型时,您可以使用 ConvertTime 方法来判断该列是否为空,并使用ConvertTime(entity.StartDate) If it is null, it will show null and if it has value, it will convert string to 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);
            }

You cannot query a null value in Azure Table Storage, string is no exception to that.您不能在 Azure 表存储中查询空值, string也不例外。 The property with null value does not exist in tabular form when written to azure table, so queries referring to that property will always return an unexpected result.带有空值的属性在写入 azure 表时不以表格形式存在,因此引用该属性的查询将始终返回意外结果。 What you can do as a workaround is to provide default non-null values and query for those instead.您可以做的解决方法是提供默认的非空值并查询这些值。 In case of string assigning the value "" to a string , allows you to query for string.Empty (not null because "" is not null ).如果string将值""分配给string ,则允许您查询 string.Empty (不是null因为""不是null )。 For DateTime?对于DateTime? type, again same workaround but instead of "" , you can assign an obscure default value ie.类型,同样的解决方法,但不是"" ,您可以分配一个模糊的默认值,即。 DateTime.MinValue (or MaxValue ) if actual value of the property is null, else you can convert it to string and assign empty string as a default value but you need to pay the price there for conversion back and forth so I personally prefer to avoid that if possible. DateTime.MinValue (或MaxValue )如果属性的实际值为空,否则您可以将其转换为string并将空字符串指定为默认值,但您需要在那里支付来回转换的价格,所以我个人更愿意避免如果可能的话。

You can query the null filter on azure table storage for string data type field.您可以在 azure 表存储上查询字符串数据类型字段的空过滤器。

OData Query = not (Id ne '') OData 查询 = not (Id ne '')

Using above Odata query you can able to filter the null data only on string data type field.使用上面的 Odata 查询,您可以仅在字符串数据类型字段上过滤空数据。

You can provide above query at azure table storage Text Editor or use Below is C# Code to fetch the data您可以在 azure 表存储文本编辑器中提供上述查询或使用下面的 C# 代码来获取数据

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

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

public class AuditLogEntity : TableEntity { #region Constructor Code公共类 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
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM