After Enable IDispatchMessageFormatter I see nullable parameters in service - wcf

after add my formatter to operation behavior :
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
ServerMessageFormatter Formatter = new ServerMessageFormatter();
dispatchOperation.Formatter = Formatter;
}
In Formatter I have empty Deserialize method cause I want to use default behavior
public void DeserializeRequest(System.ServiceModel.Channels.Message message, object[] parameters)
{}
but In Serialize
public System.ServiceModel.Channels.Message SerializeReply(System.ServiceModel.Channels.MessageVersion messageVersion, object[] parameters, object result)
{
//some code
}
Problem is that after enable this class, parameters in service method always was show as null, but in IDispatchMessageInspector class I see that parameters is send properly. I don't know why it's happening, I only add this message formatter code , it is possible that empty class for Deserialize causes this ?

When we are implementing IDispatchMessageFormatter interface usually we are not thinking that method DeserializeRequest is something important as it is not returning any data. This is misleading as method needs to do something.
The easiest way to make parameters correctly passed is to use base DispatchMessageFormatter. Add it to the constructor.
public class ResponseJsonFormatter : IDispatchMessageFormatter
{
IDispatchMessageFormatter basicDispatchMessageFormatter;
OperationDescription Operation;
public ResponseJsonFormatter(OperationDescription operation, IDispatchMessageFormatter inner)
{
this.Operation = operation;
this.basicDispatchMessageFormatter = inner;
}
public void DeserializeRequest(Message message, object[] parameters)
{
basicDispatchMessageFormatter.DeserializeRequest(message, parameters);
}
public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result)
{
string json=Newtonsoft.Json.JsonConvert.SerializeObject(result);
byte[] bytes = Encoding.UTF8.GetBytes(json);
Message replyMessage = Message.CreateMessage(messageVersion, Operation.Messages[1].Action, new RawDataWriter(bytes));
replyMessage.Properties.Add(WebBodyFormatMessageProperty.Name, new WebBodyFormatMessageProperty(WebContentFormat.Raw));
return replyMessage;
}
}
And initiate it in the behavior:
public class ClientJsonDateFormatterBehavior : IOperationBehavior
{
public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
{
// throw new NotImplementedException();
}
public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
{
}
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
dispatchOperation.Formatter = new ResponseJsonFormatter(operationDescription, dispatchOperation.Formatter);
}
public void Validate(OperationDescription operationDescription)
{
// throw new NotImplementedException();
}
}
You can check the working example here in the github branch DateTimeFormatterWithParams

There is no default behavior if you do not provide your own logic on DeserializeRequest. You need to either reference the existing formatter and delegate manually in your ServerMessageFormater or provide your own logic.

Related

How to add control the cache of a restful service operation by operation?

