Unable to deserialize PSObject in response data contract. Running out of leads - c#

Background:
The company I work at has a product exposed to the network via WCF, specifically, through a service contract containing a single method that handles all messages. Internally, the product parses the SOAP header and body, loads a service plug-in, and routes the request to that plug-in.
I have written one of these plug-ins to host the PowerShell runtime, and provided a custom host that hooks into some of the internal plumbing of this product. A remote client could send an arbitrary script to the server to execute, including taking advantage of functionality of the product itself.
To be clear: I am well aware of the security implications. This plug-in is only for diagnostic and prototyping purposes, not for production use or deployment.
Problem:
In the data contract sent back as a response, I am passing an array of PSObject instances:
[DataContractAttribute]
public partial class RunScriptResponse
{
[DataMember]
public PSObject[] Output { get; set; }
}
According to this article, PSObject should be supported; it implements ISerializable and boasts SerializableAttribute.
The response data contract is getting serialized just peachy by the server, and is being received without issue by the client--as verified by WCF extensibility points. However, when the service client tries to deserialize the objects, I hit this [1]:
Member 'CliXml' was not found.
Yet, looking at the response XML, it's pretty darn clear, that member is there:
<RunScriptResponse xmlns="...">
<RunScriptResult xmlns:d1p1="..." xmlns:e="..." xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<e:Output xmlns:d1p1="http://schemas.datacontract.org/2004/07/System.Management.Automation">
<d1p1:PSObject>
<CliXml i:type="d3p1:string" xmlns:d3p1="http://www.w3.org/2001/XMLSchema">
<Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04">
It suggests that something is happening during deserialization where the PSObject isn't able to find the <CliXml> element, but I can't figure out why.
Also, the particular client that is trying to access this server is itself a PowerShell module, dynamically generated from proxy assemblies via reflection. The intent is that you can pass a script to the server, get some PSObject instances back, and do further processing on the client.
Given its auto-generated nature, it's not possible to switch to other serialization scenarios, such as serializing the PSObject instances on the server, passing an array of strings, and then deserializing them on the client. That would require unique-to-one-method handling, which would significantly complicate the module generation process.
Does anyone have any suggestions on how to further troubleshoot this, or other strategies?
Thank you!
[1] Stack trace for exception:
at System.Runtime.Serialization.SerializationInfo.GetElement(String name, Type& foundType)
at System.Runtime.Serialization.SerializationInfo.GetValue(String name, Type type)
at System.Management.Automation.PSObject..ctor(SerializationInfo info, StreamingContext context)
at ReadPSObjectFromXml(XmlReaderDelegator , XmlObjectSerializerReadContext , XmlDictionaryString[] , XmlDictionaryString[] )
at System.Runtime.Serialization.ClassDataContract.ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context)
at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator reader, String name, String ns, Type declaredType, DataContract& dataContract)
at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator xmlReader, Int32 id, RuntimeTypeHandle declaredTypeHandle, String name, String ns)
at ReadArrayOfPSObjectFromXml(XmlReaderDelegator , XmlObjectSerializerReadContext , XmlDictionaryString , XmlDictionaryString , CollectionDataContract )
at System.Runtime.Serialization.CollectionDataContract.ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context)
at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator reader, String name, String ns, Type declaredType, DataContract& dataContract)
at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator xmlReader, Int32 id, RuntimeTypeHandle declaredTypeHandle, String name, String ns)
at ReadRunScriptResponseFromXml(XmlReaderDelegator , XmlObjectSerializerReadContext , XmlDictionaryString[] , XmlDictionaryString[] )
at System.Runtime.Serialization.ClassDataContract.ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context)
at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator reader, String name, String ns, Type declaredType, DataContract& dataContract)
at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, DataContract dataContract, String name, String ns)
at System.Runtime.Serialization.DataContractSerializer.InternalReadObject(XmlReaderDelegator xmlReader, Boolean verifyObjectName, DataContractResolver dataContractResolver)
at System.Runtime.Serialization.XmlObjectSerializer.ReadObjectHandleExceptions(XmlReaderDelegator reader, Boolean verifyObjectName, DataContractResolver dataContractResolver)
at System.Runtime.Serialization.DataContractSerializer.ReadObject(XmlDictionaryReader reader, Boolean verifyObjectName)
at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.PartInfo.ReadObject(XmlDictionaryReader reader, XmlObjectSerializer serializer)
at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.DeserializeParameterPart(XmlDictionaryReader reader, PartInfo part, Boolean isRequest)
20180129: The code that I'm using on the client side is plain vanilla WCF. I exported the WSDL and XSDs from the service interface assembly via svcutil.exe, ran those back through svcutil.exe to generate a C# proxy. From there, everything is just calling the proxy as-is:
// Utility code that wraps the programmatic generation of a proxy.
// Manually instantiates `WSHttpBinding` and `EndpointAddress`, then
// constructs an instance of the proxy client using those two objects.
//
var client = GenerateProxyClient<RunScriptRequest,
IScriptingService,
ScriptingServiceClient,
RunScriptResponse>();
var response = client.RunScript(new RunScriptRequest
{
Script = #"Get-ChildItem C:\"
});

