WCF Cannot deserialize when using IDataContractSurrogate - wcf

I'm using WCF service with WebHttpBinding. I have written custom IDataContractSurrogate implementation to serialize enum as strings.
Enums are serialization WORKS, but deserialization fails.
when request contains enum, then I get:
The remote server returned an unexpected response: (400) Bad Request.
or when response contains an enum, then I get:
InvalidCastException: Specified cast is not valid
Server stack trace:
at ReadMyResponseFromJson(XmlReaderDelegator , XmlObjectSerializerReadContextComplexJson , XmlDictionaryString , XmlDictionaryString[] )
at System.Runtime.Serialization.Json.JsonClassDataContract.ReadJsonValueCore(XmlReaderDelegator jsonReader, XmlObjectSerializerReadContextComplexJson context)
at System.Runtime.Serialization.Json.XmlObjectSerializerReadContextComplexJson.ReadDataContractValue(DataContract dataContract, XmlReaderDelegator reader)
at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator reader, String name, String ns, Type declaredType, DataContract& dataContract)
at System.Runtime.Serialization.XmlObjectSerializerReadContextComplex.InternalDeserializeWithSurrogate(XmlReaderDelegator xmlReader, Type declaredType, DataContract surrogateDataContract, String name, String ns)
at System.Runtime.Serialization.XmlObjectSerializerReadContextComplex.InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, DataContract dataContract, String name, String ns)
at System.Runtime.Serialization.Json.DataContractJsonSerializer.InternalReadObject(XmlReaderDelegator xmlReader, Boolean verifyObjectName)
at System.Runtime.Serialization.XmlObjectSerializer.InternalReadObject(XmlReaderDelegator reader, Boolean verifyObjectName, DataContractResolver dataContractResolver)
at System.Runtime.Serialization.XmlObjectSerializer.ReadObjectHandleExceptions(XmlReaderDelegator reader, Boolean verifyObjectName, DataContractResolver dataContractResolver)
at System.Runtime.Serialization.Json.DataContractJsonSerializer.ReadObject(XmlDictionaryReader reader, Boolean verifyObjectName)
at ...
the server code:
string baseAddress = "http://localhost:8733/Design_Time_Addresses/FingerprintService/";
_serviceHost = new WebServiceHost(myServiceInstance, new Uri(baseAddress));
_serviceHost.AddServiceEndpoint(typeof (IMyService), new WebHttpBinding(WebHttpSecurityMode.None), baseAddress);
EndpointExtension.Setup(_serviceHost.Description.Endpoints[0]);
_serviceHost.Open();
client code:
IMyService FingerprintService()
{
var channelFaftory = new WebChannelFactory<IMyService>(new Uri(TbxUri.Text));
EndpointExtension.Setup(channelFaftory.Endpoint);
return channelFaftory.CreateChannel();
}
the endpoint setup (common for both host and client):
public static void Setup(ServiceEndpoint endpoint)
{
var webHttpBehavior = endpoint.Behaviors.Find<WebHttpBehavior>();
if (webHttpBehavior == null)
{
webHttpBehavior = new WebHttpBehavior();
endpoint.Behaviors.Add(webHttpBehavior);
}
foreach (OperationDescription opertion in endpoint.Contract.Operations)
{
var dataContractBehavior = opertion.Behaviors.Find<DataContractSerializerOperationBehavior>();
dataContractBehavior.DataContractSurrogate = new EnumSurrogate();
}
}
}
and finaly, the surrogate:
public class EnumSurrogate : IDataContractSurrogate
{
public Type GetDataContractType(Type type)
{
if (type.IsEnum)
{
return typeof(string);
}
return type;
}
public object GetObjectToSerialize(object obj, Type targetType)
{
if (obj is Enum)
{
return obj.ToString();
}
return obj;
}
public object GetDeserializedObject(object obj, Type targetType)
{
if (obj is string && targetType.IsEnum)
{
return Enum.Parse(targetType, (string)obj);
}
return obj;
}
//other methods throws NotImplementedException
}

your approach works nicely when serializing, but will not work when deserializing. This will fail because primitive values cannot be surrogated while deserializing.
https://canbilgin.wordpress.com/2012/06/07/how-to-serialize-an-enum-as-string-with-idatacontractsurrogate/

Related

Cannot consume WCF Rest Services in WCF Client

