Skip to content

Commit

Permalink
Add support for filtering by enum property types
Browse files Browse the repository at this point in the history
"Fix" this support in CosmosDB table client, which does not support this type of query :).

Fixes #35
  • Loading branch information
kzu committed Jun 16, 2021
1 parent 24a669e commit 37e8442
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 36 deletions.
5 changes: 5 additions & 0 deletions src/TableStorage/DocumentSerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ partial class DocumentSerializer : IStringDocumentSerializer
AllowTrailingCommas = true,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault,
WriteIndented = true,
Converters =
{
// Enums should persist/parse with their string values instead
new JsonStringEnumConverter(allowIntegerValues: false)
}
};

/// <summary>
Expand Down
7 changes: 6 additions & 1 deletion src/TableStorage/TableRepositoryQuery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,15 @@ public async IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellati
var query = (DataServiceQuery)new DataServiceContext(account.TableStorageUri.PrimaryUri).CreateQuery<T>(tableName)
.Provider.CreateQuery(expression);

// Remove enum type names and just keep the string value
var rawqs = Regex.Replace(
query.RequestUri.GetComponents(UriComponents.Query, UriFormat.Unescaped),
"(\\W)[\\w\\+\\.]+('\\w+')", "$1$2");

// We need to count & break manually because $top is interpreted as the max records per page
// if the set matches more items. This is clearly unintuitive and *not* what one typically
// wants when using LINQ queries! See Note on https://docs.microsoft.com/en-us/rest/api/storageservices/querying-tables-and-entities#supported-query-options
var qs = HttpUtility.ParseQueryString(query.RequestUri.GetComponents(UriComponents.Query, UriFormat.Unescaped));
var qs = HttpUtility.ParseQueryString(rawqs);
if (!long.TryParse(qs["$top"], out var top))
top = -1;

Expand Down
60 changes: 25 additions & 35 deletions src/Tests/QueryTests.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,10 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Net;
using System.Net.Http;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.Azure.Cosmos.Table;
using Microsoft.OData.Client;
using Microsoft.OData.UriParser;
using Xunit;

namespace Devlooped
Expand All @@ -27,7 +19,7 @@ public async Task CanTake()
await LoadBooksAsync(repo);

var query = from book in repo.CreateQuery()
where book.Format == "Hardback" && book.IsPublished
where book.Format == BookFormat.Hardback && book.IsPublished
select new { book.ISBN, book.Title };

var result = await query.Take(2).AsAsyncEnumerable().ToListAsync();
Expand Down Expand Up @@ -58,7 +50,7 @@ public async Task CanProject()
var hasResults = false;

await foreach (var info in from book in repo.CreateQuery()
where book.Author == "Rick Riordan" && book.Format == "Hardback" && book.IsPublished
where book.Author == "Rick Riordan" && book.Format == BookFormat.Hardback && book.IsPublished
select new { book.ISBN, book.Title })
{
hasResults = true;
Expand All @@ -83,7 +75,7 @@ public async Task CanProjectPartition()
var hasResults = false;

await foreach (var info in from book in repo.CreateQuery()
where book.Format == "Hardback" && book.IsPublished
where book.Format == BookFormat.Hardback && book.IsPublished
select new { book.ISBN, book.Title })
{
hasResults = true;
Expand All @@ -94,42 +86,40 @@ public async Task CanProjectPartition()
Assert.True(hasResults);
}

[Fact]
public async Task EnumFailsInTableClient()
{
var account = CloudStorageAccount.DevelopmentStorageAccount;
var table = account.CreateCloudTableClient().GetTableReference(nameof(EnumFailsInTableClient));

await table.CreateIfNotExistsAsync();

Assert.Throws<StorageException>(() =>
(from book in table.CreateQuery<BookEntity>()
where book.Format == BookFormat.Hardback
select new { book.Title })
.ToList());
}


async Task LoadBooksAsync(ITableRepository<Book> books)
{
foreach (var book in File.ReadAllLines("Books.csv").Skip(1)
.Select(line => line.Split(','))
.Select(values => new Book(values[1], values[2], values[3], values[4], int.Parse(values[5]), bool.Parse(values[6]))))
.Select(values => new Book(values[1], values[2], values[3], Enum.Parse<BookFormat>(values[4]), int.Parse(values[5]), bool.Parse(values[6]))))
{
await books.PutAsync(book);
}
}

public record Book(string ISBN, string Title, string Author, string Format, int? Pages = null, bool IsPublished = true);
public enum BookFormat { Paperback, Hardback }

public class RequestStatusEntity : TableEntity
{
public RequestStatusEntity()
{
}

public RequestStatusEntity(string ID) : base(nameof(RequestStatus), ID)
{
}

public string? ID
{
get => RowKey;
set => RowKey = value;
}

public int Status { get; set; }
public string? Reason { get; set; }
}
public record Book(string ISBN, string Title, string Author, BookFormat Format, int? Pages = null, bool IsPublished = true);

public record RequestStatus(string ID)
public class BookEntity : TableEntity
{
public int Status { get; set; }
public string? Reason { get; set; }
public string? Title { get; set; }
public BookFormat Format { get; set; }
}
}
}

0 comments on commit 37e8442

Please sign in to comment.