diff --git a/src/Microsoft.OData.Core/ODataServiceCollectionExtensions.cs b/src/Microsoft.OData.Core/ODataServiceCollectionExtensions.cs index 7637411ef3..66cd9c3199 100644 --- a/src/Microsoft.OData.Core/ODataServiceCollectionExtensions.cs +++ b/src/Microsoft.OData.Core/ODataServiceCollectionExtensions.cs @@ -55,15 +55,15 @@ public static IServiceCollection AddDefaultODataServices(this IServiceCollection // Finally, register configurable settings. var readerSettings = new ODataMessageReaderSettings(odataVersion); configureReaderAction?.Invoke(readerSettings); - services.AddScoped(sp => readerSettings); + services.AddScoped(sp => readerSettings.Clone()); var writerSettings = new ODataMessageWriterSettings(odataVersion); configureWriterAction?.Invoke(writerSettings); - services.AddScoped(sp => writerSettings); + services.AddScoped(sp => writerSettings.Clone()); var parserSettings = new ODataUriParserSettings(); configureUriParserAction?.Invoke(parserSettings); - services.AddScoped(sp => parserSettings); + services.AddScoped(sp => parserSettings.Clone()); return services; } diff --git a/src/Microsoft.OData.Core/UriParser/ODataUriParserSettings.cs b/src/Microsoft.OData.Core/UriParser/ODataUriParserSettings.cs index f41fbaacff..a19d663fe6 100644 --- a/src/Microsoft.OData.Core/UriParser/ODataUriParserSettings.cs +++ b/src/Microsoft.OData.Core/UriParser/ODataUriParserSettings.cs @@ -89,6 +89,20 @@ public ODataUriParserSettings() this.EnableParsingKeyAsSegment = true; } + internal ODataUriParserSettings Clone() + { + return new() + { + FilterLimit = this.FilterLimit, + OrderByLimit = this.OrderByLimit, + PathLimit = this.PathLimit, + SearchLimit = this.SearchLimit, + MaximumExpansionDepth = this.MaximumExpansionDepth, + MaximumExpansionCount = this.MaximumExpansionCount, + EnableParsingKeyAsSegment = this.EnableParsingKeyAsSegment + }; + } + /// /// Gets or sets the maximum depth of the tree that results from parsing $expand. /// diff --git a/test/FunctionalTests/Microsoft.OData.Core.Tests/ODataServiceCollectionExtensionTests.cs b/test/FunctionalTests/Microsoft.OData.Core.Tests/ODataServiceCollectionExtensionTests.cs index 5450f5e7f1..8a1b05d6b1 100644 --- a/test/FunctionalTests/Microsoft.OData.Core.Tests/ODataServiceCollectionExtensionTests.cs +++ b/test/FunctionalTests/Microsoft.OData.Core.Tests/ODataServiceCollectionExtensionTests.cs @@ -4,11 +4,11 @@ // //--------------------------------------------------------------------- -using System; using Microsoft.Extensions.DependencyInjection; using Microsoft.OData.Edm; using Microsoft.OData.Json; using Microsoft.OData.UriParser; +using System; using Xunit; namespace Microsoft.OData.Tests @@ -136,6 +136,29 @@ public void AddDefaultODataServices_ReaderSettings_CanConfigure() Assert.True(readerSettings.EnableCharactersCheck); } + [Fact] + public void AddDefaultODataServices_ReaderSettings_InstancesAreScoped() + { + var services = new ServiceCollection(); + services.AddDefaultODataServices(); + var provider = services.BuildServiceProvider(); + using var scope1 = provider.CreateScope(); + var settings = scope1.ServiceProvider.GetRequiredService(); + settings.EnableCharactersCheck = true; + + var settingsFromSameScope = scope1.ServiceProvider.GetRequiredService(); + + using var scope2 = provider.CreateScope(); + var settingsFromOtherScope = scope2.ServiceProvider.GetRequiredService(); + + // Instances from the same scope should be the same + Assert.True(object.ReferenceEquals(settings, settingsFromSameScope)); + Assert.True(settingsFromSameScope.EnableCharactersCheck); + // Instances from different scopes should be different + Assert.False(object.ReferenceEquals(settings, settingsFromOtherScope)); + Assert.False(settingsFromOtherScope.EnableCharactersCheck); + } + /// /// Tests whether the can be configured using the . /// @@ -160,6 +183,29 @@ public void AddDefaultODataServices_WriterSettings_CanConfigure() Assert.True(writerSettings.EnableCharactersCheck); } + [Fact] + public void AddDefaultODataServices_WriterSettings_InstancesAreScoped() + { + var services = new ServiceCollection(); + services.AddDefaultODataServices(); + var provider = services.BuildServiceProvider(); + using var scope1 = provider.CreateScope(); + var settings = scope1.ServiceProvider.GetRequiredService(); + settings.EnableCharactersCheck = true; + + var settingsFromSameScope = scope1.ServiceProvider.GetRequiredService(); + + using var scope2 = provider.CreateScope(); + var settingsFromOtherScope = scope2.ServiceProvider.GetRequiredService(); + + // Instances from the same scope should be the same + Assert.True(object.ReferenceEquals(settings, settingsFromSameScope)); + Assert.True(settingsFromSameScope.EnableCharactersCheck); + // Instances from different scopes should be different + Assert.False(object.ReferenceEquals(settings, settingsFromOtherScope)); + Assert.False(settingsFromOtherScope.EnableCharactersCheck); + } + /// /// Tests whether the can be configured using the . /// @@ -184,6 +230,29 @@ public void AddDefaultODataServices_ODataUriParserSettings_CanConfigure() Assert.Equal(1, parserSettings.MaximumExpansionCount); } + [Fact] + public void AddDefaultODataServices_ODataUriParserSettings_InstancesAreScoped() + { + var services = new ServiceCollection(); + services.AddDefaultODataServices(); + var provider = services.BuildServiceProvider(); + using var scope1 = provider.CreateScope(); + var settings = scope1.ServiceProvider.GetRequiredService(); + settings.EnableParsingKeyAsSegment = false; + + var settingsFromSameScope = scope1.ServiceProvider.GetRequiredService(); + + using var scope2 = provider.CreateScope(); + var settingsFromOtherScope = scope2.ServiceProvider.GetRequiredService(); + + // Instances from the same scope should be the same + Assert.True(object.ReferenceEquals(settings, settingsFromSameScope)); + Assert.False(settingsFromSameScope.EnableParsingKeyAsSegment); + // Instances from different scopes should be different + Assert.False(object.ReferenceEquals(settings, settingsFromOtherScope)); + Assert.True(settingsFromOtherScope.EnableParsingKeyAsSegment); + } + /// /// Tests whether the correct exception is thrown when no is provided. ///