I have a Service and the releated interface under namespace called "WCFService":
[ServiceContract]
public interface IBook
{
[OperationContract]
[WebInvoke(Method="GET"
,ResponseFormat=WebMessageFormat.Json
,BodyStyle=WebMessageBodyStyle.Wrapped
,UriTemplate="/?bookID={bookID}")]
Book Get(int bookID);
}
//========================================================
public Book Get(int bookID)
{
return bookList.Where(p => p.BookID == bookID).FirstOrDefault();
}
And the Book DataModel is under another namespace called "WCFModel":
[DataContract]
public class Book
{
[DataMember]
public int BookID { get; set; }
[DataMember]
public string BookName { get; set; }
[DataMember]
public decimal BookPrice { get; set; }
[DataMember]
public string BookPublish { get; set; }
}
After set the config file, I got the service run ok.
Then I planned to consume it in client side. I added a windows form application to the project and added reference to the WCFModel book datamodel, I used below code to extract the data:
string serviceURL = "http://localhost:15020/BookService.svc/?bookID=" + txtBookID.Text.Trim();
var request = WebRequest.Create(serviceURL);
var response = request.GetResponse();
DataContractSerializer serializer = new DataContractSerializer(typeof(WCFModel.Book));
var bookObj = serializer.ReadObject(response.GetResponseStream());
But the result was, VS thrown me an exception at the ReadObject below:
Error when deserializing the WCFModel.Book type, Data at the root level is invalid.
stack trace below:
在 System.Xml.XmlExceptionHelper.ThrowXmlException(XmlDictionaryReader reader, String res, String arg1, String arg2, String arg3)
在 System.Xml.XmlUTF8TextReader.Read()
在 System.Xml.XmlBaseReader.IsStartElement()
在 System.Xml.XmlBaseReader.IsStartElement(XmlDictionaryString localName, XmlDictionaryString namespaceUri)
在 System.Runtime.Serialization.XmlReaderDelegator.IsStartElement(XmlDictionaryString localname, XmlDictionaryString ns)
在 System.Runtime.Serialization.XmlObjectSerializer.IsRootElement(XmlReaderDelegator reader, DataContract contract, XmlDictionaryString name, XmlDictionaryString ns)
在 System.Runtime.Serialization.DataContractSerializer.InternalIsStartObject(XmlReaderDelegator reader)
在 System.Runtime.Serialization.DataContractSerializer.InternalReadObject(XmlReaderDelegator xmlReader, Boolean verifyObjectName, DataContractResolver dataContractResolver)
在 System.Runtime.Serialization.XmlObjectSerializer.ReadObjectHandleExceptions(XmlReaderDelegator reader, Boolean verifyObjectName, DataContractResolver dataContractResolver)
I don't know why, After searching a lot, I still can't get the point. Anyone can give me some reference? thx.
I hope this may work for you!
Try implementing this!
WebClient proxy = new WebClient();
string serviceURL =
string.Format("http://localhost:15020/BookService.svc/?bookID=" + txtBookID.Text.Trim().ToString());
byte[] data = proxy.DownloadData(serviceURL);
Stream stream = new MemoryStream(data);
DataContractJsonSerializer obj =
new DataContractJsonSerializer(typeof(Book));
Book book = obj.ReadObject(stream) as Book;
Console.WriteLine("Book ID : " + book.BookID);
Console.WriteLine("Book Name : " + book.BookName);
Console.WriteLine("Book ID : " + book.BookPrice);
Console.WriteLine("Book ID : " + book.BookPublish);
See this link
EDIT: Change BodyStyle=WebMessageBodyStyle.Wrapped to BodyStyle=WebMessageBodyStyle.Bare.

WCF AsyncPattern and masstransit request/response

