diff --git a/.gitignore b/.gitignore index 16dd581a..b71330e0 100644 --- a/.gitignore +++ b/.gitignore @@ -68,6 +68,7 @@ artifacts/ *.pidb *.svclog *.scc +*.lutconfig # Chutzpah Test files _Chutzpah* @@ -251,3 +252,4 @@ paket-files/ .idea/ *.sln.iml **/BenchmarkDotNet.Artifacts/ + diff --git a/README.md b/README.md index c7a51be2..c8176981 100644 --- a/README.md +++ b/README.md @@ -14,17 +14,15 @@ Support ref\out params, exceptions. Works with legacy SOAP\WCF-clients. The following frameworks are supported: -- .NET 8.0 (using ASP.NET Core 8.0) -- .NET Core 3.1 (using ASP.NET Core 3.1) -- .NET Standard 2.0-2.1 (using ASP.NET Core 2.1) +- .NET 10.0 +- .NET 8.0 +- .NET Standard 2.0-2.1 ### Installing `PM> Install-Package SoapCore` -There are 2 different ways of adding SoapCore to your ASP.NET Core website. If you are using ASP.NET Core 3.1 or higher with endpoint routing enabled (the default): - -In Startup.cs: +In Startup.cs or Program.cs (for .NET 8+ minimal hosting): ```csharp public void ConfigureServices(IServiceCollection services) diff --git a/samples/Client/Client.csproj b/samples/Client/Client.csproj index d6568865..7c0f424f 100644 --- a/samples/Client/Client.csproj +++ b/samples/Client/Client.csproj @@ -1,12 +1,13 @@  - netcoreapp3.1 + net8.0 Exe + enable - + diff --git a/samples/Net10Client/Net10Client.csproj b/samples/Net10Client/Net10Client.csproj new file mode 100644 index 00000000..f8d0a895 --- /dev/null +++ b/samples/Net10Client/Net10Client.csproj @@ -0,0 +1,22 @@ + + + + net10.0 + Exe + enable + enable + + + + + + + + + + + + + + + diff --git a/samples/Net10Client/Program.cs b/samples/Net10Client/Program.cs new file mode 100644 index 00000000..5fab175b --- /dev/null +++ b/samples/Net10Client/Program.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.ServiceModel; +using Models; + +namespace Net10Client +{ + public class Program + { + public static void Main() + { + Console.WriteLine("NET 10 Client - Connecting to SOAP service..."); + + var binding = new BasicHttpBinding(); + var endpoint = new EndpointAddress(new Uri($"http://{Environment.MachineName}:5060/Service.svc")); + using var channelFactory = new ChannelFactory(binding, endpoint); + var serviceClient = channelFactory.CreateChannel(); + + try + { + var result = serviceClient.Ping("Hello from NET 10 Client"); + Console.WriteLine("Ping method result: {0}", result); + + var complexModel = new ComplexModelInput + { + StringProperty = Guid.NewGuid().ToString(), + IntProperty = int.MaxValue / 2, + ListProperty = new List { "NET", "10", "test", "list" }, + DateTimeOffsetProperty = new DateTimeOffset(2024, 12, 31, 13, 59, 59, TimeSpan.FromHours(1)) + }; + + var complexResult = serviceClient.PingComplexModel(complexModel); + Console.WriteLine("PingComplexModel result. FloatProperty: {0}, StringProperty: {1}, ListProperty: {2}, DateTimeOffsetProperty: {3}, EnumProperty: {4}", + complexResult.FloatProperty, complexResult.StringProperty, string.Join(", ", complexResult.ListProperty), + complexResult.DateTimeOffsetProperty, complexResult.TestEnum); + + serviceClient.VoidMethod(out var stringValue); + Console.WriteLine("Void method result: {0}", stringValue); + + var asyncMethodResult = serviceClient.AsyncMethod().Result; + Console.WriteLine("Async method result: {0}", asyncMethodResult); + + var xmlElement = System.Xml.Linq.XElement.Parse("NET 10 string"); + serviceClient.XmlMethod(xmlElement); + Console.WriteLine("XmlMethod executed successfully"); + + var complexReturnModels = serviceClient.ComplexReturnModel(); + Console.WriteLine("ComplexReturnModel results:"); + foreach (var model in complexReturnModels) + { + Console.WriteLine(" Id: {0}, Name: {1}", model.Id, model.Name); + } + + Console.WriteLine("\nAll tests completed successfully!"); + } + catch (Exception ex) + { + Console.WriteLine("Error: {0}", ex.Message); + } + + Console.WriteLine("\nPress any key to exit..."); + Console.ReadKey(); + } + } +} diff --git a/samples/Net10Client/SampleService.cs b/samples/Net10Client/SampleService.cs new file mode 100644 index 00000000..3e3bb996 --- /dev/null +++ b/samples/Net10Client/SampleService.cs @@ -0,0 +1,62 @@ +using Models; +using System.Xml.Linq; + +namespace Net10Client +{ + public class SampleService : ISampleService + { + public string Ping(string s) + { + Console.WriteLine("NET 10 Server - Exec ping method"); + return $"NET 10 Response: {s}"; + } + + public ComplexModelResponse PingComplexModel(ComplexModelInput inputModel) + { + Console.WriteLine("NET 10 Server - Input data. IntProperty: {0}, StringProperty: {1}", + inputModel.IntProperty, inputModel.StringProperty); + + return new ComplexModelResponse + { + FloatProperty = float.MaxValue / 2, + StringProperty = inputModel.StringProperty, + ListProperty = inputModel.ListProperty, + DateTimeOffsetProperty = inputModel.DateTimeOffsetProperty + }; + } + + public int[] IntArray() + { + return new int[] { 123, 456, 789 }; + } + + public void VoidMethod(out string s) + { + s = "Value from NET 10 server"; + } + + public Task AsyncMethod() + { + return Task.FromResult(42); + } + + public int? NullableMethod(bool? arg) + { + return null; + } + + public void XmlMethod(XElement xml) + { + Console.WriteLine("NET 10 Server - XML: {0}", xml); + } + + public ComplexReturnModel[] ComplexReturnModel() + { + return new ComplexReturnModel[] + { + new ComplexReturnModel { Id = 1, Name = "NET 10 Item 1" }, + new ComplexReturnModel { Id = 2, Name = "NET 10 Item 2" } + }; + } + } +} diff --git a/samples/Net10Sample/README.md b/samples/Net10Sample/README.md new file mode 100644 index 00000000..46d82c6e --- /dev/null +++ b/samples/Net10Sample/README.md @@ -0,0 +1,53 @@ +# .NET 10 SoapCore Sample + +This sample demonstrates how to use SoapCore with .NET 10, showcasing both a SOAP server and client implementation. + +## Projects + +### Net10Server +A .NET 10 web application that hosts a SOAP service using SoapCore. + +- **Port**: 5060 +- **Endpoints**: + - `/Service.svc` - DataContractSerializer endpoint + - `/Service.asmx` - XmlSerializer endpoint + +### Net10Client +A .NET 10 console application that consumes the SOAP service. + +## Running the Sample + +1. **Start the Server**: + ```bash + cd Net10Server + dotnet run + ``` + +2. **Run the Client** (in a separate terminal): + ```bash + cd Net10Client + dotnet run + ``` + +## Features Demonstrated + +- Basic string operations (Ping) +- Complex model serialization/deserialization +- Void methods with out parameters +- Async methods +- XML element handling +- Array return types +- Nullable types + +## Key Differences from Previous Samples + +- Uses modern .NET 10 hosting model with `IHostBuilder` +- Updated to use System.ServiceModel 8.0.* packages for better .NET 10 compatibility +- Demonstrates implicit usings and nullable reference types +- Uses a different port (5060) to avoid conflicts with other sample projects + +## Requirements + +- .NET 10 SDK +- SoapCore library +- Models project (shared across all samples) diff --git a/samples/Net10Server/Net10Server.csproj b/samples/Net10Server/Net10Server.csproj new file mode 100644 index 00000000..5d906d14 --- /dev/null +++ b/samples/Net10Server/Net10Server.csproj @@ -0,0 +1,13 @@ + + + + net10.0 + enable + + + + + + + + diff --git a/samples/Net10Server/Program.cs b/samples/Net10Server/Program.cs new file mode 100644 index 00000000..c74922c4 --- /dev/null +++ b/samples/Net10Server/Program.cs @@ -0,0 +1,27 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace Net10Server +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + webBuilder.UseUrls("http://*:5060"); + }) + .ConfigureLogging(logging => + { + logging.AddDebug(); + logging.AddConsole(); + }); + } +} diff --git a/samples/Net10Server/Properties/launchSettings.json b/samples/Net10Server/Properties/launchSettings.json new file mode 100644 index 00000000..349e3d54 --- /dev/null +++ b/samples/Net10Server/Properties/launchSettings.json @@ -0,0 +1,12 @@ +{ + "profiles": { + "Net10Server": { + "commandName": "Project", + "launchBrowser": false, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "http://localhost:5060" + } + } +} diff --git a/samples/Net10Server/SampleService.cs b/samples/Net10Server/SampleService.cs new file mode 100644 index 00000000..0af766f0 --- /dev/null +++ b/samples/Net10Server/SampleService.cs @@ -0,0 +1,63 @@ +using Models; +using System; +using System.Threading.Tasks; +using System.Xml.Linq; + +namespace Net10Server +{ + public class SampleService : ISampleService + { + public string Ping(string s) + { + Console.WriteLine("NET 10 Server - Exec ping method"); + return $"NET 10 Response: {s}"; + } + + public ComplexModelResponse PingComplexModel(ComplexModelInput inputModel) + { + Console.WriteLine("NET 10 Server - Input data. IntProperty: {0}, StringProperty: {1}", + inputModel.IntProperty, inputModel.StringProperty); + + return new ComplexModelResponse + { + FloatProperty = float.MaxValue / 2, + StringProperty = inputModel.StringProperty, + ListProperty = inputModel.ListProperty, + DateTimeOffsetProperty = inputModel.DateTimeOffsetProperty + }; + } + + public int[] IntArray() + { + return [123, 456, 789]; + } + + public void VoidMethod(out string s) + { + s = "Value from NET 10 server"; + } + + public Task AsyncMethod() + { + return Task.FromResult(42); + } + + public int? NullableMethod(bool? arg) + { + return null; + } + + public void XmlMethod(XElement xml) + { + Console.WriteLine("NET 10 Server - XML: {0}", xml); + } + + public ComplexReturnModel[] ComplexReturnModel() + { + return [ + new ComplexReturnModel { Id = 1, Name = "NET 10 Item 1" }, + new ComplexReturnModel { Id = 2, Name = "NET 10 Item 2" } + ]; + } + } +} diff --git a/samples/Net10Server/Startup.cs b/samples/Net10Server/Startup.cs new file mode 100644 index 00000000..0cd7c9f0 --- /dev/null +++ b/samples/Net10Server/Startup.cs @@ -0,0 +1,28 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Models; +using SoapCore; + +namespace Net10Server +{ + public class Startup + { + public void ConfigureServices(IServiceCollection services) + { + services.AddSoapCore(); + services.TryAddSingleton(); + } + + public void Configure(IApplicationBuilder app) + { + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.UseSoapEndpoint("/Service.svc", new SoapEncoderOptions(), SoapSerializer.DataContractSerializer); + endpoints.UseSoapEndpoint("/Service.asmx", new SoapEncoderOptions(), SoapSerializer.XmlSerializer); + }); + } + } +} diff --git a/samples/README.md b/samples/README.md new file mode 100644 index 00000000..847e32b2 --- /dev/null +++ b/samples/README.md @@ -0,0 +1,75 @@ +# SoapCore Samples + +This directory contains various sample projects demonstrating SoapCore functionality across different .NET versions. + +## Sample Projects + +### Server (.NET 8) +A .NET 8 web application hosting a SOAP service. +- **Port**: 5050 +- **Target Framework**: net8.0 + +### Client (.NET 8) +A .NET 8 console application consuming the SOAP service. +- **Target Framework**: net8.0 + +### .NET 10 Sample + +#### Net10Server +A modern .NET 10 web application hosting a SOAP service using SoapCore. +- **Port**: 5060 +- **Target Framework**: net10.0 +- **Features**: Modern hosting model, latest System.ServiceModel packages (8.0.*) + +#### Net10Client +A .NET 10 console application demonstrating SOAP client capabilities. +- **Target Framework**: net10.0 +- **Features**: Implicit usings, nullable reference types, latest ServiceModel packages + +### Shared Projects + +#### Models +Shared model library used across all samples. +- **Target Framework**: netstandard2.0 +- **Contains**: Service contracts and data models + +## Getting Started + +1. **Build the solution**: + ```bash + dotnet build + ``` + +2. **Run a server** (choose one): + ```bash + # .NET 10 server (recommended) + cd Net10Server + dotnet run + + # .NET 8 server + cd Server + dotnet run + ``` + +3. **Run a client** (in a separate terminal): + ```bash + # .NET 10 client (recommended) + cd Net10Client + dotnet run + + # .NET 8 client + cd Client + dotnet run + ``` + +## Port Assignments + +- **5050**: Server (.NET 8) +- **5060**: Net10Server (.NET 10) + +Make sure the client connects to the correct port based on which server you're running. + +## Requirements + +- .NET 8 SDK (for Server, Client, and ServiceReferenceClient) +- .NET 10 SDK (for .NET 10 samples - recommended) diff --git a/samples/Sample.sln b/samples/Sample.sln index cfff8cec..8537880f 100644 --- a/samples/Sample.sln +++ b/samples/Sample.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.7.34031.279 +# Visual Studio Version 18 +VisualStudioVersion = 18.3.11304.161 d18.3 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Client", "Client\Client.csproj", "{EBFDF95D-F7CC-457F-8924-3CB6069FBA3B}" EndProject @@ -13,6 +13,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Models", "Models\Models.csp EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServiceReferenceClient", "ServiceReferenceClient\ServiceReferenceClient.csproj", "{B68A055C-6B71-4D2D-B663-6111753A0DA1}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Net10Server", "Net10Server\Net10Server.csproj", "{52EF2B0B-FAD1-9788-52D5-E74E7536ED7F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Net10Client", "Net10Client\Net10Client.csproj", "{15A8D149-7403-EFF0-D880-92D66D37F6C9}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -39,6 +43,14 @@ Global {B68A055C-6B71-4D2D-B663-6111753A0DA1}.Debug|Any CPU.Build.0 = Debug|Any CPU {B68A055C-6B71-4D2D-B663-6111753A0DA1}.Release|Any CPU.ActiveCfg = Release|Any CPU {B68A055C-6B71-4D2D-B663-6111753A0DA1}.Release|Any CPU.Build.0 = Release|Any CPU + {52EF2B0B-FAD1-9788-52D5-E74E7536ED7F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {52EF2B0B-FAD1-9788-52D5-E74E7536ED7F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {52EF2B0B-FAD1-9788-52D5-E74E7536ED7F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {52EF2B0B-FAD1-9788-52D5-E74E7536ED7F}.Release|Any CPU.Build.0 = Release|Any CPU + {15A8D149-7403-EFF0-D880-92D66D37F6C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {15A8D149-7403-EFF0-D880-92D66D37F6C9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {15A8D149-7403-EFF0-D880-92D66D37F6C9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {15A8D149-7403-EFF0-D880-92D66D37F6C9}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/samples/Server/Server.csproj b/samples/Server/Server.csproj index b9319b2b..e0248537 100644 --- a/samples/Server/Server.csproj +++ b/samples/Server/Server.csproj @@ -1,7 +1,8 @@ - netcoreapp3.1 + net8.0 + enable diff --git a/src/SoapCore.Benchmark/Program.cs b/src/SoapCore.Benchmark/Program.cs index 1726d121..33188e3d 100644 --- a/src/SoapCore.Benchmark/Program.cs +++ b/src/SoapCore.Benchmark/Program.cs @@ -13,12 +13,13 @@ using Microsoft.Extensions.Logging; using BenchmarkDotNet.Jobs; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.Extensions.Hosting; namespace SoapCore.Benchmark { [MemoryDiagnoser] [SimpleJob(RuntimeMoniker.Net80, baseline: true, iterationCount: 20)] - //[SimpleJob(RuntimeMoniker.NetCoreApp31, iterationCount: 20)] + [SimpleJob(RuntimeMoniker.Net10_0, iterationCount: 20)] public class EchoBench { // 0 measures overhead of creating host @@ -33,10 +34,24 @@ public class EchoBench "; static TestServer CreateTestHost() { +#if NET10_0_OR_GREATER + var host = new HostBuilder() + .ConfigureWebHost(webBuilder => + { + webBuilder + .UseTestServer() + .ConfigureLogging(logging => logging.SetMinimumLevel(LogLevel.Critical)) + .UseStartup(); + }) + .Build(); + host.Start(); + return host.GetTestServer(); +#else var builder = WebHost.CreateDefaultBuilder() .ConfigureLogging(logging => logging.SetMinimumLevel(LogLevel.Critical)) .UseStartup(); return new TestServer(builder); +#endif } TestServer m_Host; [GlobalSetup] diff --git a/src/SoapCore.Benchmark/SoapCore.Benchmark.csproj b/src/SoapCore.Benchmark/SoapCore.Benchmark.csproj index ccac0494..827e995b 100644 --- a/src/SoapCore.Benchmark/SoapCore.Benchmark.csproj +++ b/src/SoapCore.Benchmark/SoapCore.Benchmark.csproj @@ -2,26 +2,30 @@ Exe - netcoreapp3.1;net8.0 + net8.0;net10.0 ..\SoapCore.ruleset true - 8 + latest false - - - + + + - + + + + + diff --git a/src/SoapCore.Tests/SoapCore.Tests.csproj b/src/SoapCore.Tests/SoapCore.Tests.csproj index 26ae0353..af8987ed 100644 --- a/src/SoapCore.Tests/SoapCore.Tests.csproj +++ b/src/SoapCore.Tests/SoapCore.Tests.csproj @@ -1,7 +1,7 @@ - net8.0; + net8.0;net10.0; ..\SoapCore.ruleset false true @@ -9,6 +9,10 @@ latest + + $(NoWarn);ASPDEPR004;ASPDEPR008 + + @@ -25,24 +29,21 @@ runtime; build; native; contentfiles; analyzers - - - - - - - + + + + + all runtime; build; native; contentfiles; analyzers - - + all runtime; build; native; contentfiles; analyzers @@ -54,8 +55,8 @@ all runtime; build; native; contentfiles; analyzers - - + + diff --git a/src/SoapCore/ConcurrentXmlNamespaceLookup.cs b/src/SoapCore/ConcurrentXmlNamespaceLookup.cs index 5cf8594f..38409862 100644 --- a/src/SoapCore/ConcurrentXmlNamespaceLookup.cs +++ b/src/SoapCore/ConcurrentXmlNamespaceLookup.cs @@ -10,13 +10,17 @@ public class ConcurrentXmlNamespaceLookup private readonly ConcurrentDictionary _prefixToUri = new (); public IDictionary GetNamespacesInScope(XmlNamespaceScope scope) => ToXmlNamespaceManager().GetNamespacesInScope(scope); + public string LookupPrefix(string uri) => _uriToPrefix.TryGetValue(uri, out var r) ? r : null; + public string LookupNamespace(string prefix) => _prefixToUri.TryGetValue(prefix, out var r) ? r : null; + public void AddNamespace(string prefix, string uri) { _uriToPrefix.AddOrUpdate(uri, _ => prefix, (_, _) => prefix); _prefixToUri.AddOrUpdate(prefix, _ => uri, (_, _) => uri); } + public XmlNamespaceManager ToXmlNamespaceManager() { var r = new XmlNamespaceManager(new NameTable()); @@ -24,6 +28,7 @@ public XmlNamespaceManager ToXmlNamespaceManager() { r.AddNamespace(e.Key, e.Value); } + return r; } } diff --git a/src/SoapCore/DocumentationWriter/SoapBinding.cs b/src/SoapCore/DocumentationWriter/SoapBinding.cs index e6d76702..2eb0732a 100644 --- a/src/SoapCore/DocumentationWriter/SoapBinding.cs +++ b/src/SoapCore/DocumentationWriter/SoapBinding.cs @@ -4,8 +4,14 @@ namespace SoapCore.DocumentationWriter { + /// + /// Represents a SOAP service definition in WSDL format. + /// public partial class SoapDefinition { + /// + /// Represents a SOAP binding definition in a WSDL document. + /// public class SoapBinding : IElementWithSpecialTransforms { private string _namespace; diff --git a/src/SoapCore/DocumentationWriter/SoapDefinition.cs b/src/SoapCore/DocumentationWriter/SoapDefinition.cs index 3d9ae379..42ff4788 100644 --- a/src/SoapCore/DocumentationWriter/SoapDefinition.cs +++ b/src/SoapCore/DocumentationWriter/SoapDefinition.cs @@ -3,27 +3,51 @@ namespace SoapCore.DocumentationWriter { + /// + /// Represents a SOAP service definition in WSDL format. + /// [XmlRoot(ElementName = "definitions", Namespace = "http://schemas.xmlsoap.org/wsdl/")] public partial class SoapDefinition { + /// + /// Gets or sets the name of the service definition. + /// [XmlAttribute(AttributeName = "name")] public string Name { get; set; } + /// + /// Gets or sets the target namespace for the service definition. + /// [XmlAttribute(AttributeName = "targetNamespace")] public string TargetNamespace { get; set; } + /// + /// Gets or sets the types definitions for the service. + /// [XmlElement(ElementName = "types")] public SoapTypes Types { get; set; } + /// + /// Gets or sets the list of messages used in the service. + /// [XmlElement(ElementName = "message")] public List Messages { get; set; } + /// + /// Gets or sets the port type definition for the service. + /// [XmlElement(ElementName = "portType")] public SoapPortType PortType { get; set; } + /// + /// Gets or sets the list of bindings for the service. + /// [XmlElement(ElementName = "binding")] public List Bindings { get; set; } + /// + /// Gets or sets the service definition. + /// [XmlElement(ElementName = "service")] public SoapService Service { get; set; } } diff --git a/src/SoapCore/DocumentationWriter/SoapMessage.cs b/src/SoapCore/DocumentationWriter/SoapMessage.cs index 110de82f..ad1d66e3 100644 --- a/src/SoapCore/DocumentationWriter/SoapMessage.cs +++ b/src/SoapCore/DocumentationWriter/SoapMessage.cs @@ -3,19 +3,42 @@ namespace SoapCore.DocumentationWriter { + /// + /// Represents a SOAP service definition in WSDL format. + /// public partial class SoapDefinition { + /// + /// Represents a SOAP message definition in WSDL. + /// public class SoapMessage { + /// + /// Gets or sets the name of the message. + /// [XmlAttribute(AttributeName = "name")] public string Name { get; set; } + + /// + /// Gets or sets the list of message parts. + /// [XmlElement(ElementName = "part")] public List Part { get; set; } + /// + /// Represents a part of a SOAP message. + /// public class SoapMessagePart { + /// + /// Gets or sets the name of the message part. + /// [XmlAttribute(AttributeName = "name")] public string Name { get; set; } + + /// + /// Gets or sets the element reference for the message part. + /// [XmlAttribute(AttributeName = "element")] public string Element { get; set; } } diff --git a/src/SoapCore/DocumentationWriter/SoapMethods.cs b/src/SoapCore/DocumentationWriter/SoapMethods.cs index 7f970f0d..0825d8ee 100644 --- a/src/SoapCore/DocumentationWriter/SoapMethods.cs +++ b/src/SoapCore/DocumentationWriter/SoapMethods.cs @@ -6,6 +6,9 @@ namespace SoapCore.DocumentationWriter { + /// + /// Partial class containing methods for SOAP definition deserialization and documentation generation. + /// public partial class SoapDefinition { private static XmlElementEventHandler _unknownElementHandler = (sender, args) => diff --git a/src/SoapCore/DocumentationWriter/SoapPortType.cs b/src/SoapCore/DocumentationWriter/SoapPortType.cs index 89ea931e..46a9c76b 100644 --- a/src/SoapCore/DocumentationWriter/SoapPortType.cs +++ b/src/SoapCore/DocumentationWriter/SoapPortType.cs @@ -3,13 +3,25 @@ namespace SoapCore.DocumentationWriter { + /// + /// Represents a SOAP service definition in WSDL format. + /// public partial class SoapDefinition { + /// + /// Represents a SOAP port type definition in WSDL. + /// public class SoapPortType { + /// + /// Gets or sets the name of the port type. + /// [XmlAttribute(AttributeName = "name")] public string Name { get; set; } + /// + /// Gets or sets the list of operations for this port type. + /// [XmlElement(ElementName = "operation")] public List Operations { get; set; } } diff --git a/src/SoapCore/DocumentationWriter/SoapService.cs b/src/SoapCore/DocumentationWriter/SoapService.cs index 16329c85..fc8ac4b2 100644 --- a/src/SoapCore/DocumentationWriter/SoapService.cs +++ b/src/SoapCore/DocumentationWriter/SoapService.cs @@ -4,26 +4,55 @@ namespace SoapCore.DocumentationWriter { + /// + /// Represents a SOAP service definition in WSDL format. + /// public partial class SoapDefinition { + /// + /// Represents a SOAP service definition in WSDL. + /// public class SoapService { + /// + /// Gets or sets the name of the service. + /// [XmlAttribute(AttributeName = "name")] public string Name { get; set; } + /// + /// Gets or sets the list of service ports. + /// [XmlElement(ElementName = "port")] public List Ports { get; set; } + /// + /// Represents a service port definition. + /// public class SoapServicePort : IElementWithSpecialTransforms { + /// + /// Gets or sets the name of the port. + /// [XmlAttribute(AttributeName = "name")] public string Name { get; set; } + + /// + /// Gets or sets the binding reference for this port. + /// [XmlAttribute(AttributeName = "binding")] public string Binding { get; set; } + /// + /// Gets or sets the address information for this port. + /// [XmlElement(ElementName = "address")] public SoapServicePortAddress Address { get; set; } + /// + /// Deserializes XML elements to populate port properties. + /// + /// The XML element to deserialize. public void DeserializeElements(XmlElement element) { if (element.Name.EndsWith("address")) @@ -36,11 +65,20 @@ public void DeserializeElements(XmlElement element) } } + /// + /// Represents the address configuration for a service port. + /// public class SoapServicePortAddress { + /// + /// Gets or sets the location URL for the service port. + /// [XmlAttribute(AttributeName = "location")] public string Location { get; set; } + /// + /// Gets or sets the namespace for the service port address. + /// public string Namespace { get; set; } } } diff --git a/src/SoapCore/DocumentationWriter/SoapShared.cs b/src/SoapCore/DocumentationWriter/SoapShared.cs index 8ffb0f75..66e175e8 100644 --- a/src/SoapCore/DocumentationWriter/SoapShared.cs +++ b/src/SoapCore/DocumentationWriter/SoapShared.cs @@ -3,25 +3,52 @@ namespace SoapCore.DocumentationWriter { + /// + /// Represents a SOAP service definition in WSDL format. + /// public partial class SoapDefinition { + /// + /// Represents a WSDL operation definition. + /// public class WsdlOperation : IElementWithSpecialTransforms { private string _namespace; + /// + /// Gets or sets the name of the operation. + /// [XmlAttribute(AttributeName = "name")] public string Name { get; set; } + /// + /// Gets or sets the input message for the operation. + /// [XmlElement(ElementName = "input")] public OperationItem Input { get; set; } + + /// + /// Gets or sets the output message for the operation. + /// [XmlElement(ElementName = "output")] public OperationItem Output { get; set; } + + /// + /// Gets or sets the fault message for the operation. + /// [XmlElement(ElementName = "fault")] public OperationItem Fault { get; set; } + /// + /// Gets or sets the SOAP operation details. + /// [XmlElement(ElementName = "operation")] public SoapOperation Operation { get; set; } + /// + /// Deserializes XML elements to populate operation properties. + /// + /// The XML element to deserialize. public void DeserializeElements(XmlElement element) { if (element.Name.EndsWith("operation")) @@ -36,14 +63,27 @@ public void DeserializeElements(XmlElement element) } } + /// + /// Represents an operation input, output, or fault message. + /// public class OperationItem : IElementWithSpecialTransforms { + /// + /// Gets or sets the message reference. + /// [XmlAttribute(AttributeName = "message")] public string Message { get; set; } + /// + /// Gets or sets the operation body configuration. + /// [XmlElement(ElementName = "body")] public OperationBody Body { get; set; } + /// + /// Deserializes XML elements to populate operation item properties. + /// + /// The XML element to deserialize. public void DeserializeElements(XmlElement element) { if (element.Name.EndsWith("body")) @@ -55,18 +95,34 @@ public void DeserializeElements(XmlElement element) } } + /// + /// Represents the body configuration for an operation message. + /// public class OperationBody { + /// + /// Gets or sets the encoding style (literal or encoded). + /// [XmlAttribute(AttributeName = "use")] public string Use { get; set; } } } } + /// + /// Represents SOAP operation configuration details. + /// public class SoapOperation { + /// + /// Gets or sets the SOAP action URI. + /// [XmlAttribute(AttributeName = "soapAction")] public string SoapAction { get; set; } + + /// + /// Gets or sets the operation style (document or rpc). + /// [XmlAttribute(AttributeName = "style")] public string Style { get; set; } } diff --git a/src/SoapCore/DocumentationWriter/SoapTypes.cs b/src/SoapCore/DocumentationWriter/SoapTypes.cs index 440a16fb..9f26ae9c 100644 --- a/src/SoapCore/DocumentationWriter/SoapTypes.cs +++ b/src/SoapCore/DocumentationWriter/SoapTypes.cs @@ -3,91 +3,207 @@ namespace SoapCore.DocumentationWriter { + /// + /// Represents a SOAP service definition in WSDL format. + /// public partial class SoapDefinition { + /// + /// Represents the type definitions section of a WSDL document. + /// public class SoapTypes { + /// + /// Gets or sets the list of XML schema definitions. + /// [XmlElement(ElementName = "schema", Namespace = "http://www.w3.org/2001/XMLSchema")] public List Schema { get; set; } + /// + /// Represents an XML schema definition. + /// public class SoapTypeSchema { + /// + /// Gets or sets the element form default attribute. + /// [XmlAttribute(AttributeName = "elementFormDefault")] public string ElementFormDefault { get; set; } + + /// + /// Gets or sets the target namespace for the schema. + /// [XmlAttribute(AttributeName = "targetNamespace")] public string TargetNamespace { get; set; } + /// + /// Gets or sets the list of schema imports. + /// [XmlElement(ElementName = "import")] public List Imports { get; set; } + + /// + /// Gets or sets the list of schema elements. + /// [XmlElement(ElementName = "element")] public List Elements { get; set; } + /// + /// Gets or sets the list of complex type definitions. + /// [XmlElement(ElementName = "complexType")] public List ComplexTypes { get; set; } + + /// + /// Gets or sets the list of simple type definitions. + /// [XmlElement(ElementName = "simpleType")] public List SimpleTypes { get; set; } + /// + /// Represents a schema import statement. + /// public class SoapTypeSchemaImport { + /// + /// Gets or sets the namespace being imported. + /// [XmlAttribute(AttributeName = "namespace")] public string Namespace { get; set; } } + /// + /// Represents a schema element definition. + /// public class SoapTypeSchemaElement { + /// + /// Gets or sets the complex type for this element. + /// [XmlElement(ElementName = "complexType")] public ComplexType ComplexElementType { get; set; } + + /// + /// Gets or sets the name of the element. + /// [XmlAttribute(AttributeName = "name")] public string Name { get; set; } } + /// + /// Represents a simple type definition. + /// public class SimpleType { + /// + /// Gets or sets the name of the simple type. + /// [XmlAttribute(AttributeName = "name")] public string Name { get; set; } + + /// + /// Gets or sets the value restriction for the simple type. + /// [XmlElement(ElementName = "restriction")] public ValueRestriction Restriction { get; set; } + /// + /// Represents value restrictions for a simple type. + /// public class ValueRestriction { + /// + /// Gets or sets the list of enumeration values. + /// [XmlElement(ElementName = "enumeration")] public List EnumerationValue { get; set; } + /// + /// Represents an enumeration value. + /// public class EnumValue { + /// + /// Gets or sets the enumeration value. + /// [XmlAttribute(AttributeName = "value")] public string Value { get; set; } } } } + /// + /// Represents a complex type definition. + /// public class ComplexType { + /// + /// Gets or sets the name of the complex type. + /// [XmlAttribute(AttributeName = "name")] public string Name { get; set; } + /// + /// Gets or sets the sequence of elements in the complex type. + /// [XmlElement(ElementName = "sequence")] public Sequence TypeInformation { get; set; } + /// + /// Represents a sequence of elements. + /// public class Sequence { + /// + /// Gets or sets the list of elements in the sequence. + /// [XmlElement(ElementName = "element")] public List Element { get; set; } + /// + /// Represents an element in a sequence. + /// public class SequenceElement { + /// + /// Gets or sets the name of the element. + /// [XmlAttribute(AttributeName = "name")] public string Name { get; set; } + + /// + /// Gets or sets the minimum number of occurrences. + /// [XmlAttribute(AttributeName = "minOccurs")] public string MinimumOccurences { get; set; } + + /// + /// Gets or sets the maximum number of occurrences. + /// [XmlAttribute(AttributeName = "maxOccurs")] public string MaximumOccurences { get; set; } + + /// + /// Gets or sets the type of the element. + /// [XmlAttribute(AttributeName = "type")] public string Type { get; set; } + + /// + /// Gets or sets the reference to another element. + /// [XmlAttribute(AttributeName = "ref")] public string Ref { get; set; } + + /// + /// Gets or sets a value indicating whether the element is nullable. + /// [XmlAttribute(AttributeName = "nillable")] public bool Nullable { get; set; } = false; + + /// + /// Gets or sets a value indicating whether the element is abstract. + /// [XmlAttribute(AttributeName = "abstract")] public bool Abstract { get; set; } = false; } diff --git a/src/SoapCore/HeadersHelper.cs b/src/SoapCore/HeadersHelper.cs index 0ecfc228..16b45e6e 100644 --- a/src/SoapCore/HeadersHelper.cs +++ b/src/SoapCore/HeadersHelper.cs @@ -145,8 +145,7 @@ public static string GetSoapAction(HttpContext httpContext, ref Message message) } #endif - if (soapAction != null && - (GetTrimmedSoapAction(soapAction).Length == 0 || GetTrimmedClearedSoapAction(soapAction).Length == 0)) + if (GetTrimmedSoapAction(soapAction).Length == 0 || GetTrimmedClearedSoapAction(soapAction).Length == 0) { soapAction = ReadOnlySpan.Empty; } @@ -171,7 +170,9 @@ public static string GetSoapAction(HttpContext httpContext, ref Message message) if (soapAction.IsEmpty) { - XmlDictionaryReader reader = null; +#nullable enable + XmlDictionaryReader? reader = null; +#nullable restore if (!message.IsEmpty) { MessageBuffer mb = message.CreateBufferedCopy(int.MaxValue); @@ -181,6 +182,8 @@ public static string GetSoapAction(HttpContext httpContext, ref Message message) reader = responseMsg.GetReaderAtBodyContents(); soapAction = reader.LocalName.AsSpan(); } + + reader?.Dispose(); } } diff --git a/src/SoapCore/MembersWithAttributeCache.cs b/src/SoapCore/MembersWithAttributeCache.cs index f88cc273..aadaf80d 100644 --- a/src/SoapCore/MembersWithAttributeCache.cs +++ b/src/SoapCore/MembersWithAttributeCache.cs @@ -1,14 +1,17 @@ -using System; +using System; using System.Collections.Concurrent; namespace SoapCore { + /// Extensions to . internal static partial class ReflectionExtensions { private static class MembersWithAttributeCache where TAttribute : Attribute { - public static ConcurrentDictionary[]> CacheEntries = new(); +#pragma warning disable SA1401 // Fields should be private + internal static readonly ConcurrentDictionary[]> CacheEntries = new (); +#pragma warning restore SA1401 } } } diff --git a/src/SoapCore/MessageEncoder/SoapMessageEncoder.cs b/src/SoapCore/MessageEncoder/SoapMessageEncoder.cs index f1652740..3f89268f 100644 --- a/src/SoapCore/MessageEncoder/SoapMessageEncoder.cs +++ b/src/SoapCore/MessageEncoder/SoapMessageEncoder.cs @@ -184,10 +184,6 @@ public virtual async Task WriteMessageAsync(Message message, HttpContext httpCon message.WriteMessage(xmlTextWriter); xmlTextWriter.WriteEndDocument(); xmlTextWriter.Flush(); - //using var xmlWriter = XmlDictionaryWriter.CreateDictionaryWriter(xmlTextWriter); - //message.WriteMessage(xmlWriter); - //xmlWriter.WriteEndDocument(); - //xmlWriter.Flush(); } //Set Content-length in Response @@ -234,9 +230,10 @@ public virtual async Task WriteMessageAsync(Message message, HttpContext httpCon xmlWriter.Flush(); } - if (httpContext != null) // HttpContext is null in unit tests + // Set Content-Length in response. This will disable chunked transfer-encoding. + // Note: HttpContext is null in unit tests + if (httpContext != null) { - // Set Content-Length in response. This will disable chunked transfer-encoding. httpContext.Response.ContentLength = memoryStream.Length; } diff --git a/src/SoapCore/Meta/MetaFromFile.cs b/src/SoapCore/Meta/MetaFromFile.cs index 752d8aa2..9f608481 100644 --- a/src/SoapCore/Meta/MetaFromFile.cs +++ b/src/SoapCore/Meta/MetaFromFile.cs @@ -57,18 +57,6 @@ public Task ReadLocalFileAsync(string path) } #endif - private XmlAttribute EnsureAttribute(XmlDocument xmlDoc, XmlNode node, string attributeName) - { - var attribute = node.Attributes[attributeName]; - if (attribute == null) - { - attribute = xmlDoc.CreateAttribute(attributeName); - node.Attributes.Append(attribute); - } - - return attribute; - } - public string ModifyWSDLAddRightSchemaPath(string xmlString) { var xmlDoc = new XmlDocument() { XmlResolver = null }; @@ -149,6 +137,18 @@ public string ModifyXSDAddRightSchemaPath(string xmlString) return xmlDoc.InnerXml; } + private XmlAttribute EnsureAttribute(XmlDocument xmlDoc, XmlNode node, string attributeName) + { + var attribute = node.Attributes[attributeName]; + if (attribute == null) + { + attribute = xmlDoc.CreateAttribute(attributeName); + node.Attributes.Append(attribute); + } + + return attribute; + } + private string SchemaLocation() { var schemaLocation = ServerUrl + CurrentWebServer + CurrentWebService + "?xsd"; diff --git a/src/SoapCore/Meta/MetaMessage.cs b/src/SoapCore/Meta/MetaMessage.cs index 8b54cd41..4e38156c 100644 --- a/src/SoapCore/Meta/MetaMessage.cs +++ b/src/SoapCore/Meta/MetaMessage.cs @@ -54,7 +54,7 @@ protected override void OnWriteStartEnvelope(XmlDictionaryWriter writer) wroteSoapNamespace = true; } - if(!wroteSoapNamespace) + if (!wroteSoapNamespace) { throw new ArgumentOutOfRangeException(nameof(Version), "Unsupported MessageVersion encountered while writing envelope."); } diff --git a/src/SoapCore/Meta/MetaWCFBodyWriter.cs b/src/SoapCore/Meta/MetaWCFBodyWriter.cs index 5b379618..978017e8 100644 --- a/src/SoapCore/Meta/MetaWCFBodyWriter.cs +++ b/src/SoapCore/Meta/MetaWCFBodyWriter.cs @@ -184,7 +184,10 @@ private void WriteParameters(XmlDictionaryWriter writer, SoapMethodParameterInfo foreach (var parameterInfo in parameterInfos) { if (parameterInfo.Parameter.ParameterType.FullName == "System.Threading.CancellationToken") + { continue; + } + var elementAttribute = parameterInfo.Parameter.GetCustomAttribute(); var parameterName = !string.IsNullOrEmpty(elementAttribute?.ElementName) ? elementAttribute.ElementName @@ -264,7 +267,10 @@ private void AddContractOperations(XmlDictionaryWriter writer, ContractDescripti var type = parameter.Parameter.ParameterType; var typeInfo = type.GetTypeInfo(); if (typeInfo.FullName == "System.Threading.CancellationToken") + { continue; + } + if (typeInfo.IsByRef) { type = typeInfo.GetElementType(); diff --git a/src/SoapCore/ParsedMessage.cs b/src/SoapCore/ParsedMessage.cs index 97b72679..e2403c56 100644 --- a/src/SoapCore/ParsedMessage.cs +++ b/src/SoapCore/ParsedMessage.cs @@ -3,7 +3,6 @@ using System.IO; using System.IO.Pipelines; using System.Linq; -using System.Reflection; using System.ServiceModel.Channels; using System.Text; using System.Threading; @@ -118,21 +117,6 @@ protected override XmlDictionaryReader OnGetReaderAtBodyContents() return InternalGetReaderAtBodyContents(); } - private XmlDictionaryReader InternalGetReaderAtBodyContents() - { - var reader = new XDocumentXmlReader(_body); - - XNamespace soapNs = _version.Envelope.Namespace(); - - reader.ReadToFollowing("Body", soapNs.ToString()); - - while (reader.Read() && reader.NodeType != XmlNodeType.Element && reader.NodeType != XmlNodeType.EndElement) - { - } - - return XmlDictionaryReader.CreateDictionaryReader(reader); - } - protected override void OnClose() { _properties.Dispose(); @@ -188,7 +172,7 @@ private static (XDocument, bool isEmpty) ExtractSoapBody(XDocument envelope, Mes return (new XDocument(), true); } - //return new XDocument(bodyNode.Elements().FirstOrDefault()); + // return new XDocument(bodyNode.Elements().FirstOrDefault()); return (new XDocument(bodyNode), bodyNode.IsEmpty); } @@ -211,6 +195,22 @@ private static MessageProperties ExtractSoapProperties(HttpRequest httpRequest) return properties; } + private XmlDictionaryReader InternalGetReaderAtBodyContents() + { + var reader = new XDocumentXmlReader(_body); + + XNamespace soapNs = _version.Envelope.Namespace(); + + reader.ReadToFollowing("Body", soapNs.ToString()); + + while (reader.Read() && reader.NodeType != XmlNodeType.Element && reader.NodeType != XmlNodeType.EndElement) + { + // Read document until next element. + } + + return XmlDictionaryReader.CreateDictionaryReader(reader); + } + private void ResetMessageConsumed() { var stateField = typeof(Message).GetProperty("State"); diff --git a/src/SoapCore/ReflectionExtensions.cs b/src/SoapCore/ReflectionExtensions.cs index 7271eda1..b9ad866f 100644 --- a/src/SoapCore/ReflectionExtensions.cs +++ b/src/SoapCore/ReflectionExtensions.cs @@ -150,17 +150,6 @@ internal static IEnumerable> GetMembersWithAttri return MembersWithAttributeCache.CacheEntries.GetOrAdd(type, ComputeMembersWithAttribute); } - private static MemberWithAttribute[] ComputeMembersWithAttribute(Type type) - where TAttribute : Attribute - { - var res = from p in GetPropertyOrFieldMembers(type) - let attr = p.GetCustomAttribute() - where attr != null - select new MemberWithAttribute(p, attr); - - return res.ToArray(); - } - internal static bool TryGetBaseTypeWithKnownTypes(this Type type, out Type result) { if (type is null) @@ -196,5 +185,16 @@ internal static bool TryGetBaseTypeWithKnownTypes(this Type type, out Type resul : null; return hasKnownTypes; } + + private static MemberWithAttribute[] ComputeMembersWithAttribute(Type type) + where TAttribute : Attribute + { + var res = from p in GetPropertyOrFieldMembers(type) + let attr = p.GetCustomAttribute() + where attr != null + select new MemberWithAttribute(p, attr); + + return res.ToArray(); + } } } diff --git a/src/SoapCore/ServiceBodyWriter.cs b/src/SoapCore/ServiceBodyWriter.cs index 5b72e78d..7b1543e3 100644 --- a/src/SoapCore/ServiceBodyWriter.cs +++ b/src/SoapCore/ServiceBodyWriter.cs @@ -81,6 +81,7 @@ private static void WriteStream(XmlDictionaryWriter writer, object value) block = ArrayPool.Shared.Rent(blockSize); } } + ArrayPool.Shared.Return(block); } @@ -208,7 +209,8 @@ private void OnWriteXmlSerializerBodyContents(XmlDictionaryWriter writer) xElement.WriteTo(writer); writer.WriteEndElement(); } - //https://github.com/DigDes/SoapCore/issues/385 + + // https://github.com/DigDes/SoapCore/issues/385 else if (_operation.DispatchMethod.GetCustomAttribute()?.Style == OperationFormatStyle.Rpc) { var importer = new SoapReflectionImporter(_serviceNamespace); @@ -220,7 +222,8 @@ private void OnWriteXmlSerializerBodyContents(XmlDictionaryWriter writer) else { var serializer = CachedXmlSerializer.GetXmlSerializer(resultType, xmlName, xmlNs); - //https://github.com/DigDes/SoapCore/issues/719 + + // https://github.com/DigDes/SoapCore/issues/719 serializer.Serialize(writer, _result); } } diff --git a/src/SoapCore/ServiceModel/OperationDescription.cs b/src/SoapCore/ServiceModel/OperationDescription.cs index 925eb30c..f5caff58 100644 --- a/src/SoapCore/ServiceModel/OperationDescription.cs +++ b/src/SoapCore/ServiceModel/OperationDescription.cs @@ -114,8 +114,8 @@ public IEnumerable GetServiceKnownTypesHierarchy() { if (serviceKnownType.Type == null && !string.IsNullOrEmpty(serviceKnownType.MethodName)) { - var method = serviceKnownType.DeclaringType.GetMethod(serviceKnownType.MethodName,BindingFlags.Public | BindingFlags.Static); - var types = (IEnumerable)method.Invoke(null, new object[]{method}); + var method = serviceKnownType.DeclaringType.GetMethod(serviceKnownType.MethodName, BindingFlags.Public | BindingFlags.Static); + var types = (IEnumerable)method.Invoke(null, new object[] { method }); foreach (var t in types) { yield return new ServiceKnownTypeAttribute(t); @@ -131,8 +131,8 @@ public IEnumerable GetServiceKnownTypesHierarchy() { if (serviceKnownType.Type == null && !string.IsNullOrEmpty(serviceKnownType.MethodName)) { - var method = serviceKnownType.DeclaringType.GetMethod(serviceKnownType.MethodName,BindingFlags.Public | BindingFlags.Static); - var types = (IEnumerable)method.Invoke(null, new object[]{method}); + var method = serviceKnownType.DeclaringType.GetMethod(serviceKnownType.MethodName, BindingFlags.Public | BindingFlags.Static); + var types = (IEnumerable)method.Invoke(null, new object[] { method }); foreach (var t in types) { yield return new ServiceKnownTypeAttribute(t); @@ -151,7 +151,6 @@ public IEnumerable GetServiceKnownTypesHierarchy() } } - private static SoapMethodParameterInfo CreateParameterInfo(ParameterInfo info, int index, ContractDescription contract) { var elementAttribute = info.GetCustomAttribute(); diff --git a/src/SoapCore/ServiceModel/ServiceDescription.cs b/src/SoapCore/ServiceModel/ServiceDescription.cs index 91309da3..7e5f6b75 100644 --- a/src/SoapCore/ServiceModel/ServiceDescription.cs +++ b/src/SoapCore/ServiceModel/ServiceDescription.cs @@ -12,8 +12,8 @@ public class ServiceDescription public ServiceDescription(Type serviceType, bool generateSoapActionWithoutContractName) { ServiceType = serviceType; - //ServiceKnownTypes = serviceType.GetCustomAttributes(inherit: false); + //ServiceKnownTypes = serviceType.GetCustomAttributes(inherit: false); ServiceKnownTypes = serviceType.Assembly.GetTypes() .Where(t => t.Namespace == serviceType.Namespace) .SelectMany(type => type.GetCustomAttributes(inherit: false)) diff --git a/src/SoapCore/SoapCore.csproj b/src/SoapCore/SoapCore.csproj index 9efe76f7..646d12d7 100644 --- a/src/SoapCore/SoapCore.csproj +++ b/src/SoapCore/SoapCore.csproj @@ -1,10 +1,10 @@ - + SOAP protocol middleware for ASP.NET Core 1.2.1.12 Digital Design - netstandard2.0;netstandard2.1;net8.0;netcoreapp3.1; + netstandard2.0;netstandard2.1;net8.0;net10.0; SoapCore SOAP;ASP.NET Core https://github.com/DigDes/SoapCore @@ -33,17 +33,20 @@ - - - - - - + + + + + + + + + @@ -54,6 +57,7 @@ + all runtime; build; native; contentfiles; analyzers diff --git a/src/SoapCore/SoapCoreOptions.cs b/src/SoapCore/SoapCoreOptions.cs index 388f8106..c1818aa6 100644 --- a/src/SoapCore/SoapCoreOptions.cs +++ b/src/SoapCore/SoapCoreOptions.cs @@ -22,6 +22,7 @@ public class SoapCoreOptions /// public string Path { get; set; } #endif + /// /// Gets or sets encoders /// @@ -117,11 +118,6 @@ public class SoapCoreOptions public WsdlFileOptions WsdlFileOptions { get; set; } - /// - /// Get or sets a value indicating the use of custom serializer, use for if multiple custom serializer used to services - /// - internal Type SerializerIdentifier { get; set; } - /// /// Sets additional namespace declaration attributes in envelope /// @@ -148,6 +144,11 @@ public class SoapCoreOptions /// public string SchemeOverride { get; set; } + /// + /// Get or sets a value indicating the use of custom serializer, use for if multiple custom serializer used to services + /// + internal Type SerializerIdentifier { get; set; } + public void UseCustomSerializer() where TCustomSerializer : class, IXmlSerializationHandler { diff --git a/src/SoapCore/SoapEndpointMiddleware.cs b/src/SoapCore/SoapEndpointMiddleware.cs index 69f98e7b..d4c0750c 100644 --- a/src/SoapCore/SoapEndpointMiddleware.cs +++ b/src/SoapCore/SoapEndpointMiddleware.cs @@ -38,6 +38,8 @@ namespace SoapCore public class SoapEndpointMiddleware where T_MESSAGE : CustomMessage, new() { + private static IOperationInvoker _operationInvoker; + private readonly ILogger> _logger; private readonly RequestDelegate _next; private readonly SoapOptions _options; @@ -45,8 +47,6 @@ public class SoapEndpointMiddleware private readonly StringComparison _pathComparisonStrategy; private readonly SoapMessageEncoder[] _messageEncoders; private readonly IXmlSerializationHandler _serializerHandler; - private static IOperationInvoker _operationInvoker; - private readonly ConcurrentDictionary _xmlNamespaceLookupsByMessageEncoder = new ConcurrentDictionary(); public SoapEndpointMiddleware(ILogger> logger, RequestDelegate next, SoapOptions options, IServiceProvider serviceProvider) { diff --git a/src/SoapCore/WSDLFileOptions.cs b/src/SoapCore/WSDLFileOptions.cs index 5865f4f5..05d36c63 100644 --- a/src/SoapCore/WSDLFileOptions.cs +++ b/src/SoapCore/WSDLFileOptions.cs @@ -1,21 +1,16 @@ -using System; -using System.Collections.Generic; -using Microsoft.AspNetCore.Http; - -namespace SoapCore -{ - public class WsdlFileOptions - { - public virtual Dictionary WebServiceWSDLMapping { get; set; } = new Dictionary(); - public string UrlOverride { get; set; } - public string SchemeOverride { get; set; } - public string VirtualPath { get; set; } - public string AppPath { get; set; } - public Func UrlOverrideFunc { get; set; } - } - - public class WsdlFileOptionsCaseInsensitive : WsdlFileOptions - { - public override Dictionary WebServiceWSDLMapping { get; set; } = new Dictionary(StringComparer.OrdinalIgnoreCase); - } +using System; +using System.Collections.Generic; +using Microsoft.AspNetCore.Http; + +namespace SoapCore +{ + public class WsdlFileOptions + { + public virtual Dictionary WebServiceWSDLMapping { get; set; } = new Dictionary(); + public string UrlOverride { get; set; } + public string SchemeOverride { get; set; } + public string VirtualPath { get; set; } + public string AppPath { get; set; } + public Func UrlOverrideFunc { get; set; } + } } diff --git a/src/SoapCore/WsdlFileOptionsCaseInsensitive.cs b/src/SoapCore/WsdlFileOptionsCaseInsensitive.cs new file mode 100644 index 00000000..76c22697 --- /dev/null +++ b/src/SoapCore/WsdlFileOptionsCaseInsensitive.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; + +namespace SoapCore +{ + public class WsdlFileOptionsCaseInsensitive : WsdlFileOptions + { + public override Dictionary WebServiceWSDLMapping { get; set; } = new Dictionary(StringComparer.OrdinalIgnoreCase); + } +}