Somethings broken....

Apr 24, 2013 at 12:02 AM
I have to believe I just missed one little setting somewhere....
I have 2 separate projects - one for host, one for client. I am running the host in debug mode and can access the data via the Development WCF Service Host form.

I try to run the client, but it gets errors and can't get the data. The errors happen in the GetInternalNetTcpChannel method. It creates the proxy using the NetTcpBinding and an endpoint of "net.tcp://localhost:50000/dev/ICustomersService" - My app.config matches this (<add key="ServicePort:ICustomersService" value="50000"/>). And when I look at the Host form, it shows that the base port is 50000 and the Net.Tcp (and others) service is started.

The channel is not null but BeforeChannelOpens is, so the next block is skipped. But on the channelOpen() statement, I get the following error:

"There was no endpoint listening at net.tcp://localhost:50000/dev/ICustomersService that could accept the message. This is often caused by an incorrect address or SOAP action."
  • The inner exception is null.
  • The spelling of ICustomersService is spelled the same.
  • Both the host and client projects use the same contracts project.
  • This is initiated by the LoadData() method of my CustomerListViewModel class which looks like this:
    AsyncWorker.Execute(() =>
    {
    var response = new GetAllCustomersResponse();
    ServiceClient.Call<ICustomersService>(service => response = service.GetAllCustomers(new GetAllCustomersRequest
    {
    } ));
    return response;
    }, ....
  • And the ICustomersService service contract is:
    GetAllCustomersResponse GetAllCustomers(GetAllCustomersRequest request);
But, as mentioned, if I go back to the Development WCF Service Host form and double click any of the services, they all seem to work.

Any idea where I am missing something?
Apr 24, 2013 at 10:23 PM
Ok, I found that I need to set the ServicePort: for the service to the correct value. So if the first service is on 50000, the 2nd should be 50001, etc. This is assuming that the ServiceBasePort is set to the default (50000).

Now I can't find out where I specify the Port to use on the host side (other than the starting port). Is it just based on the order that they are added to the host via the host.AddServiceHostXXX() call? I don't see a Port or sequence attribute I can use.

So if I have 3 services and I don't want to break existing code, I have to make sure I add the new services after the existing ones or all the app.config files will need to be updated... Is this true?

Thanks
Coordinator
Apr 26, 2013 at 5:39 PM
On the host side, you can just define the port for the first service you are hosting and it counts up from there. So using this mechanism depends on keeping the sequence the same, yes. And once a service is published into the wild, you should stick with the same port, because that port is part of the address. So changing it, is just like any other change to the URL you are making.

You could of course always go out of your way to explicitly force a certain port for a certain service if you wanted, but letting it pick the sequence is typically more convenient.

Markus
May 2, 2013 at 7:34 PM
Edited May 2, 2013 at 7:39 PM
Markus,

Ok, so the host default port is 50000 - which is fine. I assume that the remaining ports are incremented in the order they are called, also fine.

On the client, I believe that I have to specify the port in the app.config for each service. So I might have something like:
<add key="ServicePort:ICustomersService" value="50000"/>
<add key="ServicePort:ICategoriesService" value="50001"/>
<add key="ServicePort:IEmployeesService" value="50002"/>
Is this true? And, if so, then how can I do a test to see if the port is set right on the client? Right now, if the port is wrong, I get an error.

If the above assumptions are correct, would it be possible to add a simple test to the FW that could be run after the shell is displayed (preferably by an async worker) that would be able to check all the port settings and alert the user (or developer) if one or more does not appear to be working? I figured I could write something, but would need much more code as I would have to simulate a model for each app in order to call the default CODE FW connection code.

I was thinking that the FW could just add a default test ping type call that can be checked by a client - without having to know any of the other details about the connection.

And this might be used to help with the issue addressed by discussions: 442026

Or am I just missing something obvious?

Thanks
Coordinator
May 2, 2013 at 8:42 PM
There is no mechanism to just test whether a port is right other than making a call. I often add a simple Ping service to a service contact. Just so I know the service is there.

Markus
May 3, 2013 at 1:56 AM
Markus,

If I wanted to add the ping, I would do the following:
1) Add a "ping" type method to the contract This might simply return the name of the service.
2) Add a method to the implementation of the contact on the host side
3) Create a "test" class on the client side that I can instantiate and have it go through and test all the expected services and then alert the user, developer, and/or update any configurations settings as necessary.
4) Return a flag that indicates if the services are functioning or not.