I try to implement an Asynchronous Service Operation (http://msdn.microsoft.com/en-us/library/ms731177.aspx) with masstransit request/response.
[ServiceContract]
public interface IService1
{
[OperationContractAttribute(AsyncPattern = true)]
IAsyncResult BeginMyOperation(string data, AsyncCallback callback, object asyncState);
string EndMyOperation(IAsyncResult result);
}
public class Service1 : IService1
{
private string _answer;
public IAsyncResult BeginMyOperation(string data, AsyncCallback callback, object asyncState)
{
return Bus.Instance.BeginPublishRequest(
new MyRequestMessage { Data = data }, callback, asyncState, cfg =>
{
cfg.Handle<MyResponseMessage>(c => _answer = c.Answer);
cfg.SetTimeout(5.Seconds());
});
}
public string EndMyOperation(IAsyncResult result)
{
Bus.Instance.EndPublishRequest<MyResponseMessage>(result);
return _answer;
}
}
But EndPublishRequest throws an Exception "The argument is not an IRequest". Am I doing something wrong?
Yeah, the exception message needs to be a bit better, if you look at the signature though:
public static bool EndPublishRequest<TRequest>(this IServiceBus bus, IAsyncResult asyncResult)
it becomes apparent that the request message type is the required type arg here.

WCF IClientMessageInspector crash on serialization

I use the IClientMessageInspector and it's method BeforeSendRequest, to inspect the request messages. The thing is that some of request messages has members, which members are of type object (in the real scenario thre may be in example string or some other value, which is specified in the KnownType attribute):
[DataContract(Namespace = "")]
[KnownType(typeof(SomeValueDTO))]
public class CenDTO
{
[DataMember]
public string Uri { get; set; }
[DataMember]
public object Value { get; set; }
}
Notice, that in this project the namespaces are not defined anywhere. So on the "BeforeSendRequest" I got exception. And looks like this interface uses not the same DataContractSerializer as in the real service, which hasn't such serialization errors or smth like that.
The error:
The empty string '' is not a valid name.
at System.Xml.XmlTextWriter.LookupPrefix(String ns)
at System.Xml.XmlDictionaryWriter.XmlWrappedWriter.LookupPrefix(String namespaceUri)
at System.Xml.XmlDictionaryWriter.XmlWrappedWriter.WriteXmlnsAttribute(String prefix, String namespaceUri)
at System.Xml.XmlDictionaryWriter.WriteXmlnsAttribute(String prefix, XmlDictionaryString namespaceUri)
at System.Runtime.Serialization.XmlWriterDelegator.WriteXmlnsAttribute(XmlDictionaryString ns)
at System.Runtime.Serialization.XmlWriterDelegator.WriteAttributeQualifiedName(String attrPrefix, XmlDictionaryString attrName, XmlDictionaryString attrNs, XmlDictionaryString name, XmlDictionaryString ns)
at System.Runtime.Serialization.XmlObjectSerializerWriteContext.WriteTypeInfo(XmlWriterDelegator writer, XmlDictionaryString dataContractName, XmlDictionaryString dataContractNamespace)
at System.Runtime.Serialization.XmlObjectSerializerWriteContext.WriteTypeInfo(XmlWriterDelegator writer, DataContract contract, DataContract declaredContract)
at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithXsiType(XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle objectTypeHandle, Type objectType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle, Type declaredType)
at System.Runtime.Serialization.XmlObjectSerializerWriteContext.InternalSerialize(XmlWriterDelegator xmlWriter, Object obj, Boolean isDeclaredType, Boolean writeXsiType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle)
at WriteCenDTOToXml(XmlWriterDelegator , Object , XmlObjectSerializerWriteContext , ClassDataContract )
at System.Runtime.Serialization.ClassDataContract.WriteXmlValue(XmlWriterDelegator xmlWriter, Object obj, XmlObjectSerializerWriteContext context)
at System.Runtime.Serialization.XmlObjectSerializerWriteContext.WriteDataContractValue(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle)
at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithoutXsiType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle)
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.XmlObjectSerializer.WriteObject(XmlDictionaryWriter writer, Object graph)
at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.SerializeParameterPart(XmlDictionaryWriter writer, PartInfo part, Object graph)
at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.SerializeParameter(XmlDictionaryWriter writer, PartInfo part, Object graph)
at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.SerializeParameters(XmlDictionaryWriter writer, PartInfo[] parts, Object[] parameters)
at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.SerializeBody(XmlDictionaryWriter writer, MessageVersion version, String action, MessageDescription messageDescription, Object returnValue, Object[] parameters, Boolean isRequest)
at System.ServiceModel.Dispatcher.OperationFormatter.SerializeBodyContents(XmlDictionaryWriter writer, MessageVersion version, Object[] parameters, Object returnValue, Boolean isRequest)
at System.ServiceModel.Dispatcher.OperationFormatter.OperationFormatterMessage.OperationFormatterBodyWriter.OnWriteBodyContents(XmlDictionaryWriter writer)
at System.ServiceModel.Channels.BodyWriter.WriteBodyContents(XmlDictionaryWriter writer)
at System.ServiceModel.Channels.BodyWriterMessage.OnBodyToString(XmlDictionaryWriter writer)
at System.ServiceModel.Channels.Message.ToString(XmlDictionaryWriter writer)
at System.ServiceModel.Channels.Message.ToString()
at CID.TopicAnalyst.CIA.Test.Utils.MessageInspector.BeforeSendRequest(Message& request, IClientChannel channel) in D:\git\...\MessageInspector.cs:line 25
at System.ServiceModel.Dispatcher.ImmutableClientRuntime.BeforeSendRequest(ProxyRpc& rpc)
Implementation of "BeforeSendRequest"(The crash is on the .ToString() ):
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
if(request.Headers.Count > 0)
{
request.Headers.RemoveAt(0);
}
MessageBuffer buffer = request.CreateBufferedCopy(Int32.MaxValue);
request = buffer.CreateMessage();
var message = buffer.CreateMessage().ToString();
var resultFile = ConfigurationManager.AppSettings.GetValues("requestFile").First();
_serializer.Write(resultFile, message);
return null;
}
The testing itself is a separate solution, which uses the service's reference.
On the service imnplmentation the operation looks like this:
[ServiceContract]
public interface ISoapService
{
[OperationContract]
SomeListResponseMessage GetSomeList(SomeListRequestMessage request);
}
[MessageContract]
public class SomeListRequestMessage : BaseRequestMessage
{
...
[MessageBodyMember]
public CenDTO Cen { get; set; }
...
}
[DataContract(Namespace = "")]
[KnownType(typeof(SemValueDTO))]
public class CenDTO
{
...
[DataMember]
public object Value { get; set; }
}
Some code of the Testing Solution:
static class Program
{
static void Main(string[] args)
{
while(true)
{
var client = new SoapServiceClient("BasicHttpBinding_ISoapService");
client.Endpoint.Behaviors.Add(new InspectorBehavior());
var soapService = client.ChannelFactory.CreateChannel();
var co = new CooRequestCreator(soapService);
co.CreateAndWriteRequests();
Console.WriteLine("Press y to repeat");
var str = Console.ReadLine();
if(str != "y") break;
}
}
Constructing the request message before calling the service:
class CooRequestCreator : BaseRequestCreator<SomeListRequestMessage>
{
SemValueDTO _semaValue = new SemValueDTO
{
Key = "fb",
Label = "Guild",
Type = "org"
};
public CooRequestCreator(ISoapService soapService) : base(soapService)
{
}
protected override SomeListRequestMessage CreateMinRequest()
{
return new SomeListRequestMessage
{
Cen =
new CenDTO
{
Value = _semaValue,
},
MaxCount = 5
};
}
Maybe someone can has the same experience and can help with that?

Serializing / Deserializing System.Uri

I've been using RavenDB for all of two hours, so apologies if I've missed something obvious.
I'm storing a denormalized view model with a property of type System.Uri. The Uri is serialized as a string, which is OK I guess, but throws this exception when I load the document:
Message=Could not cast or convert from System.String to System.Uri.
Source=Newtonsoft.Json
StackTrace:
at Newtonsoft.Json.Utilities.ConvertUtils.EnsureTypeAssignable(Object value, Type initialType, Type targetType) in d:\Development\Releases\Json\Working\Src\Newtonsoft.Json\Utilities\ConvertUtils.cs:line 267
at Newtonsoft.Json.Utilities.ConvertUtils.ConvertOrCast(Object initialValue, CultureInfo culture, Type targetType) in d:\Development\Releases\Json\Working\Src\Newtonsoft.Json\Utilities\ConvertUtils.cs:line 244
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.EnsureType(JsonReader reader, Object value, CultureInfo culture, JsonContract contract, Type targetType) in d:\Development\Releases\Json\Working\Src\Newtonsoft.Json\Serialization\JsonSerializerInternalReader.cs:line 544
Got it! There are two secrets. The first is to create a JsonConverter for the Uri type.
public class UriJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return object.Equals(objectType, typeof (Uri));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
switch (reader.TokenType)
{
case JsonToken.String:
return CreateUri((string) reader.Value);
case JsonToken.Null:
return null;
default:
var msg = string.Format("Unable to deserialize Uri from token type {0}", reader.TokenType);
throw new InvalidOperationException(msg);
}
}
private static Uri CreateUri(string uriString)
{
Uri uri;
if (!Uri.TryCreate(uriString, UriKind.Absolute, out uri))
if (!Uri.TryCreate(uriString, UriKind.Absolute, out uri))
if (!Uri.TryCreate(uriString, UriKind.RelativeOrAbsolute, out uri))
{
var msg = string.Format("Unable to determine proper UriKind for Uri {0}", uriString);
throw new InvalidOperationException(msg);
}
return uri;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (null == value)
{
writer.WriteNull();
return;
}
var uri = value as Uri;
if (uri != null)
{
writer.WriteValue(uri.OriginalString);
return;
}
var msg = string.Format("Unable to serialize {0} with {1}", value.GetType(), typeof (UriJsonConverter));
throw new InvalidOperationException(msg);
}
}
The second is to register the converter with the RavenDB serializer.
private static DocumentStore OpenStore()
{
var store = new DocumentStore()
{
ConnectionStringName = "RavenDB"
};
store.Conventions.CustomizeJsonSerializer = CustomJsonSerializer;
store.Initialize();
return store;
}
private static void CustomJsonSerializer(JsonSerializer serializer)
{
serializer.Converters.Add(new UriJsonConverter());
}
I'm not positive what Newtonsoft is doing under the covers, but if it's calling new System.Uri("/about-us") without specifying UriKind.Relative in the constructor a UriFormatException will be thrown.
So, depending on how your Uri is being created in your model, it might be an option for you to ensure it's absolute before storing it.
I'm not positive, but I would think a Uri like new System.Uri("http://foo.com/about-us") would be stored as "http://foo.com/about-us" and would convert OK when it comes back out.

