I have the following code:
public partial class MyServiceClient : System.ServiceModel.ClientBase<...
if (m_MyClient == null)
m_MyClient = new MyServiceClient
("BasicHttpBinding_IMyService", remoteAddress);
WriteOutput("Successfully connected to service");
My question is how do i know that my client actually connected to the service at this point? I would like to display a message of either failure or success.
When you've created the client, and no exception like EndpointNotFoundException has occured - then you are "connected" to the service which really means: the communication channel between the client and the service is ready to be used for sending messages back and forth. That's all there is - there's nothing on the server side yet to really handle your calls (except for the channel listener which will get activated if a message arrives).
You can also check the client channel's .State property - ideally, it should be Opened at that point:
Use this if you're deriving from ClientBase<T>
m_MyClient.State == CommunicationState.Opened
or this is you're using the standard client class generated by the Add Service Reference functionality in Visual Studio:
(m_MyClient as IClientChannel).State == CommunicationState.Opened
After realizing what i mentioned in my above comment, i realized the answer to my question was as follows:
In my ServiceContract i added the following:
[OperationContract]
bool IsAlive();
Whose implentation simply looks as follows:
public bool IsAlive()
{
return true;
}
Then changed my code as follows:
m_MyClient = new MyServiceClient("BasicHttpBinding_IMyService", remoteAddress);
try
{
m_MyClient.IsAlive();
}
catch (EndpointNotFoundException)
{
WriteOutput("Unable to connect to service");
m_MyClient = null;
}
if (m_MyClient != null)
WriteOutput("Successfully connected to service");
Related
I have a BizTalk WCF-Custom receive location to which I have added a custom behavior:
public class SasTokenProviderEndpointBehavior : BehaviorExtensionElement, IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
var tokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider(sharedAccessSecretName, sharedAccessKey);
bindingParameters.Add(new TransportClientEndpointBehavior { TokenProvider = tokenProvider });
}
}
}
parameter setup code omitted for brevity
This is adapted from a sample found at https://code.msdn.microsoft.com/How-to-integrate-BizTalk-07fada58#content - this author is widely respected in the BizTalk community and code of this kind has been in use for some years. All I am doing is adapting the method he uses, that is proven to work, to substitute a different TokenProvider.
I can see through debugging that this code runs and the TransportClientEndpointBehavior with correct parameters is added to the channel. However when the BizTalk receive location polls Service Bus, I see the following in the event log:
The adapter "WCF-Custom" raised an error message. Details "System.UnauthorizedAccessException: 40102: Missing authorization token, Resource:sb://[namespace].servicebus.windows.net/[queue]. TrackingId:452c2534-d3e6-400f-874f-09be324e9e11_G27, SystemTracker:[namespace].servicebus.windows.net:[queue], Timestamp:12/1/2016 11:38:56 AM ---> System.ServiceModel.FaultException: 40102: Missing authorization token, Resource:sb://[namespace].servicebus.windows.net/[queue]. TrackingId:452c2534-d3e6-400f-874f-09be324e9e11_G27, SystemTracker:[namespace].servicebus.windows.net:[queue], Timestamp:12/1/2016 11:38:56 AM
I cannot see any reason that the Azure Service Bus endpoint would return this error message except that because the token provider is not being used. Why would the channel ignore the TokenProvider and what do I have to do to pass the token correctly?
edit:
I have inspected the raw WCF message traffic for the port in question as well as one using the SB-Messaging adapter, which works as expected. The difference is that the SB-Messaging adapter's messages contain a SOAP header like:
<Authorization xmlns="http://schemas.microsoft.com/servicebus/2010/08/protocol/">SharedAccessSignature sr=[really long encoded string]</Authorization> and my custom binding port's messages do not. So it is true that the problem is a missing Authorization SOAP header; but the question persists - why isn't the channel adding this header?
edit #2:
I have decompiled Microsoft.ServiceBus.dll and I believe I've found the class that actually creates the WCF messsage, Microsoft.ServiceBus.Messaging.Sbmp.SbmpMessageCreator. It has this method:
private Message CreateWcfMessageInternal(string action, object body, bool includeToken, string parentLinkId, RetryPolicy policy, TrackingContext trackingContext, RequestInfo requestInfo)
{
Message message = Message.CreateMessage(this.messageVersion, action, body);
MessageHeaders headers = message.Headers;
headers.To = this.logicalAddress;
string sufficientClaims = this.GetSufficientClaims();
if (this.linkInfo != null)
{
if (!string.IsNullOrEmpty(this.linkInfo.TransferDestinationEntityAddress))
{
SecurityToken authorizationToken = this.GetAuthorizationToken(this.linkInfo.TransferDestinationEntityAddress, sufficientClaims);
if (authorizationToken != null)
{
SimpleWebSecurityToken webSecurityToken = (SimpleWebSecurityToken) authorizationToken;
if (webSecurityToken != null)
this.linkInfo.TransferDestinationAuthorizationToken = webSecurityToken.Token;
}
}
this.linkInfo.AddTo(headers);
}
if (includeToken)
{
ServiceBusAuthorizationHeader authorizationHeader = this.GetAuthorizationHeader(sufficientClaims);
if (authorizationHeader != null)
headers.Add((MessageHeader) authorizationHeader);
}
if (this.messagingFactory.FaultInjectionInfo != null)
this.messagingFactory.FaultInjectionInfo.AddToHeader(message);
if (!string.IsNullOrWhiteSpace(parentLinkId))
message.Properties["ParentLinkId"] = (object) parentLinkId;
if (trackingContext != null)
TrackingIdHeader.TryAddOrUpdate(headers, trackingContext.TrackingId);
MessageExtensionMethods.AddHeaderIfNotNull<RequestInfo>(message, "RequestInfo", "http://schemas.microsoft.com/netservices/2011/06/servicebus", requestInfo);
return message;
}
So thinking about it logically, there are two reasons the Authorization header would be missing:
includeToken is false (Why would this be so?)
GetAuthorizationHeader() returns null (Why?)
edit #3:
I have compiled and run the example code and this works. The only significant difference between my code and his is that mine includes a line which calls out to Azure Key Vault:
var kv = new KeyVaultClient(this.GetAccessToken);
var key = kv.GetSecretAsync(this.KeyVaultUri.AbsoluteUri, this.SharedAccessSecretName).Result;
var sharedAccessKey = key.Value;
var tokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider(
this.SharedAccessSecretName,
sharedAccessKey);
bindingParameters.Add(new TransportClientEndpointBehavior { TokenProvider = tokenProvider });
This is an asynchronous method that returns a Task. Can it be that blocking on the result of this Task somehow doesn't do what would be expected in certain situations, and this is messing up the configuration of the WCF channel somehow? As I said, I am certain this code runs and assigns the TokenProvider. I am now merely not certain when it runs.
D'OH!
I had neglected to realise that the very old version of Microsoft.ServiceBus.dll we still have in the solution for interop with the (equally old) on premises version of Service Bus (Service Bus for Windows Server) was the one referenced by my project. For whatever reason this version just doesn't do what it's supposed to, and doesn't give any indication that it's bypassing the intended behaviour. Updating to have the current NuGet package for Service Bus fixes the problem.
I am very new at programming WCF services, so I hope that if you answer my question - you will take that into account and explain it to me as if I was a kid (wcf services for dummies :). I have an existing WCF service which I need to connect to. I am supposed to make my own WCF service that will communicate with the existing one and share some request and response objects which are already defined in the existing service. Can anyone tell me how to do that (establish the communication between the two and use the same type of object in the service which I need to make as it is in the existing one), step by step? I have tried to find the answer online but it is all a bit confusing (referencing, using contracts...). As I said, you are free to explain as if you would to a real beginner. Any help is more than welcome...
"I am supposed to make my own WCF service that will communicate with the existing one and share some request and response objects which are already defined in the existing service." - This sounds like you need to create a client to connect to the service (see below how to create client). You can create WCF service to communicate with another service but you would need bit more background than this format allows.
You can get up to speed with WCF through WCF examples. Under WF_WCF_Samples\WCF\Basic in the examples you can find many Service/Client setups that you should go through first. MSDN Magazine has tons of articles on this topic.
In a 10,000 foot view of things:
Client - To consume service create a test console application. Add Service Reference in your project (when you right click references you will see that option). Point the address of the Service Reference dialog to the service you would like to consume and lot of stuff will happen. Final result is that you can call service methods on your service with something like below (where Service1 will be replaced with what ever service you are calling)
static void Main(string[] args)
{
var proxy = new ServiceReference1.Service1Client();
var test = proxy.GetData(1);
}
Service - you would create an interface with methods and types then decorate this interface with attributes for example:
[ServiceContract]
public interface IService1
{
[OperationContract]
string GetData(int value);
[OperationContract]
CompositeType GetDataUsingDataContract(CompositeType composite);
// TODO: Add your service operations here
}
These are operations (OperationContract) that your serive can perform. Service methods can return primitive or complex type (string vs. CompositeType) as well as take parameters that are complex or primitive.
You would implement this contract:
public class Service1 : IService1
{
public string GetData(int value)
{
throw new ApplicationException("Boom");
return string.Format("You entered: {0}", value);
}
public CompositeType GetDataUsingDataContract(CompositeType composite)
{
if (composite == null)
{
throw new ArgumentNullException("composite");
}
if (composite.BoolValue)
{
composite.StringValue += "Suffix";
}
return composite;
}
}
Next you need to host your service. You have many options to accomplish this depending on your hosting requirements. The simplest hosting you can do is using Console application:
class Program
{
static void Main(string[] args)
{
var host = new ServiceHost(typeof(Service1), new Uri("http://localhost:8999/"));
host.AddServiceEndpoint(typeof(IService1), new BasicHttpBinding(), "");
var metadataBehavior = host.Description.Behaviors.Find<ServiceMetadataBehavior>();
if (metadataBehavior == null)
{
metadataBehavior = new ServiceMetadataBehavior();
metadataBehavior.HttpGetEnabled = true;
host.Description.Behaviors.Add(metadataBehavior);
}
host.Open();
Console.WriteLine("Running..");
Console.ReadLine();
}
}
So I'm experiencing a strange error. I have a WP7 application that has a service reference to a WCF service I wrote. The WCF has an entity model for the database.
Basically what I want to happen, is when a user logs in on the phone, the matching SystemUser entry is returned from the WCF service. SystemUser is a table in the db.
On the WCF service side I have the following:
public SystemUser UserLogin(string emailAddress, string userPassword)
{
Regex emailRegex = new Regex(#"^([a-zA-Z0-9_\-\.]+)#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$");
if (emailRegex.IsMatch(emailAddress) && !string.IsNullOrEmpty(userPassword))
{
using (var context = new DBEntities())
{
var users = context.SystemUsers.Where(su => su.EmailAddress.ToLower().Trim() == emailAddress.ToLower().Trim());
// there should only be one user in here!
if (users.Count() <= 0)
{
return null;
}
else
{
return users.FirstOrDefault();
}
}
}
return null;
}
Nothing too hardcore. And when the phone calls that method, it throws an EndPointNotFoundException when trying to return the SystemUser object. "There was no endpoint listening at http://localhost:49676/Service1.svc that could accept the message. This is often caused by an incorrect address or SOAP action. See InnerException, if present, for more details." The inner exception is: "{"The remote server returned an error: NotFound."}"
The generated code that has this problem is here:
public ServiceProxy.UserLoginResponse EndUserLogin(System.IAsyncResult result) {
object[] _args = new object[0];
PhoneApp.ServiceProxy.UserLoginResponse _result = ((PhoneApp.ServiceProxy.UserLoginResponse)(base.EndInvoke("UserLogin", _args, result)));
return _result;
}
It basically looks like it cannot transport the entity? Any ideas? I thought I didn't need to use POCO objects?
Do you have [OperationContract] before Method Name?
http://localhost:49676/Service1.svc is working in browser?
Ok what I did to fix this issue is to right click on blank space in the edmx designer and choose "Add Code Generated Item". From there I picked ADO.NET Self-Tracking Entity Generator. Works beautifully now!
I'm trying to achieve the following:
whenever a call to service is performed I want to be able to identify the client.
I thought about getting the calling assembly name by iterating over stack trace
but I failed to get the client assembly name.
Sample code:
private List<System.Reflection.Assembly> GetCallingAssemblies()
{
List<System.Reflection.Assembly> assemblies = new List<System.Reflection.Assembly>();
StackTrace stackTrace = new StackTrace(0, true);
for (int i = 0; i < stackTrace.FrameCount; i++)
{
StackFrame stackFrame = stackTrace.GetFrame(i);
System.Reflection.MethodBase methodBase = stackFrame.GetMethod();
Type type = methodBase.ReflectedType;
System.Reflection.Assembly assembly;
if (type != null)
{
assembly = System.Reflection.Assembly.GetAssembly(type);
if (assemblies.Contains(assembly) == false)
{
assemblies.Add(assembly);
}
}
}
return assemblies;
}
I must be missing something: you're trying to identify the client through assemblies? Why not use authentication?
Besides, who says the client even has assemblies? It may be a Java client, or some other platform.
When your client calls a WCF service, all that goes between the two is the serialized message - the method to call and all the parameters to pass in.
There is no other connection at runtime between server and client. The server cannot "reach back" and look at the client - there is no connection.
All your service can look at is the serialized message, and any message headers. So if you really really need this (what do you need it for??) then you need to make sure the client puts a marker / identification of some sort as a message header into the call.
How can I implement one way WCF operations?
I just tried using IsOneWay attribute as:
[OperationContract(IsOneWay=true)]
void MethodName(string param1, int param2)
Is there any other change I need to make or any specific change in app.config?
FYI, my WCF service implements netTcpBinding, though I think that shouldn't make any difference.
As shown, your code looks ok. There should be no problem with doing one-way calls with netTcpBinding.
If you're interested, chapter 5 in Juval Lowy's awesome Programming WCF Services 2nd Edition contains a good bit of information about one-way services.
From what you've shown, so far though I don't see anything wrong. Please give us some more details.
We had a problem with one-way calls not returning immediately using the NetTcpBinding. This blog post identifies the problem and provides a solution.
http://blogs.msdn.com/b/distributedservices/archive/2009/02/12/client-proxy-close-method-call-does-not-finish-immediately-in-one-way-wcf-calls.aspx
From the article:
Problem: Clients calling a one-way method in WCF Service and then close method on proxy does not return until the call is actually finished or call times out. Ever wonder why this happens?
Cause: When you specify “One-Way” on your interface, the underlying channel operation is still two-way since the one way binding element is not in the channel stack. Thus, the close operation gets blocked until the one way operation completes.
This is by design and the development team is working to change it in future versions of .Net framework.
...
Solution (Work around):
Layer the OneWayBindingElement on top of netTcpBinding as shown in the below code. This way, close call on proxy will return immediately and eventually the one-way call will return in fire and forget fashion.
[ServiceContract]
public interface IService1
{
[OperationContract(IsOneWay = true)]
void SetData(int value);
}
public class Service1 : IService1
{
public void SetData(int value)
{
//Application specific code
}
}
Service Host code:
Form1ServiceHost = new ServiceHost(this, new Uri("net.tcp://localhost:8091/WindowsFormApp/Form1/"), new Uri("http://localhost:8090/WindowsFormApp/Form1/"));
Binding binding = new NetTcpBinding();
BindingElementCollection oldBindingElements = binding.CreateBindingElements();
BindingElementCollection bindingElements = new BindingElementCollection();
bindingElements.Add(new OneWayBindingElement());
foreach (BindingElement bindingElement in oldBindingElements)
{
bindingElements.Add(bindingElement);
}
binding = new CustomBinding(bindingElements);
Form1ServiceHost.AddServiceEndpoint("WCFServiceLibrary.IService1", binding, "");
Form1ServiceHost.Open();
Client Code:
Binding binding = new NetTcpBinding();
BindingElementCollection oldBindingElements = binding.CreateBindingElements();
BindingElementCollection bindingElements = new BindingElementCollection();
bindingElements.Add(new OneWayBindingElement());
foreach (BindingElement bindingElement in oldBindingElements)
{
bindingElements.Add(bindingElement);
}
binding = new CustomBinding(bindingElements);
Service1Client client = new Service1Client(binding, new EndpointAddress("net.tcp://localhost:8091/WindowsFormApp/Form1/"));
client.SetData(10);
Console.WriteLine("set data");
Console.WriteLine("Now closing the channel,Before close, current time is {0}", DateTime.Now.ToString() + " " + DateTime.Now.Millisecond.ToString());
client.Close();
Console.WriteLine("Now closing the channel,After close, current time is {0}", DateTime.Now.ToString() + " " + DateTime.Now.Millisecond.ToString());`