WCF Unique ID for each service method call - wcf

I'm logging using log4net, and I want to log a id that is unique for each serice method call. I dont need it unique across service calls, just within a method call. Is there any built in id i can use in wcf? I don't want to manually create a guid or something at the start of the method call.
e.g.
wcfMethod(int x)
{
log("xxx");
somework
log("yyy");
}
private log(string message)
{
var frame = new StackFrame(1);
var method = frame.GetMethod();
var type = method.DeclaringType;
var name = method.Name;
var log = LogManager.GetLogger(type);
// LOG ID HERE
ThreadContext.Properties["MessageId"] = OperationContext.Current.IncomingMessageHeaders.MessageId; // SOMETHING HERE
}
I've tried OperationContext.Current.IncomingMessageHeaders.MessageId but thats always null.
I've read about wcf instance correlation but i don't need something that complicated (e.g. unique across different method calls).
Please if anyone can help that would be much apprieciated. Thanks in advance.

Plain SOAP or REST has no such identification included in messages. You must use some additional feature or transport protocol (for example MSMQ) supporting identifications of messages. In case of MessageId you have to use SOAP service with WS-Addressing and this information must be passed from client.

Related

How to get the current TraceId and SpanId

This article, https://devblogs.microsoft.com/aspnet/improvements-in-net-core-3-0-for-troubleshooting-and-monitoring-distributed-apps/, tells me that the field TraceId is available as a correlation id, which is great!
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
=> ConnectionId:0HLR1BR0PL1CH
=> RequestPath:/weatherforecastproxy
RequestId:0HLR1BR0PL1CH:00000001,
SpanId:|363a800a-4cf070ad93fe3bd8.,
TraceId:363a800a-4cf070ad93fe3bd8,
ParentId: Executed endpoint 'FrontEndApp.Controllers.WeatherForecastProxyController.Get
(FrontEndApp)'
In fact, I can see that in our log sink this works as advertised: When web application A serves a request and in doing so invokes web application B, both of them write the same TraceId value to the log.
As far as I understand, any ASP.NET Core application that receives an incoming Request-Id header will attach the same header to outgoing requests, but if the header does not exist on the incoming request, an new value will be generated for the outgoing request.
We have been asked to add that value to the response from web application A, but it is (not surprisingly) not available on the incoming request.
I have been looking at the System.Diagnostics.Activity class, but accessing Activity.Current isn't giving me an instance with anything useful - the TraceID is just {} - i.e. empty.
My question is this: How can I access the TraceId value in the context of a web application?
-S
I had the same problem when I tried to add a header with TraceId value.
Doing some tests with ModelValidation, I saw then in this kind of error response the "traceId" value was correct, but I couldn't obtain this value from http context variable in any way.
Then I went to net core source code to see DefaultProblemDetailsFactory implementation and surprise! The "traceId" value is obtained doing this:
var traceId = Activity.Current?.Id ?? httpContext?.TraceIdentifier;
Yes, you can get THE traceId using Activity static variable.
You can get tracid and spanid in dictionary.
using var subject = _tracer.BuildSpan($"Operation").StartActive();
var spanContext = subject.Span.Context;
var dictionary = new Dictionary<string, string>();
_tracer.Inject(spanContext, BuiltinFormats.TextMap, new TextMapInjectAdapter(dictionary));

Custom Collection null when passed through WCF Service

We have some custom collections such as this:
[Serializable]
public class OccupationCollection : Collection<Occupation>
{
}
We use these in objects like the following:
private OccupationCollection _occupations;
public OccupationCollection CurrentOccupations
{
get
{
if (this._occupations == null)
return new OccupationCollection();
else
return _occupations;
}
}
Now we are making a call to a WCF service, passing objects that contain these type of lists. The lists always end up being null in the service.
I'm pretty sure this has somthing to do with serialization or something like that.
What would the simplest solution that would require minimal changes to the existing objects to get this to work?
Have you hosted your service over HTTP?
If yes, can you use fiddler to check the HTTP traffic and confirm whether serialized version of the parameter is being sent across the wire? If yes, there can be a parameter mismatch in contract between server and client.
Also is the object holding OccupationCollection decorated with Serializable/DataContract attribute? If you have DataContract attribute, ensure that the properties that need to be serialized are marked with Datamember attribute.
More details out here..
http://blog.functionalfun.net/2009/09/if-your-wcf-service-is-unexpectedly.html

how to set a property globally in wso2 ESB