Creating WCF Service at runtime

We are going to build a web service from metadata read at runtime. I mean the entire web service: the signatures, contracts and implementation.
There are two main paths I see from here.
The first path is that you generate code. Either you generate C# code in strings and compile it on the fly or more elegantly (and complicatedly), you emit MSIL code. This way you have WCF code and WCF will take care of generating the WSDL from it.
The second path is to use a generic service. A service with an operation Message Process(Message) accepting everything. We still want to expose the service as a 'normal' service, so I would need a WSDL somewhere. How can I create a WSDL? I thought about using System.ServiceModel.Description until I realised that deep inside, this API depends on concrete types. With this approach we wouldn't have any data contract type and would process XML on the fly, using metadata to interpret it. So we need somehow to generate the WSDL. Is that a crazy idea? WSDL has quite a complicated spec...
A third option would be to use an hybrid approach, emitting types just to create signatures but implementing the service using non-emitted code (reflecting on emitted types). Weird, but might be simpler than hand crafting WSDL by hand...
Suggestions?
It's a pain to do, but possible to emitting types. Create your .svc and point it at your custom ServiceHostFactory:
<%# ServiceHost Language="C#" Debug="true" Factory="Foo.FooServiceHostFactory" %>
[ServiceContract]
public class FooService { }
Your ServiceHostFactory is where you generate your operation contracts, types that go with it, etc:
public class FooServiceHostFactory : ServiceHostFactory {
public override System.ServiceModel.ServiceHostBase CreateServiceHost(string constructorString, Uri[] baseAddresses) {
ServiceHost serviceHost = new FooServiceHost(baseAddresses);
serviceHost.AddDefaultEndpoints();
GenerateServiceOperations(serviceHost);
return serviceHost;
}
private void GenerateServiceOperations(ServiceHost serviceHost) {
var methodNames = new[] {
new { Name = "Add" },
new { Name = "Subtract" },
new { Name = "Multiply" }
};
foreach (var method in methodNames) {
foreach (var endpoint in serviceHost.Description.Endpoints) {
var contract = endpoint.Contract;
var operationDescription = new OperationDescription("Operation" + method.Name, contract);
var requestMessageDescription = new MessageDescription(string.Format("{0}{1}/Operation{2}", contract.Namespace, contract.Name, method.Name), MessageDirection.Input);
var responseMessageDescription = new MessageDescription(string.Format("{0}{1}/Operation{2}Response", contract.Namespace, contract.Name, method.Name), MessageDirection.Output);
var elements = new List<FooDataItem>();
elements.Add(new FooDataItem { Name = "X", DataType = typeof(int) });
elements.Add(new FooDataItem { Name = "Y", DataType = typeof(int) });
//note: for a complex type it gets more complicated, but the same idea using reflection during invoke()
//object type = TypeFactory.CreateType(method.Name, elements);
//var arrayOfType = Array.CreateInstance(type.GetType(), 0);
//var parameter = new MessagePartDescription(method.Name + "Operation", contract.Namespace);
//parameter.Type = arrayOfType.GetType();
//parameter.Index = 0;
//requestMessageDescription.Body.Parts.Add(parameter);
var retVal = new MessagePartDescription("Result", contract.Namespace);
retVal.Type = typeof(int);
responseMessageDescription.Body.ReturnValue = retVal;
int indexer = 0;
foreach (var element in elements) {
var parameter = new MessagePartDescription(element.Name, contract.Namespace);
parameter.Type = element.DataType;
parameter.Index = indexer++;
requestMessageDescription.Body.Parts.Add(parameter);
}
operationDescription.Messages.Add(requestMessageDescription);
operationDescription.Messages.Add(responseMessageDescription);
operationDescription.Behaviors.Add(new DataContractSerializerOperationBehavior(operationDescription));
operationDescription.Behaviors.Add(new FooOperationImplementation());
contract.Operations.Add(operationDescription);
}
}
}
protected override System.ServiceModel.ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses) {
return base.CreateServiceHost(serviceType, baseAddresses);
}
}
In the ServiceHostFactory you define the behaviors along with the metadata, so your behavior would need to implement IOperationBehavior and IOperationInvoker (or you could implement them separately) and look something like this:
public class FooOperationImplementation : IOperationBehavior, IOperationInvoker {
OperationDescription operationDescription;
DispatchOperation dispatchOperation;
public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters) {
}
public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation) {
}
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation) {
this.operationDescription = operationDescription;
this.dispatchOperation = dispatchOperation;
dispatchOperation.Invoker = this;
}
public void Validate(OperationDescription operationDescription) {
}
public object[] AllocateInputs() {
return new object[2];
}
public object Invoke(object instance, object[] inputs, out object[] outputs) {
//this would ALL be dynamic as well depending on how you are creating your service
//for example, you could keep metadata in the database and then look it up, etc
outputs = new object[0];
switch (operationDescription.Name) {
case "OperationAdd":
return (int)inputs[0] + (int)inputs[1];
case "OperationSubtract":
return (int)inputs[0] - (int)inputs[1];
case "OperationMultiply":
return (int)inputs[0] * (int)inputs[1];
default:
throw new NotSupportedException("wtf");
}
}
public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state) {
throw new NotImplementedException("Method is not asynchronous.");
}
public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result) {
throw new NotImplementedException("Method is not asynchronous.");
}
public bool IsSynchronous {
get { return true; }
}
}
For complex types, this is where you need to make a judgement call, which is the root of your question, is how to emit the types. Here's an example, but you can do it any way you can see fit. I'm calling this in my ServiceHostFactory (warning: it's demo code)
static public class TypeFactory {
static object _lock = new object();
static AssemblyName assemblyName;
static AssemblyBuilder assemblyBuilder;
static ModuleBuilder module;
static TypeFactory() {
lock (_lock) {
assemblyName = new AssemblyName();
assemblyName.Name = "FooBarAssembly";
assemblyBuilder = Thread.GetDomain().DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
module = assemblyBuilder.DefineDynamicModule("FooBarModule");
}
}
static public object CreateType(string typeName, List<FooDataItem> elements) {
TypeBuilder typeBuilder = module.DefineType(typeName, TypeAttributes.Public | TypeAttributes.Class);
foreach(var element in elements) {
string propertyName = element.Name;
Type dataType = element.DataType;
FieldBuilder field = typeBuilder.DefineField("_" + propertyName, dataType, FieldAttributes.Private);
PropertyBuilder property =
typeBuilder.DefineProperty(propertyName,
PropertyAttributes.None,
dataType,
new Type[] { dataType });
MethodAttributes GetSetAttr =
MethodAttributes.Public |
MethodAttributes.HideBySig;
MethodBuilder currGetPropMthdBldr =
typeBuilder.DefineMethod("get_value",
GetSetAttr,
dataType,
Type.EmptyTypes);
ILGenerator currGetIL = currGetPropMthdBldr.GetILGenerator();
currGetIL.Emit(OpCodes.Ldarg_0);
currGetIL.Emit(OpCodes.Ldfld, field);
currGetIL.Emit(OpCodes.Ret);
MethodBuilder currSetPropMthdBldr =
typeBuilder.DefineMethod("set_value",
GetSetAttr,
null,
new Type[] { dataType });
ILGenerator currSetIL = currSetPropMthdBldr.GetILGenerator();
currSetIL.Emit(OpCodes.Ldarg_0);
currSetIL.Emit(OpCodes.Ldarg_1);
currSetIL.Emit(OpCodes.Stfld, field);
currSetIL.Emit(OpCodes.Ret);
property.SetGetMethod(currGetPropMthdBldr);
property.SetSetMethod(currSetPropMthdBldr);
}
Type generetedType = typeBuilder.CreateType();
return Activator.CreateInstance(generetedType);
}
}
update: I wrote a quick example and it is available here.