diff --git a/src/TableStorage/DocumentSerializer.cs b/src/TableStorage/DocumentSerializer.cs index 2084a01..5b32c72 100644 --- a/src/TableStorage/DocumentSerializer.cs +++ b/src/TableStorage/DocumentSerializer.cs @@ -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) + } }; /// diff --git a/src/TableStorage/TableRepositoryQuery.cs b/src/TableStorage/TableRepositoryQuery.cs index 337412f..7841fca 100644 --- a/src/TableStorage/TableRepositoryQuery.cs +++ b/src/TableStorage/TableRepositoryQuery.cs @@ -46,10 +46,15 @@ public async IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellati var query = (DataServiceQuery)new DataServiceContext(account.TableStorageUri.PrimaryUri).CreateQuery(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; diff --git a/src/Tests/QueryTests.cs b/src/Tests/QueryTests.cs index 93ea2d6..88112db 100644 --- a/src/Tests/QueryTests.cs +++ b/src/Tests/QueryTests.cs @@ -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 @@ -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(); @@ -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; @@ -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; @@ -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(() => + (from book in table.CreateQuery() + where book.Format == BookFormat.Hardback + select new { book.Title }) + .ToList()); + } + + async Task LoadBooksAsync(ITableRepository 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(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; } } } }