I am trying to figure out how to implement session management in wso2 esb.So i have written a class mediator which generates session_ID that i want to store.For storing the session id I am using following code as:
org.apache.axis2.context.ServiceContext serviceContext = org.apache.axis2.context.MessageContext
.getCurrentMessageContext().getServiceContext();
serviceContext.setProperty("SessionIDGlobal", uuid);
But while running it in my esb's proxy it throws null pointer exception at getCurrentMessageContext part.I have followed another approach where-in i am storing the sessionID in property mediator and tried to get its value but when i click postRequest operation after generateSessionID operation from try-it. all the property gets reset and my sessionID property gives me null value. What should i do to rectify this problem? Is there any alternate way?
You have to create servicecontext like this;
ConfigurationContext cfgCtx =(((Axis2MessageContext) synCtx).getAxis2MessageContext(). getConfigurationContext();
cfgCtx.getOperationContext().getServiceContext();
You should store in the Message context but you have stored in the service Context. please refer this to understand how you can set the properties at different scopes. Synapase (default), Axis2, Transport etc.
Please refer this blog post for complete details.
http://blog.facilelogin.com/2011/02/wso2-esb-property-mediator-different.html

How to expose ErrorCode values?

I have web services written on WCF. I use request/response pattern and don't use FaultException. I return an error code in response contract as string. I need to expose error codes for clients in order to clients can handle exceptions. 
For example:
Var r = client.DoSomething();
Switch (r.ErrorCode)
{
Case ERROR_CODES.SomeCode:
//TODO:
}
Clients are WS-*, not only .Net.
UPDATE:
Sorry, my English is elementary. I've tried to explain in a different way.
When I use class File, I know that this class can throws some exceptions, for example, FileNotFoundException or DirectoryNotFoundException. If I create a File service How can I tell client that my service can returns "FileNotFound" or other error codes?
We generally try and use FaultContracts.
When we cannot we use a Response object that inherits from ResponseBase. ResponseBase has 2 properties, StatusCode and StatusMessage.
In your case ErrorCode, just add this property to your data contract.

WCF routing -- how to correctly add filter table programmatically

I am using the WCF 4 routing service, and need to configure the service programmatically (as opposed to via config). The examples I have seen of doing so, which are rare, create a MessageFilterTable as follows:
var filterTable=new MessageFilterTable<IEnumerable<ServiceEndpoint>>();
But, the generic parameter to that method is supposed to be TFilterData (the type of data you are filtering on)? I have my own custom filter that accepts a string -- can I still create the filter table this way?
If this will work...will the routing infrastructure create client endpoints out of the list I pass in?
I have created a WCF 4 routing service and configured it programmatically. My code is a bit more spaced out than it needs to be (maintainability for others being a concern, hence the comments), but it definitely works. This has two filters: one filters some specific Actions to a given endpoint, and the second sends the remaining actions to a generic endpoint.
// Create the message filter table used for routing messages
MessageFilterTable<IEnumerable<ServiceEndpoint>> filterTable = new MessageFilterTable<IEnumerable<ServiceEndpoint>>();
// If we're processing a subscribe or unsubscribe, send to the subscription endpoint
filterTable.Add(
new ActionMessageFilter(
"http://etcetcetc/ISubscription/Subscribe",
"http://etcetcetc/ISubscription/KeepAlive",
"http://etcetcetc/ISubscription/Unsubscribe"),
new List<ServiceEndpoint>()
{
new ServiceEndpoint(
new ContractDescription("ISubscription", "http://etcetcetc/"),
binding,
new EndpointAddress(String.Format("{0}{1}{2}", TCPPrefix, HostName, SubscriptionSuffix)))
},
HighRoutingPriority);
// Otherwise, send all other packets to the routing endpoint
MatchAllMessageFilter filter = new MatchAllMessageFilter();
filterTable.Add(
filter,
new List<ServiceEndpoint>()
{
new ServiceEndpoint(
new ContractDescription("IRouter", "http://etcetcetc/"),
binding,
new EndpointAddress(String.Format("{0}{1}{2}", TCPPrefix, HostName, RouterSuffix)))
},
LowRoutingPriority);
// Then attach the filter table as part of a RoutingBehaviour to the host
_routingHost.Description.Behaviors.Add(
new RoutingBehavior(new RoutingConfiguration(filterTable, false)));
You can find a good example on MSDN here: How To: Dynamic Update Routing Table
Note how they dont directly create an instance of the MessageFilterTable, but instead use the 'FilterTable' property provided by a new RoutingConfiguration instance.
If you have written a custom filter, then you will add it like this:
rc.FilterTable.Add(new CustomMessageFilter("customStringParameter"), new List<ServiceEndpoint> { physicalServiceEndpoint });
The CustomMessageFilter will be your filter, and the "customStringParameter" is the string that (I believe) you are talking about.
When the Router receives a connection request, it will attempt to map it via this table entry, if this is successful, then you are right, the router will create a client endpoint to talk to the ServiceEndpoint that you provided.