I've written a rest service using WCF. The service contains several operations. Some are GET based ([WebGet]), others are POST based ([WebInvoke]).
The service is working as expected. However, the GET based operations are put in the client cache, which is not desirable for all operations.
After a bit a search, I've found How to prevent the browser from caching WCF JSON responses. This is working, but I found it's not very reusable.
My platform does not allows me to update the web.config. Actually, my service is part of a SharePoint project. and updating the web.config file is hard to implement properly. This forbid me to use the [WebCache] attribute.
So I implemented a custom MessageInspector which fix the proper headers:
public class CacheAttribute : Attribute, IServiceBehavior
{
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase host)
{
foreach (ChannelDispatcher cDispatcher in host.ChannelDispatchers)
{
foreach (EndpointDispatcher eDispatcher in cDispatcher.Endpoints)
{
eDispatcher.DispatchRuntime.MessageInspectors.Add(new CacheInspector(m_CacheEnabled, CacheDuration));
}
}
}
/*...
Other code omitted for brievty
*/
}
public class CacheInspector : IDispatchMessageInspector
{
/*...
Code omitted for brievety
*/
public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
var cache = HttpContext.Current.Response.Cache;
if (m_CacheEnabled)
{
cache.SetCacheability(HttpCacheability.Public);
cache.SetExpires(DateTime.UtcNow + CacheDuration.Value);
}
else
{
cache.SetExpires(DateTime.UtcNow.AddMinutes(-1));
cache.SetNoStore();
}
}
}
This code is working as expected, but it applies to all operations in the service.
How can I code an attribute based class that apply the same logic, but at the operation scope ?
I've tried to find something useful in the IOperationBehavior interface, but I did not find the appropriate implementation.
Full code (.net 4.5):
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
public class CacheAttribute : Attribute, IServiceBehavior
{
private readonly bool m_CacheEnabled;
public bool CacheEnabled { get { return m_CacheEnabled; } }
public TimeSpan? CacheDuration { get; set; }
public CacheAttribute(bool cacheEnabled)
{
this.m_CacheEnabled = cacheEnabled;
}
public CacheAttribute(TimeSpan cacheDuration) : this(true)
{
this.CacheDuration = cacheDuration;
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase host)
{
foreach (ChannelDispatcher cDispatcher in host.ChannelDispatchers)
{
foreach (EndpointDispatcher eDispatcher in cDispatcher.Endpoints)
{
eDispatcher.DispatchRuntime.MessageInspectors.Add(new CacheInspector(m_CacheEnabled, CacheDuration));
}
}
}
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
}
public class CacheInspector : IDispatchMessageInspector
{
private readonly bool m_CacheEnabled;
private readonly TimeSpan? CacheDuration;
public CacheInspector(bool m_CacheEnabled, TimeSpan? CacheDuration)
{
this.m_CacheEnabled = m_CacheEnabled;
this.CacheDuration = CacheDuration;
}
public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
{
return null;
}
public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
var cache = HttpContext.Current.Response.Cache;
if (m_CacheEnabled)
{
cache.SetCacheability(HttpCacheability.Public);
cache.SetExpires(DateTime.UtcNow + CacheDuration.Value);
}
else
{
cache.SetExpires(DateTime.UtcNow.AddMinutes(-1));
cache.SetNoStore();
}
}
}
I think this is what your are looking for.
[AttributeUsage(AttributeTargets.Method)]
public class CacheAttribute : Attribute, IOperationBehavior, IParameterInspector
{
public TimeSpan CacheLifetime { get; private set; }
public CacheAttribute(double lifetime)
{
this.CacheLifetime = TimeSpan.FromSeconds(lifetime);
}
#region IOperationBehavior Members
public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters) {}
public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation) {}
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
dispatchOperation.ParameterInspectors.Add(this);
}
public void Validate(OperationDescription operationDescription) {}
#endregion
#region IParameterInspector Members
public void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState)
{
if (this.CacheLifetime == TimeSpan.Zero) {
WebOperationContext.Current.OutgoingResponse.Headers.Add("Cache-Control", "no-cache");
} else {
WebOperationContext.Current.OutgoingResponse.Headers.Add("Cache-Control", string.Format("max-age={0}",this.CacheLifetime.TotalSeconds));
}
}
public object BeforeCall(string operationName, object[] inputs)
{
return null;
}
#endregion
}
usage
[ServiceContract]
public interface ICacheTestService
{
[OperationContract]
[WebGet(UriTemplate = "CurrentTime")]
[Cache(0)]
string GetCurrentTime();
[OperationContract]
[WebGet(UriTemplate = "CurrentTimeCached")]
[Cache(30)]
string GetCurrentTimeCached();
[OperationContract]
[WebGet(UriTemplate = "CurrentTimeNoCC")]
string GetCurrentTimeNoCC();
}

How do you implement a base method that should be called for all the methods?

