WCF OutgoingMessageHeaders not saving values - wcf

I am trying to write a MessageHeader to the OutgoingMessageHeaders but the value isn't sticking.
BasicHttpBinding basicHttpBinding = new BasicHttpBinding();
EndpointAddress endpointAddress = new EndpointAddress("http://localhost:1003/Client.svc");
IClientService serviceClient = new ChannelFactory<IClientService>(basicHttpBinding, endpointAddress).CreateChannel();
// attempt 1
using (OperationContextScope scope = new OperationContextScope(serviceClient as IContextChannel))
{
OperationContext.Current.OutgoingMessageHeaders.Add(MessageHeader.CreateHeader("SessionId","","ABC"));
}
// attempt 2
using (OperationContextScope scope = new OperationContextScope(serviceClient as IContextChannel))
{
OperationContext.Current.OutgoingMessageHeaders.Add(MessageHeader.CreateHeader("SessionId","","ABC"));
}
OK, you can see that I am setting OutgoingMessageHeaders twice but that is simply to prove a point. In the second attempt, before I do the actual add, I inspect the OperationContext.Current.OutgoingMessageHeaders. I would expect this to have one entry. But it is zero. As soon as it gets out of the using scope the value is lost.
When this flows through to the server, it says it can't find the message header, indicating that as far as its concerned it hasn't been saved either.
Why isn't my MessageHeader sticking?
Jeff

The end of the using block calls the dispose and resets the previous OperationContext.
So you want something like this with the service call inside of the OperationContextScope.
using (OperationContextScope scope = new OperationContextScope(serviceClient as IContextChannel))
{
OperationContext.Current.OutgoingMessageHeaders.Add(MessageHeader.CreateHeader("SessionId", "", "ABC"));
serviceClient.CallOperation();
}

Related

Creating flow or model programmatically

I want to create a flow or model dynamically without using mule-config.xml for tcp with remote machines.
It should be something like this:
MuleContext context = new DefaultMuleContextFactory().createMuleContext();
MuleRegistry registry = context.getRegistry();
EndpointBuilder testEndpointBuilder = new EndpointURIEndpointBuilder("vm://testFlow.in",
context);
testEndpointBuilder.setExchangePattern(MessageExchangePattern.REQUEST_RESPONSE);
registry.registerEndpointBuilder("testFlow.in", testEndpointBuilder);
InboundEndpoint vmInboundEndpoint = testEndpointBuilder.buildInboundEndpoint();
registry.registerEndpoint(vmInboundEndpoint);
StringAppendTransformer stringAppendTransformer = new StringAppendTransformer(" world");
stringAppendTransformer.setMuleContext(context);
Flow testFlow = new Flow("testFlow", context);
testFlow.setMessageSource(vmInboundEndpoint);
testFlow.setMessageProcessors(Arrays.asList((MessageProcessor) stringAppendTransformer));
registry.registerFlowConstruct(testFlow);
context.start();
MuleClient muleClient = new MuleClient(context);
MuleMessage response = muleClient.send("vm://testFlow.in", "hello", null);
Validate.isTrue(response.getPayloadAsString().equals("hello world"));
muleClient.dispose();
context.stop();
Not sure if I understand your problem, but if you need a tcp outbound endpoint in your flow, you just create it similarly like the inbound vm endpoint in the example, but you then add it to a certain point in the flow in a list with all the processors with setMessageProcessors, like in the example where stringAppendTransformer is wrapped inside a list and added to the flow.
The code to create your tcp outbound would be something like this:
String address = "tcp://localhost:1234";
EndpointURIEndpointBuilder builder = new
EndpointURIEndpointBuilder(new URIBuilder(address), context);
builder.setExchangePattern(MessageExchangePattern.REQUEST_RESPONSE);
registry.registerEndpointBuilder("testFlow.out", builder);
OutboundEndpoint tcpOutboundEndpoint = builder.buildOutboundEndpoint();
registry.registerEndpoint(tcpOutboundEndpoint);
UPDATE regarding your new comment:
using a Java component:
//object factory for your Java class
PrototypeObjectFactory objectFactory = new PrototypeObjectFactory(MyClass.class);
objectFactory.initialise();
//the actual component
DefaultJavaComponent component = new DefaultJavaComponent(objectFactory);
//entry point resolver to determine the called method
EntryPointResolver resolver = new ExplicitMethodEntryPointResolver();
((ExplicitMethodEntryPointResolver)resolver).addMethod("myMethod");
component.setEntryPointResolvers(Arrays.asList(resolver));
Then add the component in the list like you add all the other processors

