Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for circular references in datacontract objects #303

Merged
merged 4 commits into from
Aug 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/SoapCore.Tests/Wsdl/Services/CircularReferenceFirstObject.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System.Runtime.Serialization;

namespace SoapCore.Tests.Wsdl.Services
{
[DataContract]
public class CircularReferenceFirstObject
{
[DataMember]
public CircularReferenceSecondObject SecondObject { get; set; }
}
}
11 changes: 11 additions & 0 deletions src/SoapCore.Tests/Wsdl/Services/CircularReferenceSecondObject.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System.Runtime.Serialization;

namespace SoapCore.Tests.Wsdl.Services
{
[DataContract]
public class CircularReferenceSecondObject
{
[DataMember]
public CircularReferenceFirstObject FirstObject { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;
using System.ServiceModel;

namespace SoapCore.Tests.Wsdl.Services
{
[ServiceContract]
public interface IDataContractCircularReferenceService
{
[OperationContract]
CircularReferenceFirstObject GetFirstObject();
}

public class DataContractCircularReferenceService : IDataContractCircularReferenceService
{
public CircularReferenceFirstObject GetFirstObject()
{
throw new NotImplementedException();
}
}
}
111 changes: 61 additions & 50 deletions src/SoapCore.Tests/Wsdl/WsdlTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,59 +12,70 @@

namespace SoapCore.Tests.Wsdl
{
[TestClass]
public class WsdlTests
{
private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
[TestClass]
public class WsdlTests
{
private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();

[TestMethod]
public void CheckTaskReturnMethod()
{
string serviceUrl = "http://localhost:5053";
StartService(typeof(TaskNoReturnService), serviceUrl);
var wsdl = GetWsdl(serviceUrl);
Trace.TraceInformation(wsdl);
Assert.IsNotNull(wsdl);
StopServer();
}
[TestMethod]
public void CheckTaskReturnMethod()
{
string serviceUrl = "http://localhost:5053";
StartService(typeof(TaskNoReturnService), serviceUrl);
var wsdl = GetWsdl(serviceUrl);
Trace.TraceInformation(wsdl);
Assert.IsNotNull(wsdl);
StopServer();
}

[TestMethod]
public void CheckDataContractContainsItself()
{
string serviceUrl = "http://localhost:5054";
StartService(typeof(DataContractContainsItselfService), serviceUrl);
var wsdl = GetWsdl(serviceUrl);
Trace.TraceInformation(wsdl);
Assert.IsNotNull(wsdl);
StopServer();
}
[TestMethod]
public void CheckDataContractContainsItself()
{
string serviceUrl = "http://localhost:5054";
StartService(typeof(DataContractContainsItselfService), serviceUrl);
var wsdl = GetWsdl(serviceUrl);
Trace.TraceInformation(wsdl);
Assert.IsNotNull(wsdl);
StopServer();
}

[TestCleanup]
public void StopServer()
{
_cancellationTokenSource.Cancel();
}
[TestMethod]
public void CheckDataContractCircularReference()
{
string serviceUrl = "http://localhost:5055";
StartService(typeof(DataContractCircularReferenceService), serviceUrl);
var wsdl = GetWsdl(serviceUrl);
Trace.TraceInformation(wsdl);
Assert.IsNotNull(wsdl);
StopServer();
}

private string GetWsdl(string serviceUrl, string serviceName = "Service.svc")
{
using (var httpClient = new HttpClient())
{
return httpClient.GetStringAsync(string.Format("{0}/{1}?wsdl", serviceUrl, serviceName)).Result;
}
}
[TestCleanup]
public void StopServer()
{
_cancellationTokenSource.Cancel();
}

private void StartService(Type serviceType, string serviceUrl)
{
Task.Run(async () =>
{
var host = new WebHostBuilder()
.UseKestrel()
.UseUrls(serviceUrl)
.ConfigureServices(services => services.AddSingleton<IStartupConfiguration>(new StartupConfiguration(serviceType)))
.UseStartup<Startup>()
.Build();
await host.RunAsync(_cancellationTokenSource.Token);
}).Wait(1000);
}
}
private string GetWsdl(string serviceUrl, string serviceName = "Service.svc")
{
using (var httpClient = new HttpClient())
{
return httpClient.GetStringAsync(string.Format("{0}/{1}?wsdl", serviceUrl, serviceName)).Result;
}
}

private void StartService(Type serviceType, string serviceUrl)
{
Task.Run(async () =>
{
var host = new WebHostBuilder()
.UseKestrel()
.UseUrls(serviceUrl)
.ConfigureServices(services => services.AddSingleton<IStartupConfiguration>(new StartupConfiguration(serviceType)))
.UseStartup<Startup>()
.Build();
await host.RunAsync(_cancellationTokenSource.Token);
}).Wait(1000);
}
}
}
13 changes: 10 additions & 3 deletions src/SoapCore/MetaWCFBodyWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ public class MetaWCFBodyWriter : BodyWriter
private readonly Binding _binding;

private readonly Dictionary<Type, string> _complexTypeToBuild = new Dictionary<Type, string>();
private readonly HashSet<Type> _complexTypeProcessed = new HashSet<Type>(); // Contains types that have been discovered
private readonly Queue<Type> _arrayToBuild;

private readonly HashSet<string> _builtEnumTypes;
Expand Down Expand Up @@ -538,6 +539,7 @@ private void AddComplexTypes(XmlDictionaryWriter writer)
{
foreach (var type in _complexTypeToBuild.ToArray())
{
_complexTypeToBuild[type.Key] = GetDataContractNamespace(type.Key);
DiscoveryTypesByProperties(type.Key, true);
}

Expand Down Expand Up @@ -592,7 +594,9 @@ private void AddComplexTypes(XmlDictionaryWriter writer)
private void DiscoveryTypesByProperties(Type type, bool isRootType)
{
//guard against infinity recursion
if (!isRootType && _complexTypeToBuild.ContainsKey(type))
//check is made against _complexTypeProcessed, which contains types that have been
//discovered by the current method
if (_complexTypeProcessed.Contains(type))
{
return;
}
Expand All @@ -602,10 +606,13 @@ private void DiscoveryTypesByProperties(Type type, bool isRootType)
return;
}

//type will be processed, so can be added to _complexTypeProcessed
_complexTypeProcessed.Add(type);

if (HasBaseType(type) && type.BaseType != null)
{
DiscoveryTypesByProperties(type.BaseType, false);
_complexTypeToBuild[type.BaseType] = GetDataContractNamespace(type.BaseType);
DiscoveryTypesByProperties(type.BaseType, false);
}

foreach (var property in type.GetProperties().Where(prop =>
Expand Down Expand Up @@ -641,8 +648,8 @@ private void DiscoveryTypesByProperties(Type type, bool isRootType)
continue;
}

DiscoveryTypesByProperties(propertyType, false);
_complexTypeToBuild[propertyType] = GetDataContractNamespace(propertyType);
DiscoveryTypesByProperties(propertyType, false);
}
}
}
Expand Down