I have a Product Service. On each call to Service, I want to call a method. In this case, I am logging. I am looking for a way, not to write the using statement in each method. But I still want the Logging to happen on each call. How do I do this?
public class ProductService : IProductService
{
public IList<Product> GetProductsByBrand(int BrandID)
{
using (new Logging())
{
// Get a list of products By Brand
}
return new List<Product>();
}
public IList<Product> Search(string ProductName)
{
using (new Logging())
{
// Search
}
return new List<Product>();
}
public static string OrderProducts(IList<Order> Orders, Payment paymentDetials)
{
string AuthCode;
using (new Logging())
{
// Order and get the AuthCode
}
AuthCode = "";
return AuthCode;
}
}
Have you heard of AOP (Aspect Oriented Programming)? It's a way of implementing cross cutting concerns as reusable Aspects that wrap around the target type and perform additional processing before or after the method that they are wrapping.
http://en.wikipedia.org/wiki/Decorator_pattern
Within a WCF environment this is typically done by applying "Behaviors" to your service class. In this case I would suggest the IOperationBehavior interface using an attribute that implements IParameterInspector in order to look at the parameters before they are passed the service instance is created and called. Here is a link to a useful article that goes into more depth regarding your options for extending the wcf message pipeline.
http://msdn.microsoft.com/en-us/magazine/cc163302.aspx
//Attribute class
public class LogOperationBehavior : Attribute, IOperationBehavior, IParameterInspector {
public void AddBindingParameters(OperationDescription operationDescription, System.ServiceModel.Channels.BindingParameterCollection bindingParameters) {
return;
}
public void ApplyClientBehavior(OperationDescription operationDescription, System.ServiceModel.Dispatcher.ClientOperation clientOperation) {
//clientOperation.ParameterInspectors.Add(new ClientParameterInspector());
}
public void ApplyDispatchBehavior(OperationDescription operationDescription, System.ServiceModel.Dispatcher.DispatchOperation dispatchOperation) {
dispatchOperation.ParameterInspectors.Add(this);
}
public void Validate(OperationDescription operationDescription) {
return;
}
#region IParameterInspector Members
public void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState) {
//perform logging after
}
public object BeforeCall(string operationName, object[] inputs) {
//perform logging before
return null;
}
#endregion
}
public class BusinessOperation : IBusinessOperation {
//Apply to your service via an attribute
[LogOperationBehavior]
public DivideResponse DivideTwoNumbers(DivideRequest dr) {
return new DivideResponse() {
Answer = dr.Numerator/ dr.Demoninator2,
};
}
Have you considered creating a logging proxy? It would look something like this:
public class LoggingProductService : IProductService
{
private readonly IProductService _core;
public LoggingProductService(IProductService core)
{
_core = core;
}
public IList<Product> GetProductsByBrand(int BrandID)
{
Log("Getting products for brand " + BrandId);
return _core.GetProductsByBrand(BrandId);
}
//other IProductService methods here, all logging and delegating to _core
private void Log(string message)
{
using (var log = new Logging())
{
log.Write(message);
}
}
}
Of course, I don't entirely understand your Logging interface, so fill in the appropriate guesses with correct code. You also may not want to create and Dispose a Logging that often, I don't know.
You can create a dynamic proxy. See this article for instructions. http://www.drdobbs.com/windows/184405378

WCF response leaving out <xml header>

I am finally starting to get somewhere with WCF, but I have run into another problem. The response sent back does not include the header
<?xml version="1.0" encoding="utf-8" ?>
My Service Contract
[ServiceContract]
public interface IService1
{
// you can have optional parameters by simply specifying them and they will return null if there is nothing in there
[WebGet(UriTemplate="testing={value}", ResponseFormat = WebMessageFormat.Xml)]
[OperationContract]
XElement GetData(string value);
}
[XmlSerializerFormat]
public class Service1 : IService1
{
public XElement GetData(string value)
{
return new XElement("Somename", value);
}
}
returns this (3 is the value specified)
<Somename>3</Somename>
Is it also possible to easily wrap a response in a root element? Something like <response></response>?
responsThe result of calling the GetData method is the contents of what you return in the method. If you want a wrapper then return something like:
[XmlSerializerFormat]
public class Service1 : IService1
{
public XElement GetData(string value)
{
return new XElement("response",
new XElement("Somename", value));
}
}
EDIT:
To add the XML declaration (which actually may not be a good idea but you know best) do something like this:
var doc = new XDocument(
new XElement("response",
new XElement("Somename", value)));
doc.Declaration = new XDeclaration("1.0", "utf-8", "true");
return doc.Root;
I was looking for an answer to this myself, and stumbled upon this article:
http://shevaspace.blogspot.com/2009/01/include-xml-declaration-in-wcf-restful.html
It solved the problem in my situation, perhaps it is of help for others as well. It describes a custom attribute IncludeXmlDeclaration that you can stick on your method, and it will output the xml header.
For completeness, here is the code copied from the article:
public class XmlDeclarationMessage : Message
{
private Message message;
public XmlDeclarationMessage(Message message)
{
this.message = message;
}
public override MessageHeaders Headers
{
get { return message.Headers; }
}
protected override void OnWriteBodyContents(System.Xml.XmlDictionaryWriter writer)
{
// WCF XML serialization doesn't support emitting XML DOCTYPE, you need to roll up your own here.
writer.WriteStartDocument();
message.WriteBodyContents(writer);
}
public override MessageProperties Properties
{
get { return message.Properties; }
}
public override MessageVersion Version
{
get { return message.Version; }
}
}
public class XmlDeclarationMessageFormatter : IDispatchMessageFormatter
{
private IDispatchMessageFormatter formatter;
public XmlDeclarationMessageFormatter(IDispatchMessageFormatter formatter)
{
this.formatter = formatter;
}
public void DeserializeRequest(Message message, object[] parameters)
{
formatter.DeserializeRequest(message, parameters);
}
public Message SerializeReply(MessageVersion messageVersion, Object[] parameters, Object result)
{
var message = formatter.SerializeReply(messageVersion, parameters, result);
return new XmlDeclarationMessage(message);
}
}
public class IncludeXmlDeclarationAttribute : Attribute, IOperationBehavior
{
public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
{
}
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
dispatchOperation.Formatter = new XmlDeclarationMessageFormatter(dispatchOperation.Formatter);
}
public void Validate(OperationDescription operationDescription)
{
}
}

