|
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
namespace System.ServiceModel.Dispatcher
{
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.Collections.Generic;
using System.Runtime.Serialization;
using DiagnosticUtility = System.ServiceModel.DiagnosticUtility;
using System.Runtime.Serialization.Json;
using System.Collections;
using System.Linq;
class SingleBodyParameterDataContractMessageFormatter : SingleBodyParameterMessageFormatter
{
static readonly Type TypeOfNullable = typeof(Nullable<>);
static readonly Type[] CollectionDataContractInterfaces = new Type[] { typeof(IEnumerable), typeof(IList), typeof(ICollection), typeof(IDictionary) };
static readonly Type[] GenericCollectionDataContractInterfaces = new Type[] { typeof(IEnumerable<>), typeof(IList<>), typeof(ICollection<>), typeof(IDictionary<,>) };
XmlObjectSerializer cachedOutputSerializer;
Type cachedOutputSerializerType;
bool ignoreExtensionData;
XmlObjectSerializer[] inputSerializers;
IList<Type> knownTypes;
int maxItemsInObjectGraph;
Type parameterDataContractType;
IDataContractSurrogate surrogate;
Object thisLock;
bool useJsonFormat;
bool isParameterCollectionInterfaceDataContract;
bool isQueryable;
public SingleBodyParameterDataContractMessageFormatter(OperationDescription operation, Type parameterType, bool isRequestFormatter, bool useJsonFormat, DataContractSerializerOperationBehavior dcsob)
: base(operation, isRequestFormatter, "DataContractSerializer")
{
if (operation == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("operation");
}
if (parameterType == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("parameterType");
}
if (dcsob == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("dcsob");
}
this.parameterDataContractType = DataContractSerializerOperationFormatter.GetSubstituteDataContractType(parameterType, out isQueryable);
this.isParameterCollectionInterfaceDataContract = IsTypeCollectionInterface(this.parameterDataContractType);
List<Type> tmp = new List<Type>();
if (operation.KnownTypes != null)
{
foreach (Type knownType in operation.KnownTypes)
{
tmp.Add(knownType);
}
}
Type nullableType = UnwrapNullableType(this.parameterDataContractType);
if (nullableType != this.parameterDataContractType)
{
tmp.Add(nullableType);
}
this.surrogate = dcsob.DataContractSurrogate;
this.ignoreExtensionData = dcsob.IgnoreExtensionDataObject;
this.maxItemsInObjectGraph = dcsob.MaxItemsInObjectGraph;
this.knownTypes = tmp.AsReadOnly();
ValidateType(this.parameterDataContractType, surrogate, this.knownTypes);
this.useJsonFormat = useJsonFormat;
CreateInputSerializers(this.parameterDataContractType);
thisLock = new Object();
}
internal static Type UnwrapNullableType(Type type)
{
while (type.IsGenericType && type.GetGenericTypeDefinition() == TypeOfNullable)
{
type = type.GetGenericArguments()[0];
}
return type;
}
// The logic of this method should be kept the same as
// System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.PartInfo.ReadObject
protected override object ReadObject(Message message)
{
object val = base.ReadObject(message);
if (this.isQueryable && val != null)
{
return Queryable.AsQueryable((IEnumerable)val);
}
return val;
}
protected override void AttachMessageProperties(Message message, bool isRequest)
{
if (this.useJsonFormat)
{
message.Properties.Add(WebBodyFormatMessageProperty.Name, WebBodyFormatMessageProperty.JsonProperty);
}
}
protected override XmlObjectSerializer[] GetInputSerializers()
{
return this.inputSerializers;
}
protected override XmlObjectSerializer GetOutputSerializer(Type type)
{
lock (thisLock)
{
// if we already have a serializer for this type reuse it
if (this.cachedOutputSerializerType != type)
{
Type typeForSerializer;
if (this.isParameterCollectionInterfaceDataContract)
{
// if the parameterType is a collection interface, ensure the type implements it
if (!this.parameterDataContractType.IsAssignableFrom(type))
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SerializationException(SR2.GetString(SR2.TypeIsNotParameterTypeAndIsNotPresentInKnownTypes, type, this.OperationName, this.ContractName, parameterDataContractType)));
}
typeForSerializer = this.parameterDataContractType;
}
else
{
typeForSerializer = GetTypeForSerializer(type, this.parameterDataContractType, this.knownTypes);
}
this.cachedOutputSerializer = CreateSerializer(typeForSerializer);
this.cachedOutputSerializerType = type;
}
return this.cachedOutputSerializer;
}
}
static bool IsTypeCollectionInterface(Type parameterType)
{
if (parameterType.IsGenericType && parameterType.IsInterface)
{
Type genericTypeDef = parameterType.GetGenericTypeDefinition();
foreach (Type type in GenericCollectionDataContractInterfaces)
{
if (genericTypeDef == type)
{
return true;
}
}
}
foreach (Type type in CollectionDataContractInterfaces)
{
if (parameterType == type)
{
return true;
}
}
return false;
}
protected override void ValidateMessageFormatProperty(Message message)
{
if (this.useJsonFormat)
{
// useJsonFormat is always false in the green bits
object prop;
message.Properties.TryGetValue(WebBodyFormatMessageProperty.Name, out prop);
WebBodyFormatMessageProperty formatProperty = (prop as WebBodyFormatMessageProperty);
if (formatProperty == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR2.GetString(SR2.MessageFormatPropertyNotFound, this.OperationName, this.ContractName, this.ContractNs)));
}
if (formatProperty.Format != WebContentFormat.Json)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR2.GetString(SR2.InvalidHttpMessageFormat, this.OperationName, this.ContractName, this.ContractNs, formatProperty.Format, WebContentFormat.Json)));
}
}
else
{
base.ValidateMessageFormatProperty(message);
}
}
static void ValidateType(Type parameterType, IDataContractSurrogate surrogate, IEnumerable<Type> knownTypes)
{
XsdDataContractExporter dataContractExporter = new XsdDataContractExporter();
if (surrogate != null || knownTypes != null)
{
ExportOptions options = new ExportOptions();
options.DataContractSurrogate = surrogate;
if (knownTypes != null)
{
foreach (Type knownType in knownTypes)
{
options.KnownTypes.Add(knownType);
}
}
dataContractExporter.Options = options;
}
dataContractExporter.GetSchemaTypeName(parameterType); // throws if parameterType is not a valid data contract
}
void CreateInputSerializers(Type type)
{
List<XmlObjectSerializer> tmp = new List<XmlObjectSerializer>();
tmp.Add(CreateSerializer(type));
foreach (Type knownType in this.knownTypes)
{
tmp.Add(CreateSerializer(knownType));
}
this.inputSerializers = tmp.ToArray();
}
XmlObjectSerializer CreateSerializer(Type type)
{
if (this.useJsonFormat)
{
return new DataContractJsonSerializer(type, this.knownTypes, this.maxItemsInObjectGraph, this.ignoreExtensionData, this.surrogate, false);
}
else
{
return new DataContractSerializer(type, this.knownTypes, this.maxItemsInObjectGraph, this.ignoreExtensionData, false, this.surrogate);
}
}
}
}
|