1,2, and 4 are relatively easy. Right now, my understanding of writing #3 is the little I have learned by creating models. In these cases, I know there is a ton of code happening in the FW. Is there a significantly reduced version of that code I can use to just do the ping test?

I am guessing I could instantiate an instance of a class based on the ViewModel and in that, have a method call similar to:
public void ValidateServices()
{
AsyncWorker.Execute(() =>
{
serviceWorking = TestAllServices();
},{},this);
}

public bool TestAllServices(){
string XxxxxxValid = string.Empty;
string YxxxxxValid = string.Empty;
string ZxxxxxValid = string.Empty;
try
{
XxxxxxValid = ServiceClient.Call<IXxxxxxxService>(service => serviceWorking = service.Test() );
YxxxxxValid = ServiceClient.Call<IYxxxxxxService>(service => serviceWorking = service.Test() );
ZxxxxxValid = ServiceClient.Call<IZxxxxxxService>(service => serviceWorking = service.Test() );
} catch
{
}
// Now do something if any of the above strings are blank or contain the name of a wrong service.
}

Since the number of service calls could increase to a fairly large amount, I would want to have one place to do the tests.

My hope is that if any of them fail, I can programmatically check the configurations settings, user values, etc. and possibly fix the problem without bothering the user - especially if it's just a matter of using a different port.

My problem is that I am still very naïve in this area and not sure if I am making this more complicated than it needs or not realizing some of the complexities of the issu.

Essentially, if a host is updated, I want my clients to still work, even if the service they expected on port 50045 is now on 50046 - without them having to do anything.
or
If the default host is not available, change the settings and see if I can reach a local table to work with that until I am again able to reach the main system. (of course, then I also will need a way to replicate the changes made to the local db to the main db on the network - but I think (hope) that should be fairly straightforward.

Thanks
Coordinator
May 3, 2013 at 5:42 AM
You only have to do one Ping call per service contract. So maybe you have something like this:
[ServiceContract]
public interface ICustomerService
{
    [OperationContract]
    PingResponse Ping(PingRequest request);

    [OperationContract]
    GetCustomersResponse GetCustomers(GerCustomersRequest request);

    // Lots of more methods to go here.
}
When I do a ping service, my request and response contracts look like this, btw:
[DataContract]
public class PingRequest
{
    [DataMember(IsRequired=true)]
    public DateTime ClientTimeStamp { get; set; }
}

[DataContract]
public class PingResponse
{
    [DataMember(IsRequired=true)]
    public bool Success { get; set; }

    [DataMember(IsRequired=true)]
    public string FailureInformation { get; set; }

    [DataMember(IsRequired=true)]
    public DateTime ServerTimeStamp { get; set; }
}
I often find it useful to include dates and times so you can see how long things took and whether or not the server and client time match. But anyway: Those are just details on the side.

You can add the Ping() method to each service. Even a large contract will not have a huge number of service contracts. Each may have quite a few methods, yes, but services are generally organized by area of functionality. So all customer related stuff is in one service contract, and so on. How many such areas could even a large app have? 20? 30? That would be a lot I would think. But even if it is twice that, it's still not that big a deal. Most apps I see have less than a dozen, actually.

So now you can add your service ping to some test method if you want. If the service call doesn't go through, you may want to alert the developer (if in debug mode), or show an error message for the user, or switch the app into offline mode, or whatever makes sense for the specific app. Now the one thing you can't do is tell the developer what needs to be changed in the config file. After all, the client doesn't know what the server expects, if the call failed. It's like going to a URL with a web browser. If that call fails, what is the browser going to say? Similarly, there is really no way for the service client to know what port to use. At that point, the developer just has to do some detective work themselves. We try to make that as easy as possible by showing the exact URLs that are available in the development host (which includes the port for TCP/IP based services).


Markus