Using IOperationBehavior to supply a WCF parameter

This is my first step into the world of stackoverflow, so apologies if I cock anything up.
I'm trying to create a WCF Operation which has a parameter that is not exposed to the outside world, but is instead automatically passed into the function.
So the world sees this: int Add(int a, int b)
But it is implemented as: int Add(object context, int a, int b)
Then, the context gets supplied by the system at run-time. The example I'm working with is completely artificial, but mimics something that I'm looking into in a real-world scenario.
I'm able to get close, but not quite the whole way there.
First off, I created a simple method and wrote an application to confirm it works. It does. It returns a + b and writes the context as a string to my debug. Yay.
[OperationContract]
int Add(object context, int a, int b);
I then wrote the following code:
public class SupplyContextAttribute : Attribute, IOperationBehavior
{
public void Validate(OperationDescription operationDescription)
{
if (!operationDescription.Messages.Any(m => m.Body.Parts.First().Name == "context"))
throw new FaultException("Parameter 'context' is missing.");
}
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
dispatchOperation.Invoker = new SupplyContextInvoker(dispatchOperation.Invoker);
}
public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
{
}
public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
{
// Remove the 'context' parameter from the inbound message
operationDescription.Messages[0].Body.Parts.RemoveAt(0);
}
}
public class SupplyContextInvoker : IOperationInvoker
{
readonly IOperationInvoker _invoker;
public SupplyContextInvoker(IOperationInvoker invoker)
{
_invoker = invoker;
}
public object[] AllocateInputs()
{
return _invoker.AllocateInputs().Skip(1).ToArray();
}
private object[] IntroduceContext(object[] inputs)
{
return new[] { "MyContext" }.Concat(inputs).ToArray();
}
public object Invoke(object instance, object[] inputs, out object[] outputs)
{
return _invoker.Invoke(instance, IntroduceContext(inputs), out outputs);
}
public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
{
return _invoker.InvokeBegin(instance, IntroduceContext(inputs), callback, state);
}
public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
{
return _invoker.InvokeEnd(instance, out outputs, result);
}
public bool IsSynchronous
{
get { return _invoker.IsSynchronous; }
}
}
And my WCF operation now looks like this:
[OperationContract, SupplyContext]
int Amend(object context, int a, int b);
My updated references no longer show the 'context' parameter, which is exactly what I want.
The trouble is that whenver I run the code, it gets past the AllocateInputs and then throws an Index was outside the bounds of the Array. error somewhere in the WCF guts.
I've tried other things, and I find that I can successfully change the type of the parameter and rename it and have my code work. But the moment I remove the parameter it falls over.
Can anyone give me some idea of how to get this to work (or if it can be done at all).
Well, I figured it out on my own. The MessagePartDescription has an Index property. I just need to resynch these values.
public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
{
var parts = operationDescription.Messages[0].Body.Parts;
parts.RemoveAt(0);
for (int i = 0; i < parts.Count; i++)
parts[i].Index = i;
}

where is the right place to add IOperationBehavior programmatically

how can i add IOperationBehavior programmatically when running on iis ?
not on custom wcf host.
thanks
Ali TAKAVCI
You could attach it as an attribute:
public class CustomInspectorAttribute : Attribute, IOperationBehavior, IParameterInspector
{
#region IOperationBehavior Members
public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
{
// Attribute could be used on client side
clientOperation.ParameterInspectors.Add(this);
}
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
// Attribute could be used on server side
dispatchOperation.ParameterInspectors.Add(this);
}
public void Validate(OperationDescription operationDescription)
{
}
#endregion
#region IParameterInspector Members
public void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState)
{
// Do something with returned values from operation
}
public object BeforeCall(string operationName, object[] inputs)
{
// Do something with incoming parameters before invoking actual operation
return null;
}
#endregion
}
And attach the attribute to an operation
[ServiceContract]
public interface ICustomServiceContract
{
[CustomInspector]
[OperationContract]
void MyOperation();
}
You need to build a custom service host, then set your .svc file to use it. In the custom service host you can do whatever you like to the service before it starts, including setting behaviours. Because you want to use operation behaviours you should do it in the OnOpening() method - as the service factory applies resets the operation behaviours after endpoint behaviours are configured. You will be able to iterate through the endpoints and the operations in OnOpening.