Related

Returning a dynamic object is throwing runtime error

I am developing a Web API, where the GET method needs to return an object, whose variables will be decided based on an XML file. The returned format must be either XML or JSON as requested by the client. I want to return the data inside XML file into XML format to the client, and something reasonable for JSON when JSON is requested.
The nodes in the XML might increase or decrease and therefore I cannot define a fixed class in the Models. My current solution is to return a dynamic object, but I am getting an exception shown below. What can I do to avoid the exception?
GET Api
[AllowAnonymous]
public class DataController : ApiController
{
//GET api/----based on dynamic binding
public object Get()
{
//Read XML
XDocument xDoc = XDocument.Load(#"D:\data.xml");
string jsonStr = JsonConvert.SerializeXNode(xDoc);
dynamic dynamicObject = JsonConvert.DeserializeObject<ExpandoObject>(jsonStr);
return dynamicObject; //THIS LINE IS THROWING RUNTIME ERROR
}
}
Sample XML File:
<Data>
<Name>abcd</Name>
<bad>100</bad>
<status>running</status>
</Data>
When I try to access the GET api, the following error appears on the web page:
<Error>
<Message>An error has occurred.</Message>
<ExceptionMessage>
The 'ObjectContent`1' type failed to serialize the response body for content type 'application/xml; charset=utf-8'.
</ExceptionMessage>
<ExceptionType>System.InvalidOperationException</ExceptionType>
<StackTrace/>
<InnerException>
<Message>An error has occurred.</Message>
<ExceptionMessage>
Type 'System.Dynamic.ExpandoObject' with data contract name 'ArrayOfKeyValueOfstringanyType:http://schemas.microsoft.com/2003/10/Serialization/Arrays' is not expected. Consider using a DataContractResolver if you are using DataContractSerializer or add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to the serializer.
</ExceptionMessage>
<ExceptionType>
System.Runtime.Serialization.SerializationException
</ExceptionType>
<StackTrace>
at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeAndVerifyType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, Boolean verifyKnownType, RuntimeTypeHandle declaredTypeHandle, Type declaredType) at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithXsiTypeAtTopLevel(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle originalDeclaredTypeHandle, Type graphType) at System.Runtime.Serialization.DataContractSerializer.InternalWriteObjectContent(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver) at System.Runtime.Serialization.DataContractSerializer.InternalWriteObject(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver) at System.Runtime.Serialization.XmlObjectSerializer.WriteObjectHandleExceptions(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver) at System.Runtime.Serialization.DataContractSerializer.WriteObject(XmlWriter writer, Object graph) at System.Net.Http.Formatting.XmlMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, HttpContent content) at System.Net.Http.Formatting.XmlMediaTypeFormatter.WriteToStreamAsync(Type type, Object value, Stream writeStream, HttpContent content, TransportContext transportContext, CancellationToken cancellationToken) --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.WebHost.HttpControllerHandler.<WriteBufferedResponseContentAsync>d__1b.MoveNext()
</StackTrace>
</InnerException>
</Error>
The reason you are getting that error is that you have declared your method to return an object of type object -- but have in fact returned a polymorphic subtype, namely ExpandoObject. Since DataContractSerializer (and XmlSerializer) will refuse to serialize unexpected polymorphic types, they throw the exception you are seeing. For details see
Understanding Known Types.
Data Contract Known Types.
Known Types.
That being said, I'd like to suggest a different, simpler approach. First, define your Get() method to explicitly return an XElement like so:
public XElement Get()
{
//Read XML
XDocument xDoc = XDocument.Load(#"D:\data.xml");
return xDoc.Root;
}
DataContractSerializer (and XmlSerializer) are both able to serialize an object of this type (since it implements IXmlSerializable), so your method will now successfully return contents of the file "D:\data.xml" verbatim when XML is requested.
Now, what to do when JSON is requested? As it turns out, Json.NET has a built-in converter XmlNodeConverter that can serialize an XElement to and from JSON. It's used internally by JsonConvert.SerializeXNode() but is public and so can be used directly. Thus if you add the converter to your global Web API list of converters in JsonSerializerSettings.Converters, your method should now return something reasonable for JSON as well.
You don't specify which version of Web API you are using. To add a converter globally, see
ASP.NET Web API 2: See How to set custom JsonSerializerSettings for Json.NET in MVC 4 Web API? and also the second part of this answer to Registering a custom JsonConverter globally in Json.Net. In this scenario your code would look something like:
protected void Application_Start()
{
var config = GlobalConfiguration.Configuration;
var settings = config.Formatters.JsonFormatter.SerializerSettings;
settings.Converters.Add(new XmlNodeConverter());
}
ASP.NET Core MVC: see JsonSerializerSettings and Asp.Net Core or Setting JsonConvert.DefaultSettings asp net core 2.0 not working as expected. Here again you would access the global JsonSerializerSettings and add an XmlNodeConverter as shown above.

Exception on GET call on a table using Entity Framework in WebAPI [duplicate]

This question already has answers here:
Failed to serialize the response in Web API with Json
(27 answers)
Closed 5 years ago.
I'm trying to create a WebApi to read a data from the DB using Entity Framework.
My data model design looks like this:
I have added a WebApi controller named SportsController like this:
public class SportsController : ApiController
{
public HttpResponseMessage Get()
{
try
{
using (MyLeagueDBEntities dbEntity = new MyLeagueDBEntities())
{
return Request.CreateResponse(HttpStatusCode.OK, dbEntity.tbl_Sports.ToList());
}
}
catch (Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ex);
}
}
}
On invoking a request from browser to my WebApi controller GET method, I'm getting the exception shown below. But in debug mode, I can see that it is able to get the data from database without any error but once the control moves out of the method, it does throw the below exception.
Exception details:
"Message":"An error has occurred.",
"ExceptionMessage":"The 'ObjectContent`1' type failed to serialize the response body for content type 'application/json; charset=utf-8'.",
"ExceptionType":"System.InvalidOperationException",
"StackTrace":null,
"InnerException":{"Message":"An error has occurred.",
"ExceptionMessage":"Error getting value from 'tbl_Tournament' on 'System.Data.Entity.DynamicProxies.tbl_Sports_7461105FEAC4FCCBDED534FCE44707AF9C7DFE1E4AD69B1E7E3777FBB953F184'.",
"ExceptionType":"Newtonsoft.Json.JsonSerializationException",
"StackTrace":"
at Newtonsoft.Json.Serialization.DynamicValueProvider.GetValue(Object target)\r\n
at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.CalculatePropertyValues(JsonWriter writer, Object value, JsonContainerContract contract, JsonProperty member, JsonProperty property, JsonContract& memberContract, Object& memberValue)\r\n
at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter writer, Object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)\r\n > at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty)\r\n
at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeList(JsonWriter writer, IEnumerable values, JsonArrayContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)\r\n
at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty)\r\n
at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.Serialize(JsonWriter jsonWriter, Object value, Type objectType)\r\n
at Newtonsoft.Json.JsonSerializer.SerializeInternal(JsonWriter jsonWriter, Object value, Type objectType)\r\n
at System.Net.Http.Formatting.BaseJsonMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, Encoding effectiveEncoding)\r\n
at System.Net.Http.Formatting.JsonMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, Encoding effectiveEncoding)\r\n
at System.Net.Http.Formatting.BaseJsonMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, HttpContent content)\r\n
at System.Net.Http.Formatting.BaseJsonMediaTypeFormatter.WriteToStreamAsync(Type type, Object value, Stream writeStream, HttpContent content, TransportContext transportContext, CancellationToken cancellationToken)\r\n
End of stack trace from previous location where exception was thrown
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n
at System.Web.Http.WebHost.HttpControllerHandler.d__1b.MoveNext()",
"InnerException":{"Message":"An error has occurred.",
"ExceptionMessage":"The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.",
"ExceptionType":"System.ObjectDisposedException",
"StackTrace":"
at System.Data.Entity.Core.Objects.ObjectContext.get_Connection()\r\n
at System.Data.Entity.Core.Objects.ObjectQuery1.GetResults(Nullable1 forMergeOption)\r\n
at System.Data.Entity.Core.Objects.ObjectQuery1.Execute(MergeOption
mergeOption)\r\n
at System.Data.Entity.Core.Objects.DataClasses.EntityCollection1.Load(List1
collection, MergeOption mergeOption)\r\n
at System.Data.Entity.Core.Objects.DataClasses.EntityCollection1.Load(MergeOption
mergeOption)\r\n
at System.Data.Entity.Core.Objects.DataClasses.RelatedEnd.DeferredLoad()\r\n
at System.Data.Entity.Core.Objects.Internal.LazyLoadBehavior.LoadProperty[TItem](TItem propertyValue, String relationshipName, String targetRoleName, Boolean mustBeNull, Object wrapperObject)\r\n
at System.Data.Entity.Core.Objects.Internal.LazyLoadBehavior.<>c__DisplayClass7`2.b__1(TProxy proxy, TItem item)\r\n
at System.Data.Entity.DynamicProxies.tbl_Sports_7461105FEAC4FCCBDED534FCE44707AF9C7DFE1E4AD69B1E7E3777FBB953F184.get_tbl_Tournament()\r\n
at Gettbl_Tournament(Object )\r\n
at Newtonsoft.Json.Serialization.DynamicValueProvider.GetValue(Object
target)"}}}
I'm new to Entity Framework and WebApi, requesting one to help me out resolving this issue.
PFB the snapshot with the error message logged on fiddler. Currently getting Http 500 internal server error,
It is because you have lazy loading enabled and you are serializing the object graph after you dispose of the DbContext instance. So what is happening
You create a DbContext
You retrieve the data from the DbContext for that one type
You pass the data to the Result method
Your DbContext is closed and disposed
Your method exists
Asp.Net mvc pipeline now tries to serialize the result's content
Relationships in your type are now attempted to be retrieved in the serialization process
The relationships are attempted to be retrieved from the DbContext because you have Lazy Loading enabled
The DbContext is disposed so access is not possible, an Exception is thrown
To fix it do one of the following
Move the DbContext to the class scope and dispose of it in the Disposing method (override the existing method from the ApiController)
(Recommended!) Turn off loazy loading and specify which properties you want to retrieve from the returned type using Include. This will enable more fine grained control over what is retrieved and is more performant because everything is retrieved in a single DB call instead of on access.

Serialize List with EKEvents to a JSON in Xamarin.iOS

When I try to serialize a simple List that stores EKEvent. I get this error:
Newtonsoft.Json.JsonSerializationException: Self referencing loop
detected for property 'Self' with type 'Foundation.NSDate'. Path
'[0].EndDate'.
at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.CheckForCircularReference
(Newtonsoft.Json.JsonWriter writer, System.Object value,
Newtonsoft.Json.Serialization.JsonProperty property,
Newtonsoft.Json.Serialization.JsonContract contract,
Newtonsoft.Json.Serialization.JsonContainerContract containerContract,
Newtonsoft.Json.Serialization.JsonProperty containerProperty)
[0x00105] in :0
at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter. CalculatePropertyValues (Newtonsoft.Json.JsonWriter writer,
System.Object value,
Newtonsoft.Json.Serialization.JsonContainerContract contract,
Newtonsoft.Json.Serialization.JsonProperty member,
Newtonsoft.Json.Serialization.JsonProperty property,
Newtonsoft.Json.Serialization.JsonContract& memberContract,
System.Object& memberValue) [0x000c7] in :0
at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject
(Newtonsoft.Json.JsonWriter writer, System.Object value,
Newtonsoft.Json.Serialization.JsonObjectContract contract,
Newtonsoft.Json.Serialization.JsonProperty member,
Newtonsoft.Json.Serialization.JsonContainerContract
collectionContract, Newtonsoft.Json.Serialization.JsonProperty
containerProperty) [0x0003c] in :0
This occurs when I try to use JsonConvert.SerializeObject method.
Any workaround for serializing an EKEvent list?
EDIT
Using JsonSerializerSettings works!
However, when I try to Deserialise the object, I get another problem:
Newtonsoft.Json.JsonSerializationException: Unable to find a constructor to use for type EventKit.EKEvent. #
A class should either have a default constructor,
one constructor with arguments or a constructor marked with the JsonConstructor attribute.
Path '[0].ClassHandle', line 1, position 16.
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateNewObject
(Newtonsoft.Json.JsonReader reader, Newtonsoft.Json.Serialization.JsonObjectContract objectContract,
Newtonsoft.Json.Serialization.JsonProperty containerMember,
Newtonsoft.Json.Serialization.JsonProperty containerProperty,
System.String id, System.Boolean& createdFromNonDefaultCreator) [0x000d6] in :0
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.
CreateObject (Newtonsoft.Json.JsonReader reader, System.Type objectType, Newtonsoft.Json.Serialization.JsonContract contract, Newtonsoft.Json.Serialization.JsonProperty member, Newtonsoft.Json.Serialization.JsonContainerContract containerContract, Newtonsoft.Json.Serialization.JsonProperty containerMember, System.Object existingValue) [0x0013b] in :0
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.
CreateValueInternal (Newtonsoft.Json.JsonReader reader, System.Type objectType
, Newtonsoft.Json.Serialization.JsonContract contract, Newtonsoft.Json.Serialization.JsonProperty member,
Newtonsoft.Json.Serialization.JsonContainerContract containerContract, Newtonsoft.Json.Serialization.JsonProperty containerMember, System.Object existingValue) [0x0006d] in :0
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateList (IList list,
Newtonsoft.Json.JsonReader reader, Newtonsoft.Json.Serialization.JsonArrayContract contract,
Newtonsoft.Json.Serialization.JsonProperty containerProperty, System.String id) [0x000cb] in :0
Giorgi's answer answers the first part of your question on how to serialize your data, which you seem to have resolved. However, now you have an issue deserializing it again.
Looking at your stack trace that you have supplied in the opening post it says:
Unable to find a constructor to use for type EventKit.EKEvent. # A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor attribute
That says a lot about this error in particular. Now, looking at the EventKit EKEvent API reference, we find the following:
All the constructors are marked protected
There are no constructors that take no arguments
This means, when the DeserializeObject method tries to create an instance of EKEvent, it does not have any means to do so. It does not know what arguments to provide.
There are also other problems with the EKEvent class. Even if it had public constructors with no arguments. All the properties it has are marked read-only. This means when deserializing the serializer again, has no means of populating the properties with the serialized events.
So instead you have these two options:
Create a class wrapping EKEvent or imitating EKEvent and deserialize to that
Use the provided EKEventStore to persist EKEvents. As the API docs describe: "The EventStore is required to perform any operations in EventKit. It can be thought of as the persistent storage, or database, engine for all EventKit data."
The opening post does not describe the reasoning as to why you want to serialize and deserialize this object. However, if you want to communicate it to some API, I would suggest that you go for option 1. as it will not be platform specific and you can make it so that you only communicate relevant data between the App and API saving precious bandwidth.
You are getting the error because EKEvent has recursive references for it's properties. You can use ReferenceLoopHandling setting to specify that the json serializer should ignore self referencing loops:
JsonConvert.SerializeObject(myList, new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
});
You can also set the serializer settings globally so that you don't have to specify it for every call:
JsonConvert.DefaultSettings = () => new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};
You can see full example at Json.NET documentation: http://www.newtonsoft.com/json/help/html/ReferenceLoopHandlingIgnore.htm

Task`1 cannot be serialized for Web Application referencing BCL.Async

We recently deployed a newly developed pre-compiled service to our test domain and received the following error:
Type 'System.Threading.Tasks.Task`1[Domain.Infrastructure.Contracts.Configuration.DomainServices]' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. If the type is a collection, consider marking it with the CollectionDataContractAttribute. See the Microsoft .NET Framework documentation for other supported types.
The server is Windows 2008R2 running .NET 4.0.
There has been a few Stack Overflow questions about this, but most seem to be referring to the CTP release of Async. Apparently you MUST have .NET 4.5 installed on the server in order to use this code.
Has this situation changed at all with the release of the BCL.Async NuGet package?
I was under the impression that code that was compiled with an Async compiler and includes the BCL libraries from NuGet had everything they needed to run in a .NET 4 environment.
Is it still the case that we have to upgrade the .NET runtime on the server to 4.5?
Edit: Stack Trace Provided:
[InvalidDataContractException: Type 'System.Threading.Tasks.Task`1[Domain.Infrastructure.Contracts.Configuration.DomainServices]' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. If the type is a collection, consider marking it with the CollectionDataContractAttribute. See the Microsoft .NET Framework documentation for other supported types.]
System.Runtime.Serialization.DataContractCriticalHelper.ThrowInvalidDataContractException(String message, Type type) +1184850
System.Runtime.Serialization.DataContractCriticalHelper.CreateDataContract(Int32 id, RuntimeTypeHandle typeHandle, Type type) +787
System.Runtime.Serialization.DataContractCriticalHelper.GetDataContractSkipValidation(Int32 id, RuntimeTypeHandle typeHandle, Type type) +117
System.Runtime.Serialization.XsdDataContractExporter.GetSchemaTypeName(Type type) +85
System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.CreatePartInfo(MessagePartDescription part, OperationFormatStyle style, DataContractSerializerOperationBehavior serializerFactory) +48
System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.CreateMessageInfo(DataContractFormatAttribute dataContractFormatAttribute, MessageDescription messageDescription, DataContractSerializerOperationBehavior serializerFactory) +708
System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter..ctor(OperationDescription description, DataContractFormatAttribute dataContractFormatAttribute, DataContractSerializerOperationBehavior serializerFactory) +570
System.ServiceModel.Description.DataContractSerializerOperationBehavior.GetFormatter(OperationDescription operation, Boolean& formatRequest, Boolean& formatReply, Boolean isProxy) +308
System.ServiceModel.Description.DataContractSerializerOperationBehavior.System.ServiceModel.Description.IOperationBehavior.ApplyDispatchBehavior(OperationDescription description, DispatchOperation dispatch) +69
System.ServiceModel.Description.DispatcherBuilder.BindOperations(ContractDescription contract, ClientRuntime proxy, DispatchRuntime dispatch) +120
System.ServiceModel.Description.DispatcherBuilder.InitializeServiceHost(ServiceDescription description, ServiceHostBase serviceHost) +4250
System.ServiceModel.ServiceHostBase.InitializeRuntime() +82
System.ServiceModel.ServiceHostBase.OnOpen(TimeSpan timeout) +64
System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout) +789
System.ServiceModel.HostingManager.ActivateService(String normalizedVirtualPath) +255
System.ServiceModel.HostingManager.EnsureServiceAvailable(String normalizedVirtualPath) +1172
[ServiceActivationException: The service '/Services/Binary/Endpoint.svc' cannot be activated due to an exception during compilation. The exception message is: Type 'System.Threading.Tasks.Task`1[Domain.Infrastructure.Contracts.Configuration.DomainServices]' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. If the type is a collection, consider marking it with the CollectionDataContractAttribute. See the Microsoft .NET Framework documentation for other supported types..]
System.Runtime.AsyncResult.End(IAsyncResult result) +901504
System.ServiceModel.Activation.HostedHttpRequestAsyncResult.End(IAsyncResult result) +178638
System.Web.AsyncEventExecutionStep.OnAsyncEventCompletion(IAsyncResult ar) +107
Okay, I'm going to take a good guess here that you're trying to expose an asynchronous WCF operation that returns a Task<Domain.Infrastructure.Contracts.Configuration.DomainServices>. While Microsoft.Bcl.Async will allow you to compile code that uses tasks, it won't provide the .NET Framework 4.5 changes to WCF that allow you to use tasks in services.
That being said, you can still use the asynchronous programming model to expose the asynchronous method to WCF, while still writing the code using the TPL. To do so, you'll have to wrap the method with APM begin/end methods. Something like this:
[ServiceContractAttribute]
public interface ISampleService
{
[OperationContractAttribute]
string SampleMethod();
[OperationContractAttribute(AsyncPattern = true)]
IAsyncResult BeginSampleMethod(AsyncCallback callback, object asyncState);
string EndSampleMethod(IAsyncResult result);
}
public class SampleService : ISampleService
{
// the async method needs to be private so that WCF doesn't try to
// understand its return type of Task<string>
private async Task<string> SampleMethodAsync()
{
// perform your async operation here
}
public string SampleMethod()
{
return this.SampleMethodAsync().Result;
}
public IAsyncResult BeginSampleMethod(AsyncCallback callback, object asyncState)
{
var task = this.SampleMethodAsync();
if (callback != null)
{
task.ContinueWith(_ => callback(task));
}
return task;
}
public string EndSampleMethod(IAsyncResult result)
{
return ((Task<string>)result).Result;
}
}

