From 0bcdf9e9abe85c2cd7668aeb5b0a6d0328757a3f Mon Sep 17 00:00:00 2001 From: Adrian Hall Date: Fri, 15 Nov 2024 08:30:17 -0800 Subject: [PATCH 01/15] (#149) Update AutoMapper --- Directory.Packages.props | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 0f2295f1..195a4899 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -3,9 +3,8 @@ true true - - + From a9bdb13215a551fa01ca9b6ee64a1e0f526b8916 Mon Sep 17 00:00:00 2001 From: Adrian Hall Date: Fri, 15 Nov 2024 08:31:59 -0800 Subject: [PATCH 02/15] (#149) Update FluentAssertions --- Directory.Packages.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 195a4899..a953cf7f 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -5,8 +5,8 @@ - - + + From 947d318da60db6a642ff0f465826b0bc72937986 Mon Sep 17 00:00:00 2001 From: Adrian Hall Date: Fri, 15 Nov 2024 08:32:44 -0800 Subject: [PATCH 03/15] (#149) Update LiteDb --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index a953cf7f..ad0e81e9 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -7,7 +7,7 @@ - + From 250462068b9eb70fcd93ea0128db3e606a792eef Mon Sep 17 00:00:00 2001 From: Adrian Hall Date: Fri, 15 Nov 2024 08:43:43 -0800 Subject: [PATCH 04/15] (#149) Updated all transitive packages. --- Datasync.Toolkit.sln | 2 +- Directory.Packages.props | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Datasync.Toolkit.sln b/Datasync.Toolkit.sln index 1dc0da02..f943bc3a 100644 --- a/Datasync.Toolkit.sln +++ b/Datasync.Toolkit.sln @@ -11,7 +11,6 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{D59F1489-5D74-4F52-B78B-88037EAB2838}" ProjectSection(SolutionItems) = preProject tests\Directory.Build.props = tests\Directory.Build.props - tests\EFCore.Packages.props = tests\EFCore.Packages.props EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkit.Datasync.Server.Abstractions", "src\CommunityToolkit.Datasync.Server.Abstractions\CommunityToolkit.Datasync.Server.Abstractions.csproj", "{852F8266-603E-4FC6-A5CB-E492E747924F}" @@ -44,6 +43,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkit.Datasync.T EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{2D2A6EFC-015D-4258-96D4-24C78F8C59F9}" ProjectSection(SolutionItems) = preProject + Directory.Packages.props = Directory.Packages.props .github\workflows\SignedPackageFileList.txt = .github\workflows\SignedPackageFileList.txt .github\workflows\SignedTemplateFileList.txt = .github\workflows\SignedTemplateFileList.txt EndProjectSection diff --git a/Directory.Packages.props b/Directory.Packages.props index ad0e81e9..8500ff3e 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -5,6 +5,7 @@ + @@ -16,8 +17,9 @@ - - + + + @@ -25,9 +27,12 @@ - + + + + From 35fdac6fee2dc256060b970d71b180bd91558763 Mon Sep 17 00:00:00 2001 From: Adrian Hall Date: Fri, 15 Nov 2024 08:46:54 -0800 Subject: [PATCH 05/15] (#149) Updated MVC Testing --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 8500ff3e..01657c7c 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -9,7 +9,7 @@ - + From 2cfcaa5e25186dab30ed7b40001dddbc95ee3aee Mon Sep 17 00:00:00 2001 From: Adrian Hall Date: Fri, 15 Nov 2024 08:49:06 -0800 Subject: [PATCH 06/15] (#149) Updated Entity Framework Core. --- Directory.Packages.props | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 01657c7c..a41c3d68 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -3,6 +3,7 @@ true true + @@ -12,11 +13,11 @@ - - - - - + + + + + From b70745b036a04e51425e0bd57c976e329365a701 Mon Sep 17 00:00:00 2001 From: Adrian Hall Date: Fri, 15 Nov 2024 09:16:35 -0800 Subject: [PATCH 07/15] (#149) Upgrade Microsoft.NET.Test.Sdk --- Directory.Packages.props | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index a41c3d68..f200e971 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -3,7 +3,6 @@ true true - @@ -21,7 +20,7 @@ - + From f0131d37baf3a6f2c513491c0cf21a6478c87325 Mon Sep 17 00:00:00 2001 From: Adrian Hall Date: Fri, 15 Nov 2024 09:18:47 -0800 Subject: [PATCH 08/15] (#149) Upgrade NSubstitute --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index f200e971..e392e58c 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -23,7 +23,7 @@ - + From b1a829c9441920a4bb1ce6e1a6cdd69f688ece6c Mon Sep 17 00:00:00 2001 From: Adrian Hall Date: Fri, 15 Nov 2024 09:20:39 -0800 Subject: [PATCH 09/15] (#149) Upgrade PostgreSQL Driver --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index e392e58c..fcf780e5 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -25,7 +25,7 @@ - + From 8a4ccc2705071f64c203445e1b6e09660d48236f Mon Sep 17 00:00:00 2001 From: Adrian Hall Date: Fri, 15 Nov 2024 09:26:59 -0800 Subject: [PATCH 10/15] (#149) Upgrade Swashbuckle. --- Directory.Packages.props | 2 +- .../CommunityToolkit.Datasync.Server.Swashbuckle.Test.csproj | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index fcf780e5..6a916648 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -26,7 +26,7 @@ - + diff --git a/tests/CommunityToolkit.Datasync.Server.Swashbuckle.Test/CommunityToolkit.Datasync.Server.Swashbuckle.Test.csproj b/tests/CommunityToolkit.Datasync.Server.Swashbuckle.Test/CommunityToolkit.Datasync.Server.Swashbuckle.Test.csproj index c0a472bd..9639ae7e 100644 --- a/tests/CommunityToolkit.Datasync.Server.Swashbuckle.Test/CommunityToolkit.Datasync.Server.Swashbuckle.Test.csproj +++ b/tests/CommunityToolkit.Datasync.Server.Swashbuckle.Test/CommunityToolkit.Datasync.Server.Swashbuckle.Test.csproj @@ -14,4 +14,8 @@ + + + + From 8463df953ca49d0250480c294de423e81afd2b5e Mon Sep 17 00:00:00 2001 From: Adrian Hall Date: Fri, 15 Nov 2024 09:48:26 -0800 Subject: [PATCH 11/15] (#149) Updated xUnit, fixed .NET 9 synchronization context bug from .NET 9 performance improvements. --- Directory.Packages.props | 2 +- .../Paging/ConcurrentObservableCollection.cs | 29 ++++++++------- .../Paging/SynchronizationContextAdapter.cs | 31 ---------------- .../ConcurrentObservableCollection_Tests.cs | 36 ------------------- 4 files changed, 15 insertions(+), 83 deletions(-) delete mode 100644 src/CommunityToolkit.Datasync.Client/Paging/SynchronizationContextAdapter.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index 6a916648..02c5594b 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -33,7 +33,7 @@ - + diff --git a/src/CommunityToolkit.Datasync.Client/Paging/ConcurrentObservableCollection.cs b/src/CommunityToolkit.Datasync.Client/Paging/ConcurrentObservableCollection.cs index 371fdc3e..05f53379 100644 --- a/src/CommunityToolkit.Datasync.Client/Paging/ConcurrentObservableCollection.cs +++ b/src/CommunityToolkit.Datasync.Client/Paging/ConcurrentObservableCollection.cs @@ -16,7 +16,7 @@ namespace CommunityToolkit.Datasync.Client; /// public class ConcurrentObservableCollection : ObservableCollection { - private readonly SynchronizationContext context = SynchronizationContext.Current!; + private readonly SynchronizationContext? currentContext = SynchronizationContext.Current; private bool suppressNotification = false; /// @@ -151,31 +151,30 @@ public bool ReplaceIf(Func match, T replacement) /// /// The event arguments protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e) - => DispatchCallback(new SynchronizationContextAdapter(this.context), RaiseCollectionChanged, e); + { + if (this.currentContext is null || SynchronizationContext.Current == this.currentContext) + { + RaiseCollectionChanged(e); + } + else + { + this.currentContext.Send(RaiseCollectionChanged, e); + } + } /// /// Event trigger to indicate that a property has changed in a thread-safe way. /// /// The event arguments protected override void OnPropertyChanged(PropertyChangedEventArgs e) - => DispatchCallback(new SynchronizationContextAdapter(this.context), RaisePropertyChanged, e); - - /// - /// Dispatches the callback to the synchronization context. If the synchronization context is - /// the current one, we can avoid a dispatch and just call the callback directly. - /// - /// The context to send the request to. - /// The callback method. - /// The parameter for the callback method. - internal static void DispatchCallback(ISynchronizationContext context, SendOrPostCallback callback, object? param) { - if (context.IsCurrentContext()) + if (this.currentContext is null || SynchronizationContext.Current == this.currentContext) { - callback(param); + RaisePropertyChanged(e); } else { - context.Send(callback, param); + this.currentContext.Send(RaisePropertyChanged, e); } } diff --git a/src/CommunityToolkit.Datasync.Client/Paging/SynchronizationContextAdapter.cs b/src/CommunityToolkit.Datasync.Client/Paging/SynchronizationContextAdapter.cs deleted file mode 100644 index 7e855179..00000000 --- a/src/CommunityToolkit.Datasync.Client/Paging/SynchronizationContextAdapter.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics.CodeAnalysis; - -namespace CommunityToolkit.Datasync.Client.Paging; - -/// -/// An abstraction layer for the that we use -/// for mocking out context calls. -/// -internal interface ISynchronizationContext -{ - bool IsCurrentContext(); - void Send(SendOrPostCallback callback, object? state); -} - -/// -/// A concrete implementation of the that handles -/// a real synchronization context. -/// -[ExcludeFromCodeCoverage] -internal class SynchronizationContextAdapter(SynchronizationContext context) : ISynchronizationContext -{ - public bool IsCurrentContext() - => SynchronizationContext.Current == context; - - public void Send(SendOrPostCallback callback, object? state) - => context.Send(callback, state); -} diff --git a/tests/CommunityToolkit.Datasync.Client.Test/Paging/ConcurrentObservableCollection_Tests.cs b/tests/CommunityToolkit.Datasync.Client.Test/Paging/ConcurrentObservableCollection_Tests.cs index d074bb20..4f71bd6f 100644 --- a/tests/CommunityToolkit.Datasync.Client.Test/Paging/ConcurrentObservableCollection_Tests.cs +++ b/tests/CommunityToolkit.Datasync.Client.Test/Paging/ConcurrentObservableCollection_Tests.cs @@ -180,40 +180,4 @@ public void ReplaceIf_False_IfNoMatch() actual.Should().BeFalse(); this.movies.Should().HaveCount(count).And.NotContain(item); } - - [Fact] - public void DispatchCallback_DispatchesToSynchronizationContext() - { - int contextCaller = 0; - int functionCaller = 0; - - void dispatcher(object p) { functionCaller++; } - - ISynchronizationContext context = Substitute.For(); - context.IsCurrentContext().Returns(false); - context.When(x => x.Send(Arg.Any(), Arg.Any())).Do(x => contextCaller++); - - ConcurrentObservableCollection.DispatchCallback(context, dispatcher, new object()); - - contextCaller.Should().Be(1); - functionCaller.Should().Be(0); - } - - [Fact] - public void DispatchCallback_DispatchesLocally() - { - int contextCaller = 0; - int functionCaller = 0; - - void dispatcher(object p) { functionCaller++; } - - ISynchronizationContext context = Substitute.For(); - context.IsCurrentContext().Returns(true); - context.When(x => x.Send(Arg.Any(), Arg.Any())).Do(x => contextCaller++); - - ConcurrentObservableCollection.DispatchCallback(context, dispatcher, new object()); - - contextCaller.Should().Be(0); - functionCaller.Should().Be(1); - } } \ No newline at end of file From 77f61e64bf54069020b48461c9c8da66eea60011 Mon Sep 17 00:00:00 2001 From: Adrian Hall Date: Fri, 15 Nov 2024 09:53:12 -0800 Subject: [PATCH 12/15] (#149) Updated xUnit Test Runner --- Directory.Packages.props | 2 +- .../CommunityToolkit.Datasync.Client.Test.csproj | 14 ++++++++++++++ ...olkit.Datasync.Server.Abstractions.Test.csproj | 14 ++++++++++++++ ...Toolkit.Datasync.Server.Automapper.Test.csproj | 14 ++++++++++++++ ...atasync.Server.EntityFrameworkCore.Test.csproj | 14 ++++++++++++++ ...tyToolkit.Datasync.Server.InMemory.Test.csproj | 14 ++++++++++++++ ...nityToolkit.Datasync.Server.LiteDb.Test.csproj | 14 ++++++++++++++ ...unityToolkit.Datasync.Server.NSwag.Test.csproj | 15 +++++++++++++++ ...oolkit.Datasync.Server.Swashbuckle.Test.csproj | 15 +++++++++++++++ .../CommunityToolkit.Datasync.Server.Test.csproj | 14 ++++++++++++++ tests/Directory.Build.props | 12 ------------ 11 files changed, 129 insertions(+), 13 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 02c5594b..73b0d24d 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -34,7 +34,7 @@ - + diff --git a/tests/CommunityToolkit.Datasync.Client.Test/CommunityToolkit.Datasync.Client.Test.csproj b/tests/CommunityToolkit.Datasync.Client.Test/CommunityToolkit.Datasync.Client.Test.csproj index 9cba0af1..51d02dba 100644 --- a/tests/CommunityToolkit.Datasync.Client.Test/CommunityToolkit.Datasync.Client.Test.csproj +++ b/tests/CommunityToolkit.Datasync.Client.Test/CommunityToolkit.Datasync.Client.Test.csproj @@ -8,4 +8,18 @@ + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + diff --git a/tests/CommunityToolkit.Datasync.Server.Abstractions.Test/CommunityToolkit.Datasync.Server.Abstractions.Test.csproj b/tests/CommunityToolkit.Datasync.Server.Abstractions.Test/CommunityToolkit.Datasync.Server.Abstractions.Test.csproj index 3f75ef06..97980f0f 100644 --- a/tests/CommunityToolkit.Datasync.Server.Abstractions.Test/CommunityToolkit.Datasync.Server.Abstractions.Test.csproj +++ b/tests/CommunityToolkit.Datasync.Server.Abstractions.Test/CommunityToolkit.Datasync.Server.Abstractions.Test.csproj @@ -2,4 +2,18 @@ + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + diff --git a/tests/CommunityToolkit.Datasync.Server.Automapper.Test/CommunityToolkit.Datasync.Server.Automapper.Test.csproj b/tests/CommunityToolkit.Datasync.Server.Automapper.Test/CommunityToolkit.Datasync.Server.Automapper.Test.csproj index 23ad8252..f9d81cd2 100644 --- a/tests/CommunityToolkit.Datasync.Server.Automapper.Test/CommunityToolkit.Datasync.Server.Automapper.Test.csproj +++ b/tests/CommunityToolkit.Datasync.Server.Automapper.Test/CommunityToolkit.Datasync.Server.Automapper.Test.csproj @@ -4,4 +4,18 @@ + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + diff --git a/tests/CommunityToolkit.Datasync.Server.EntityFrameworkCore.Test/CommunityToolkit.Datasync.Server.EntityFrameworkCore.Test.csproj b/tests/CommunityToolkit.Datasync.Server.EntityFrameworkCore.Test/CommunityToolkit.Datasync.Server.EntityFrameworkCore.Test.csproj index a965d498..965b7729 100644 --- a/tests/CommunityToolkit.Datasync.Server.EntityFrameworkCore.Test/CommunityToolkit.Datasync.Server.EntityFrameworkCore.Test.csproj +++ b/tests/CommunityToolkit.Datasync.Server.EntityFrameworkCore.Test/CommunityToolkit.Datasync.Server.EntityFrameworkCore.Test.csproj @@ -6,4 +6,18 @@ + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + diff --git a/tests/CommunityToolkit.Datasync.Server.InMemory.Test/CommunityToolkit.Datasync.Server.InMemory.Test.csproj b/tests/CommunityToolkit.Datasync.Server.InMemory.Test/CommunityToolkit.Datasync.Server.InMemory.Test.csproj index 565dff36..8a370645 100644 --- a/tests/CommunityToolkit.Datasync.Server.InMemory.Test/CommunityToolkit.Datasync.Server.InMemory.Test.csproj +++ b/tests/CommunityToolkit.Datasync.Server.InMemory.Test/CommunityToolkit.Datasync.Server.InMemory.Test.csproj @@ -6,4 +6,18 @@ + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + diff --git a/tests/CommunityToolkit.Datasync.Server.LiteDb.Test/CommunityToolkit.Datasync.Server.LiteDb.Test.csproj b/tests/CommunityToolkit.Datasync.Server.LiteDb.Test/CommunityToolkit.Datasync.Server.LiteDb.Test.csproj index 1744fd72..39561d45 100644 --- a/tests/CommunityToolkit.Datasync.Server.LiteDb.Test/CommunityToolkit.Datasync.Server.LiteDb.Test.csproj +++ b/tests/CommunityToolkit.Datasync.Server.LiteDb.Test/CommunityToolkit.Datasync.Server.LiteDb.Test.csproj @@ -6,4 +6,18 @@ + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + diff --git a/tests/CommunityToolkit.Datasync.Server.NSwag.Test/CommunityToolkit.Datasync.Server.NSwag.Test.csproj b/tests/CommunityToolkit.Datasync.Server.NSwag.Test/CommunityToolkit.Datasync.Server.NSwag.Test.csproj index 5b6fcbe9..16246a53 100644 --- a/tests/CommunityToolkit.Datasync.Server.NSwag.Test/CommunityToolkit.Datasync.Server.NSwag.Test.csproj +++ b/tests/CommunityToolkit.Datasync.Server.NSwag.Test/CommunityToolkit.Datasync.Server.NSwag.Test.csproj @@ -13,4 +13,19 @@ + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + diff --git a/tests/CommunityToolkit.Datasync.Server.Swashbuckle.Test/CommunityToolkit.Datasync.Server.Swashbuckle.Test.csproj b/tests/CommunityToolkit.Datasync.Server.Swashbuckle.Test/CommunityToolkit.Datasync.Server.Swashbuckle.Test.csproj index 9639ae7e..1831fb3c 100644 --- a/tests/CommunityToolkit.Datasync.Server.Swashbuckle.Test/CommunityToolkit.Datasync.Server.Swashbuckle.Test.csproj +++ b/tests/CommunityToolkit.Datasync.Server.Swashbuckle.Test/CommunityToolkit.Datasync.Server.Swashbuckle.Test.csproj @@ -18,4 +18,19 @@ + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + diff --git a/tests/CommunityToolkit.Datasync.Server.Test/CommunityToolkit.Datasync.Server.Test.csproj b/tests/CommunityToolkit.Datasync.Server.Test/CommunityToolkit.Datasync.Server.Test.csproj index 3d74d7ad..192b804c 100644 --- a/tests/CommunityToolkit.Datasync.Server.Test/CommunityToolkit.Datasync.Server.Test.csproj +++ b/tests/CommunityToolkit.Datasync.Server.Test/CommunityToolkit.Datasync.Server.Test.csproj @@ -4,4 +4,18 @@ + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props index 00f7aad5..06698079 100644 --- a/tests/Directory.Build.props +++ b/tests/Directory.Build.props @@ -40,18 +40,6 @@ - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - From cdf3b7c00892805556769acf92c9dbb23c06e618 Mon Sep 17 00:00:00 2001 From: Adrian Hall Date: Fri, 15 Nov 2024 09:56:12 -0800 Subject: [PATCH 13/15] (#149) Updated NSwag --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 73b0d24d..cd354561 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -24,7 +24,7 @@ - + From 058f3288f1eb88cf5834dff93077eb318fd3fb00 Mon Sep 17 00:00:00 2001 From: Adrian Hall Date: Fri, 15 Nov 2024 09:58:05 -0800 Subject: [PATCH 14/15] (#149) Updated Swashbuckle. --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index cd354561..edc372e1 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -26,7 +26,7 @@ - + From d611a26636e1a1ee080abf8d6a0e121a90f4c7b9 Mon Sep 17 00:00:00 2001 From: Adrian Hall Date: Fri, 15 Nov 2024 10:18:49 -0800 Subject: [PATCH 15/15] (#149) Upgrade of OData --- Directory.Packages.props | 5 +- .../CommunityToolkit.Datasync.Client.csproj | 4 +- .../Serialization/DatasyncSerializer.cs | 3 +- .../Serialization/JsonExtensions.cs | 27 +++++ .../Serialization/SpatialGeoJsonConverter.cs | 108 ++++++++++++++++++ ...oolkit.Datasync.Server.Abstractions.csproj | 3 +- .../Json/JsonExtensions.cs | 27 +++++ .../Json/SpatialGeoJsonConverter.cs | 108 ++++++++++++++++++ .../DatasyncExtensions.cs | 3 +- .../CommunityToolkit.Datasync.Server.csproj | 1 - .../Controllers/TableController.Query.cs | 3 +- .../Extensions/InternalExtensions.cs | 3 +- .../Models/DatasyncServiceOptions.cs | 3 +- .../Offline/OfflineDbContext_Tests.cs | 4 +- .../Offline/OperationsQueueManager_Tests.cs | 2 +- .../Json/SerializerTests.cs | 3 +- .../LibraryExtensions.cs | 2 +- 17 files changed, 285 insertions(+), 24 deletions(-) create mode 100644 src/CommunityToolkit.Datasync.Client/Serialization/JsonExtensions.cs create mode 100644 src/CommunityToolkit.Datasync.Client/Serialization/SpatialGeoJsonConverter.cs create mode 100644 src/CommunityToolkit.Datasync.Server.Abstractions/Json/JsonExtensions.cs create mode 100644 src/CommunityToolkit.Datasync.Server.Abstractions/Json/SpatialGeoJsonConverter.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index edc372e1..e37b40b0 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -10,8 +10,7 @@ - - + @@ -22,7 +21,7 @@ - + diff --git a/src/CommunityToolkit.Datasync.Client/CommunityToolkit.Datasync.Client.csproj b/src/CommunityToolkit.Datasync.Client/CommunityToolkit.Datasync.Client.csproj index 9c75786f..f4831587 100644 --- a/src/CommunityToolkit.Datasync.Client/CommunityToolkit.Datasync.Client.csproj +++ b/src/CommunityToolkit.Datasync.Client/CommunityToolkit.Datasync.Client.csproj @@ -1,4 +1,4 @@ - + The client capabilities for developing applications using the Datasync Toolkit. @@ -12,7 +12,7 @@ - + diff --git a/src/CommunityToolkit.Datasync.Client/Serialization/DatasyncSerializer.cs b/src/CommunityToolkit.Datasync.Client/Serialization/DatasyncSerializer.cs index a50a206e..a2622238 100644 --- a/src/CommunityToolkit.Datasync.Client/Serialization/DatasyncSerializer.cs +++ b/src/CommunityToolkit.Datasync.Client/Serialization/DatasyncSerializer.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Azure.Core.Serialization; using System.Text.Json; using System.Text.Json.Serialization; @@ -58,7 +57,7 @@ public static string Serialize(object obj, Type objType) new DateTimeOffsetConverter(), new DateTimeConverter(), new TimeOnlyConverter(), - new MicrosoftSpatialGeoJsonConverter() + new SpatialGeoJsonConverter() }, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault, DictionaryKeyPolicy = JsonNamingPolicy.CamelCase, diff --git a/src/CommunityToolkit.Datasync.Client/Serialization/JsonExtensions.cs b/src/CommunityToolkit.Datasync.Client/Serialization/JsonExtensions.cs new file mode 100644 index 00000000..fab10d3c --- /dev/null +++ b/src/CommunityToolkit.Datasync.Client/Serialization/JsonExtensions.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Text.Json; + +namespace CommunityToolkit.Datasync.Client.Serialization; + +/// +/// Extension methods for System.Text.Json. +/// +internal static class JsonExtensions +{ + /// + /// Asserts that the current token of the matches the . + /// + /// The to assert. + /// The expected of the current token. + /// The current token did not match the . + public static void Expect(in this Utf8JsonReader reader, JsonTokenType expectedTokenType) + { + if (reader.TokenType != expectedTokenType) + { + throw new JsonException($"Deserialization failed. Expected token: '{expectedTokenType}'."); + } + } +} diff --git a/src/CommunityToolkit.Datasync.Client/Serialization/SpatialGeoJsonConverter.cs b/src/CommunityToolkit.Datasync.Client/Serialization/SpatialGeoJsonConverter.cs new file mode 100644 index 00000000..33e62fea --- /dev/null +++ b/src/CommunityToolkit.Datasync.Client/Serialization/SpatialGeoJsonConverter.cs @@ -0,0 +1,108 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.Spatial; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace CommunityToolkit.Datasync.Client.Serialization; + +/// +/// Converters between Microsoft.Spatial types and GeoJSON +/// +/// +/// Only handles GeographyPoint at this time. +/// +/// +public class SpatialGeoJsonConverter : JsonConverter +{ + private const string CoordinatesPropertyName = "coordinates"; + private const string PointTypeName = "Point"; + private const string TypePropertyName = "type"; + + private static readonly JsonEncodedText s_CoordinatesPropertyNameBytes = JsonEncodedText.Encode(CoordinatesPropertyName); + private static readonly JsonEncodedText s_TypePropertyNameBytes = JsonEncodedText.Encode(TypePropertyName); + + /// + public override bool CanConvert(Type typeToConvert) => + typeof(GeographyPoint).IsAssignableFrom(typeToConvert); + + /// + public override object? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.Null) + { + return null; + } + + string? type = default; + double? longitude = default; + double? latitude = default; + + reader.Expect(JsonTokenType.StartObject); + while (reader.Read() && reader.TokenType != JsonTokenType.EndObject) + { + reader.Expect(JsonTokenType.PropertyName); + string? propertyName = reader.GetString(); + + _ = reader.Read(); + if (string.Equals(TypePropertyName, propertyName, StringComparison.Ordinal)) + { + reader.Expect(JsonTokenType.String); + type = reader.GetString(); + } + else if (string.Equals(CoordinatesPropertyName, propertyName, StringComparison.Ordinal)) + { + reader.Expect(JsonTokenType.StartArray); + + // Longitude + _ = reader.Read(); + reader.Expect(JsonTokenType.Number); + longitude = reader.GetDouble(); + + // Latitude + _ = reader.Read(); + reader.Expect(JsonTokenType.Number); + latitude = reader.GetDouble(); + + // Skip the rest. + do + { + _ = reader.Read(); + } while (reader.TokenType != JsonTokenType.EndArray); + } + else + { + reader.Skip(); + } + } + + if (!string.Equals(PointTypeName, type, StringComparison.Ordinal)) + { + throw new JsonException($"Deserialization of {nameof(GeographyPoint)} failed. Expected geographic type: '{PointTypeName}'."); + } + + if (!longitude.HasValue || !latitude.HasValue) + { + throw new JsonException($"Deserialization of {nameof(GeographyPoint)} failed. Expected both longitude and latitude."); + } + + return GeographyPoint.Create(latitude.Value, longitude.Value); + } + + /// + public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options) + { + if (value is GeographyPoint point) + { + writer.WriteStartObject(); + writer.WriteString(s_TypePropertyNameBytes, PointTypeName); + writer.WriteStartArray(s_CoordinatesPropertyNameBytes); + writer.WriteNumberValue(point.Longitude); + writer.WriteNumberValue(point.Latitude); + writer.WriteEndArray(); + writer.WriteEndObject(); + } + } +} diff --git a/src/CommunityToolkit.Datasync.Server.Abstractions/CommunityToolkit.Datasync.Server.Abstractions.csproj b/src/CommunityToolkit.Datasync.Server.Abstractions/CommunityToolkit.Datasync.Server.Abstractions.csproj index e2ed021a..db2d1908 100644 --- a/src/CommunityToolkit.Datasync.Server.Abstractions/CommunityToolkit.Datasync.Server.Abstractions.csproj +++ b/src/CommunityToolkit.Datasync.Server.Abstractions/CommunityToolkit.Datasync.Server.Abstractions.csproj @@ -2,8 +2,7 @@ Abstractions for developing server-side applications using the Datasync Toolkit. - - + diff --git a/src/CommunityToolkit.Datasync.Server.Abstractions/Json/JsonExtensions.cs b/src/CommunityToolkit.Datasync.Server.Abstractions/Json/JsonExtensions.cs new file mode 100644 index 00000000..abc33dd8 --- /dev/null +++ b/src/CommunityToolkit.Datasync.Server.Abstractions/Json/JsonExtensions.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Text.Json; + +namespace CommunityToolkit.Datasync.Server.Abstractions.Json; + +/// +/// Extension methods for System.Text.Json. +/// +internal static class JsonExtensions +{ + /// + /// Asserts that the current token of the matches the . + /// + /// The to assert. + /// The expected of the current token. + /// The current token did not match the . + public static void Expect(in this Utf8JsonReader reader, JsonTokenType expectedTokenType) + { + if (reader.TokenType != expectedTokenType) + { + throw new JsonException($"Deserialization failed. Expected token: '{expectedTokenType}'."); + } + } +} diff --git a/src/CommunityToolkit.Datasync.Server.Abstractions/Json/SpatialGeoJsonConverter.cs b/src/CommunityToolkit.Datasync.Server.Abstractions/Json/SpatialGeoJsonConverter.cs new file mode 100644 index 00000000..9e1bf460 --- /dev/null +++ b/src/CommunityToolkit.Datasync.Server.Abstractions/Json/SpatialGeoJsonConverter.cs @@ -0,0 +1,108 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.Spatial; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace CommunityToolkit.Datasync.Server.Abstractions.Json; + +/// +/// Converters between Microsoft.Spatial types and GeoJSON +/// +/// +/// Only handles GeographyPoint at this time. +/// +/// +public class SpatialGeoJsonConverter : JsonConverter +{ + private const string CoordinatesPropertyName = "coordinates"; + private const string PointTypeName = "Point"; + private const string TypePropertyName = "type"; + + private static readonly JsonEncodedText s_CoordinatesPropertyNameBytes = JsonEncodedText.Encode(CoordinatesPropertyName); + private static readonly JsonEncodedText s_TypePropertyNameBytes = JsonEncodedText.Encode(TypePropertyName); + + /// + public override bool CanConvert(Type typeToConvert) => + typeof(GeographyPoint).IsAssignableFrom(typeToConvert); + + /// + public override object? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.Null) + { + return null; + } + + string? type = default; + double? longitude = default; + double? latitude = default; + + reader.Expect(JsonTokenType.StartObject); + while (reader.Read() && reader.TokenType != JsonTokenType.EndObject) + { + reader.Expect(JsonTokenType.PropertyName); + string? propertyName = reader.GetString(); + + _ = reader.Read(); + if (string.Equals(TypePropertyName, propertyName, StringComparison.Ordinal)) + { + reader.Expect(JsonTokenType.String); + type = reader.GetString(); + } + else if (string.Equals(CoordinatesPropertyName, propertyName, StringComparison.Ordinal)) + { + reader.Expect(JsonTokenType.StartArray); + + // Longitude + _ = reader.Read(); + reader.Expect(JsonTokenType.Number); + longitude = reader.GetDouble(); + + // Latitude + _ = reader.Read(); + reader.Expect(JsonTokenType.Number); + latitude = reader.GetDouble(); + + // Skip the rest. + do + { + _ = reader.Read(); + } while (reader.TokenType != JsonTokenType.EndArray); + } + else + { + reader.Skip(); + } + } + + if (!string.Equals(PointTypeName, type, StringComparison.Ordinal)) + { + throw new JsonException($"Deserialization of {nameof(GeographyPoint)} failed. Expected geographic type: '{PointTypeName}'."); + } + + if (!longitude.HasValue || !latitude.HasValue) + { + throw new JsonException($"Deserialization of {nameof(GeographyPoint)} failed. Expected both longitude and latitude."); + } + + return GeographyPoint.Create(latitude.Value, longitude.Value); + } + + /// + public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options) + { + if (value is GeographyPoint point) + { + writer.WriteStartObject(); + writer.WriteString(s_TypePropertyNameBytes, PointTypeName); + writer.WriteStartArray(s_CoordinatesPropertyNameBytes); + writer.WriteNumberValue(point.Longitude); + writer.WriteNumberValue(point.Latitude); + writer.WriteEndArray(); + writer.WriteEndObject(); + } + } +} diff --git a/src/CommunityToolkit.Datasync.Server.InMemory/DatasyncExtensions.cs b/src/CommunityToolkit.Datasync.Server.InMemory/DatasyncExtensions.cs index 2ca0b427..f558ab1c 100644 --- a/src/CommunityToolkit.Datasync.Server.InMemory/DatasyncExtensions.cs +++ b/src/CommunityToolkit.Datasync.Server.InMemory/DatasyncExtensions.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Azure.Core.Serialization; using CommunityToolkit.Datasync.Server.Abstractions.Json; using System.Text.Json; using System.Text.Json.Serialization; @@ -32,7 +31,7 @@ public static TEntity Clone(this TEntity entity) new DateTimeOffsetConverter(), new DateTimeConverter(), new TimeOnlyConverter(), - new MicrosoftSpatialGeoJsonConverter() + new SpatialGeoJsonConverter() }, DefaultIgnoreCondition = JsonIgnoreCondition.Never, DictionaryKeyPolicy = JsonNamingPolicy.CamelCase, diff --git a/src/CommunityToolkit.Datasync.Server/CommunityToolkit.Datasync.Server.csproj b/src/CommunityToolkit.Datasync.Server/CommunityToolkit.Datasync.Server.csproj index 8740946c..5e13ef1d 100644 --- a/src/CommunityToolkit.Datasync.Server/CommunityToolkit.Datasync.Server.csproj +++ b/src/CommunityToolkit.Datasync.Server/CommunityToolkit.Datasync.Server.csproj @@ -13,7 +13,6 @@ - diff --git a/src/CommunityToolkit.Datasync.Server/Controllers/TableController.Query.cs b/src/CommunityToolkit.Datasync.Server/Controllers/TableController.Query.cs index f719dcb2..af65def2 100644 --- a/src/CommunityToolkit.Datasync.Server/Controllers/TableController.Query.cs +++ b/src/CommunityToolkit.Datasync.Server/Controllers/TableController.Query.cs @@ -105,9 +105,8 @@ protected static IServiceProvider BuildServiceProvider(HttpRequest request) services .AddScoped() - .AddScoped() + .AddScoped(_ => new ODataQuerySettings { EnsureStableOrdering = true }) .AddSingleton(_ => new UnqualifiedODataUriResolver { EnableCaseInsensitive = true }) - .AddScoped() .AddScoped(); IServiceProvider provider = services.BuildServiceProvider(); diff --git a/src/CommunityToolkit.Datasync.Server/Extensions/InternalExtensions.cs b/src/CommunityToolkit.Datasync.Server/Extensions/InternalExtensions.cs index 54152110..0a435753 100644 --- a/src/CommunityToolkit.Datasync.Server/Extensions/InternalExtensions.cs +++ b/src/CommunityToolkit.Datasync.Server/Extensions/InternalExtensions.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Azure.Core.Serialization; using CommunityToolkit.Datasync.Server.Abstractions.Json; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Headers; @@ -66,7 +65,7 @@ internal static bool EntityIsInView(this IAccessControlProvider(this TEntity entity) new DateTimeOffsetConverter(), new DateTimeConverter(), new TimeOnlyConverter(), - new MicrosoftSpatialGeoJsonConverter() + new SpatialGeoJsonConverter() }, DefaultIgnoreCondition = JsonIgnoreCondition.Never, DictionaryKeyPolicy = JsonNamingPolicy.CamelCase,