Passing values in Header

We are consuming an external web service (WCF) in our AX2012 project. We followed the procedure described in the following blog.
We are implementing security by passing the token in the header. However, what i am not sure of is how to do this in AX2012.
the sample code for getting the token is
static void myTestServiceWSDL(Args _args)
{
myServiceWSDL.Proxies.Service.ServiceClient service;
myServiceWSDL.Proxies.Service.LoginData LoginData;
str token;
System.Exception ex;
System.Type type;
try
{
type = CLRInterop::getType('myServiceWSDL.Proxies.Service.ServiceClient');
service = AifUtil::createServiceClient(type);
LoginData = new myServiceWSDL.Proxies.Service.LoginData();
LoginData.set_uName("test");
LoginData.set_pwd("test");
token=service.Login(LoginData);
info(token);
}
catch(Exception::CLRError)
{
ex = CLRInterop::getLastException();
info(CLRInterop::getAnyTypeForObject(ex.ToString()));
}
}
The token comes back fine which confirms the code is working.
Now the question is how to do i set header values for the message.
If it was C# i would have done
using (MemberMasterClient proxy = new MemberMasterClient())
{
using (OperationContextScope scope
= new OperationContextScope(proxy.InnerChannel))
{
// set the message in header
MessageHeader header =
MessageHeader.CreateHeader("SourceApplication",
"urn:spike.WCFHeaderExample:v1",
"WCFClient Application 2");
OperationContext.Current.OutgoingMessageHeaders.Add(header);
Console.WriteLine("Membership Details");
Console.WriteLine("Henry's - {0}", proxy.GetMembership("Henry"));
}
}
}
Could any one let me know how to do the equivalent in X++
One idea which has been on my mind is to write an assembly in C# which can then be called in AX2012. Will give that a go, but the idea is to code this in X++ in AX2012
The only thing you do differently in X++ is creating the proxy using the Aif utility. So basically, your C# example you listed, the only difference would be the proxy = new MemberMasterClient() which goes through AIF. All the other code you can take into X++ as-is (except for the "using"). You just need to have the right assemblies reference in the AOT, and use the full namespace in the code.
Alternatively, as you mentioned, you can just code it all in C# and call that from AX :-)

WCF - Passing CurrentPrincipal in the Header