Serialization Exception in compiled dll

I've inherited an ecommerce ASP.NET (c# code behind) web application. We've recently moved servers and it's proving somewhat troublesome. I have very little experience with IIS server configuration and dealing with large projects like this. Most of the problems have now been fixed, but we're experiencing problems with a crucial part, as a customer attempts to make a payment.
As the customer confirms payment, the application encounters the following error:
Unable to serialize the session state. In 'StateServer' and 'SQLServer' mode, ASP.NET
will serialize the session state objects, and as a result non-serializable objects or
MarshalByRef objects are not permitted. The same restriction applies if similar
serialization is done by the custom session state store in 'Custom' mode.
Stack Trace:
[SerializationException: Type 'PayerAuthentication.PayerAuthenticationServicePost' in Assembly 'PayerAuthentication, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked as serializable.]
System.Runtime.Serialization.FormatterServices.InternalGetSerializableMembers(RuntimeType type) +7733643
System.Runtime.Serialization.FormatterServices.GetSerializableMembers(Type type, StreamingContext context) +258
System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitMemberInfo() +111
System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitSerialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter) +161
System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.Serialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter) +51
System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize(Object graph, Header[] inHeaders, __BinaryWriter serWriter, Boolean fCheck) +410
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph, Header[] headers, Boolean fCheck) +134
System.Web.Util.AltSerialization.WriteValueToStream(Object value, BinaryWriter writer) +1577
Google search results indicate I should add [Serializable] to the class declaration affected, but this is in a compiled dll to which I do not have the csproj.
The code was working fine on the previous server and I do not believe any changes have been made to the code, only to web.config - what can I do?
The sessionstate section of web.config reads <sessionState mode="StateServer" />
UPDATE1: Using Reflector, I exported the class above, made it serializable, recompiled and replaced the dll. The order process went one step further, wherepon I encountered the same error for another dll-compiled class. Once again I was able to use Reflector to see the code, and then export it, edit and recompile.
Now I have the same error occurring in:
SerializationException: Type 'System.Runtime.Remoting.Messaging.AsyncResult' in Assembly 'mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' is not marked as serializable.]
I'm not sure I can do anything about this, as this must be part of the .net system files! Any further ideas?
UPDATE2: Ha, well I've subsequently discovered it's processing the payments correctly, but then throwing the above Unable to serialize the session state error on System.Runtime.Remoting.Messaging.AsyncResult before the user gets receipt of the transaction. Not good. Unsure how to move forward now...
UPDATE3: I tried creating a copy of the System.Runtime.Remoting.Messaging.AsyncResult class, and making it serializable but this is then leading to inconsistent accessibility problems.
using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Security.Permissions;
using System.Runtime.Remoting.Messaging;
[Serializable, ComVisible(true)]
public class myAsyncResult : IAsyncResult, IMessageSink
{
// Fields
private AsyncCallback _acbd;
private Delegate _asyncDelegate;
private object _asyncState;
private ManualResetEvent _AsyncWaitHandle;
private bool _endInvokeCalled;
private bool _isCompleted;
private IMessageCtrl _mc;
private IMessage _replyMsg;
// Methods
internal myAsyncResult(Message m);
//[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure)]
public virtual IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink);
private void FaultInWaitHandle();
public virtual IMessage GetReplyMessage();
public virtual void SetMessageCtrl(IMessageCtrl mc);
//[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure)]
public virtual IMessage SyncProcessMessage(IMessage msg);
// Properties
public virtual object AsyncDelegate { get; }
public virtual object AsyncState { get; }
public virtual WaitHandle AsyncWaitHandle { get; }
public virtual bool CompletedSynchronously { get; }
public bool EndInvokeCalled { get; set; }
public virtual bool IsCompleted { get; }
public IMessageSink NextSink { [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure)] get; }
}
Specifically, that error CS0122: 'System.Runtime.Remoting.Messaging.Message' is inaccessible due to its protection level. I can see this is because Message is an internal class. But surely I can't change the accessibility level of it as it is part of the System.Runtime namespace. And making a copy and renaming it is going to cuase the same problem again, surely?
Can anyone help me now?
FINAL UPDATE
Looks like, after all this, that it was the SSL certificate (see my answer below)
If you really wanted the code, you could try using Reflector's Class View. At the very least, it could help you verify whether or not [Serializable] was part of the problem class definition or not.
You'll need to find out if the new server is a later version than the old, or an older one. If it's an older version, then upgrade it to the newer version, and things should work.
If it's newer, then is it your code (that you have source to) that puts these non-serializable objects into session state? If so, then you can maybe create your own class to mirror the properties of the old class. Make your class serializable and put an instance of your class into session state. Make an instance of the old class when you take yours out of session state.
I now believe that this problem arose when we installed a new SSL certificate.
The new certificate had Postcode extensions which our payment merchant HSBC doesn't accept over it's CPI payment gateway.
Getting the correct SSL certificate installed seems to have finally solved this problem.
If the code was previously using just the in-memory state provider, then this could be... tricky. It is a pain point that the serialization process (via BinaryFormatter, which the database state provider uses) requires the [Serializable] attribute when the default provider doesn't.
How much of the code can you edit? Any of it? For example, can you change the code that puts things into/out-of state? You could perhaps use a separate (serializable) DTO with the necessary properties and translate between them with your own code.
Other options:
go back to the in-memory provider (and wave goodbye to a cluster)
write a provider that doesn't use BinaryFormatter
I have some thoughts on the latter, but I doubt it would be trivial
If the question is just how to make the application run without this error, the fast solution is to set the mode attribute of the sessionState element to "InProc".

Categories