I have a WCF service that needs to know the Principal of the calling user.
In the constructor of the service I have:
Principal = OperationContext.Current.IncomingMessageHeaders.GetHeader<MyPrincipal>("myPrincipal", "ns");
and in the calling code I have something like:
using (var factory = new ChannelFactory<IMyService>(localBinding, endpoint))
{
var proxy = factory.CreateChannel();
using (var scope = new OperationContextScope((IContextChannel)proxy))
{
var customHeader = MessageHeader.CreateHeader("myPrincipal", "ns", Thread.CurrentPrincipal);
OperationContext.Current.OutgoingMessageHeaders.Add(customHeader);
newList = proxy.CreateList();
}
}
This all works fine.
My question is, how can I avoid having to wrap all proxy method calls in the using (var scope...{ [create header and add to OperationContext]?
Could I create a custom ChannelFactory that will handle adding the myPrincipal header to the operation context? Something like that would save a whole load of copy/paste which I'd rather not do but I'm not sure how to achieve it:)
Thanks
The correct time to set a WCF principal is via IAuthorizationPolicy, by specifying a custom policy in configuration. This covered in full here. If you try setting the principal at other points (an inspector, perhaps) it can get reset by the system.

ChannelFactory Credentials + object is read only

Greetings, what is the problem that when I try to set credentials for my factory as follows:
ChannelFactory<IWCFSeekService> factory = Factory;
if (factory != null)
{
factory.Credentials.UserName.UserName = CServiceCredentials.Instance.Username;
_Channel = factory.CreateChannel();
}
I get an exception that object is read-only. It occurs when I want to set username.
Yes, the MSDN documentation is pretty clear:
C#
public ClientCredentials Credentials { get; }
The property only has a get accessor - no set accessor --> it's readonly.
Also in the MSDN docs:
Remarks
The ClientCredentials object is stored
as a type of endpoint behavior and can
be accessed through the Behaviors
property.
The OnOpened method initializes a
read-only copy of the
ClientCredentials object for the
factory.
So what is it you're doing to do here??
UPDATE: you cannot set the user credentials that your client proxy is supposed to use on the channel factory. See this excellent blog post on how to do it anyway - with a bit of a detour:
first, remove the default endpoint behavior from the factory
secondly, instantiate your own credentials
thirdly, set those new credentials as new endpoint behavior on factory
// step one - find and remove default endpoint behavior
var defaultCredentials = factory.Endpoint.Behaviors.Find<ClientCredentials>();
factory.Endpoint.Behaviors.Remove(defaultCredentials);
// step two - instantiate your credentials
ClientCredentials loginCredentials = new ClientCredentials();
loginCredentials.UserName.UserName = CServiceCredentials.Instance.Username;
loginCredentials.UserName.Password = “Password123″;
// step three - set that as new endpoint behavior on factory
factory.Endpoint.Behaviors.Add(loginCredentials); //add required ones
Seems a bit odd and complicated, but that seems to be the one and only way to achieve this!
To complete this answer, the actual way in which it worked for everyone as explained at
http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/4668e261-0fd0-4ca5-91d2-497aa479f2a9/
You need not to remove, but override found credentials:
var credentialBehaviour = factory.Endpoint.Behaviors.Find < ClientCredentials > ();
credentialBehaviour.UserName.UserName = "test";
credentialBehaviour.UserName.Password = "test";
This has solved my problem.
This will not happen if the service reference is added through -> Add service reference ->Advanced->Add Web Reference-> Url/wsdl (local disk file).
The reference.cs file generated is different and will allow you to set credentials.
The error is because you might have added the reference through first screen itself (Add service reference)

WCF: Why does passing in a remote endpoint fail?

The problem I am having connecting a wcf client application to a host running on a separate machine is documented in a question previously asked:
WCF: Why does passing in a remote endpoint fail?
However, the solution provided here says you need to use a SpnEndpointIdentity with an empty string. Since my code doesn't look anything like the case in the example I have referenced, I need to know what to do with the SpnEndpointIdentity object I have created.
I have a ChannelFactory upon which I call Create channel, passing in an EndpointAddress:
public override void InitialiseChannel()
{
SpnEndpointIdentity spnEndpointIdentity = new SpnEndpointIdentity("");
var address = new EndpointAddress(EndpointName);
Proxy = ChannelFactory.CreateChannel(address);
}
(NB: ChannelFactory is of type IChannelFactory, where T is the service contract interface)
So what do I do with spnEndpointIdentity? I can't pass it to CreateChannel.
Or perhaps I can use it somehow when I create the channel factory:
private ChannelFactory<T> CreateChannelFactory()
{
var binding = new NetTcpBinding
{
ReaderQuotas = { MaxArrayLength = 2147483647 },
MaxReceivedMessageSize = 2147483647
};
SpnEndpointIdentity spnEndpointIdentity = new SpnEndpointIdentity("");
var channelFactory = new ChannelFactory<T>(binding);
return channelFactory;
}
Again, I can't pass it into the constructor, so what do I do with it?
Thanks.
You almiost got it.
What you're missing is that you associate the EndpointIdentity with the EndpointAddress, and then provide that to CreateChannel():
SpnEndpointIdentity spnEndpointIdentity = new SpnEndpointIdentity("");
var address = new EndpointAddress(EndpointName, spnEndpointIdentity);