1
votes

Please help...I'm going crazy....I have a wcf service that exists on a few different servers. I need to dynamically change the endpoint address on my silverlight client depending on the environment its in. I'm currently getting a very detailed 404 error (sarcasm) when I try to change the address through code or by manually updating the client config file.

However, when I right-click on the service reference and go to configure service on my client I can change the address and it works.

I have the following service.

<system.serviceModel>
 <bindings>
  <basicHttpBinding>
    <binding name="DrawingServiceBasicHttp">
      <readerQuotas maxStringContentLength="2147483647" />
    </binding>
  </basicHttpBinding>
</bindings>

<service behaviorConfiguration="md" name="My.DrawingService">
    <endpoint address="Services" 
              binding="basicHttpBinding" 
              bindingConfiguration="DrawingServiceBasicHttp"
              name="DrawingServiceEndPoint" 
              contract="MyServices.IDrawingService" />
    <endpoint address="mex" 
              binding="mexHttpBinding" 
              bindingConfiguration=""
              name="DrawingMex" 
              contract="IMetadataExchange" />
 <behaviors>
  <serviceBehaviors>
    <behavior name="md">
      <serviceMetadata httpGetEnabled="true"/>
      <serviceDebug includeExceptionDetailInFaults="true"/>
    </behavior>
  </serviceBehaviors>
</behaviors>

My client config

<bindings>
        <basicHttpBinding>
            <binding name="DrawingServiceEndPoint" maxBufferSize="2147483647"
                maxReceivedMessageSize="2147483647">
                <security>
                    <transport>
                        <extendedProtectionPolicy policyEnforcement="Never" />
                    </transport>
                </security>
            </binding>
        </basicHttpBinding>
    </bindings>
    <client>
        <endpoint address="http://MyHostName/Services/DrawingService.svc/Services"
            binding="basicHttpBinding" bindingConfiguration="DrawingServiceEndPoint"
            contract="EvalDrawingService.IDrawingService" name="DrawingServiceEndPoint" />
    </client>

In Code trying to set the address:

EvalDrawingService.DrawingServiceClient client = new EvalDrawingService.DrawingServiceClient("DrawingServiceEndPoint", GetServiceAddress());

I have verified the address being spit out by GetServiceAddress() is there and that I can use the browser to verify it exists (not to mention I can connect to it using the wcftestclient).

The Exception:

{System.ServiceModel.CommunicationException: The remote server returned an error: NotFound. ---> System.Net.WebException: The remote server returned an error: NotFound. ---> System.Net.WebException: The remote server returned an error: NotFound. at System.Net.Browser.BrowserHttpWebRequest.InternalEndGetResponse(IAsyncResult asyncResult) at System.Net.Browser.BrowserHttpWebRequest.<>c_DisplayClass5.b_4(Object sendState) at System.Net.Browser.AsyncHelper.<>c_DisplayClass4.b_1(Object sendState) --- End of inner exception stack trace --- at System.Net.Browser.AsyncHelper.BeginOnUI(SendOrPostCallback beginMethod, Object state) at System.Net.Browser.BrowserHttpWebRequest.EndGetResponse(IAsyncResult asyncResult) at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelAsyncRequest.CompleteGetResponse(IAsyncResult result) --- End of inner exception stack trace --- at System.ServiceModel.AsyncResult.End[TAsyncResult](IAsyncResult result) at System.ServiceModel.Channels.ServiceChannel.SendAsyncResult.End(SendAsyncResult result) at System.ServiceModel.Channels.ServiceChannel.EndCall(String action, Object[] outs, IAsyncResult result) at System.ServiceModel.ClientBase1.ChannelBase1.EndInvoke(String methodName, Object[] args, IAsyncResult result) at EvaluaionAncillaryControl.EvalDrawingService.DrawingServiceClient.DrawingServiceClientChannel.EndGetEvalAreaDrawing(IAsyncResult result) at EvaluaionAncillaryControl.EvalDrawingService.DrawingServiceClient.EvaluaionAncillaryControl.EvalDrawingService.IDrawingService.EndGetEvalAreaDrawing(IAsyncResult result) at EvaluaionAncillaryControl.EvalDrawingService.DrawingServiceClient.OnEndGetEvalAreaDrawing(IAsyncResult result) at System.ServiceModel.ClientBase`1.OnAsyncCallCompleted(IAsyncResult result)}

2
what happened when you changed the endpoint address in config? that should work I believeRichard
@Richard - See my edit, thanks.AGoodDisplayName
Two suggestions - please post the exact error message and you might want to use Fiddler to look at the request that is failing.rboarman
@rboarman - Fiddler returns nothing but a 404 response. The exception indicates the same but I will post it per your request, thanks.AGoodDisplayName
Is the request making it to the correct server and endpoint? Does it look correctly formed with the right ip address? Double check that the address being returned from GetServiceAddress() is in the request to the server. Also, do you see any failing requests ahead of this one? Perhaps for CrossDomain.xml or ClientAccessPolicy.xml?rboarman

2 Answers

1
votes

(RESOLUTION) I was able to find the answer by observing what was being done in the first link in the answer from @rboarman. The link is to a blog writtent by Omar Al Zabir. http://omaralzabir.com/dynamically-set-wcf-endpoint-in-silverlight/

I used the below method:

public class DynamicEndpointHelper
{
// Put the development server site URL including the trailing slash
// This should be same as what's set in the Dropthings web project's 
// properties as the URL of the site in development server
private const string BaseUrl = "http://localhost:8000/Dropthings/";

public static string ResolveEndpointUrl(string endpointUrl, string xapPath)
{
    string baseUrl = xapPath.Substring(0, xapPath.IndexOf("ClientBin"));
    string relativeEndpointUrl = endpointUrl.Substring(BaseUrl.Length);
    string dynamicEndpointUrl = baseUrl + relativeEndpointUrl;
    return dynamicEndpointUrl;
}
}

and called it in this manner:

DynamicEndpointHelper.ResolveEndpointUrl(service.Endpoint.Address.Uri.ToString(), 
    App.Current.Host.Source.ToString()));

This allowed me to see that the proper way would have been to call use an address like:

http://MyServer/Services/MyService.svc/Services //This is what I specified it in the web.config

instead of just

http://MyServer/Services/MyService.svc/

I thought that the address "Services" in the service config was a relative address to my .svc file, but obviously I was wrong.

0
votes

I found these which looks promising:

http://omaralzabir.com/dynamically-set-wcf-endpoint-in-silverlight/

http://blogs.artinsoft.net/mrojas/archive/2011/03/23/dynamically-change-wcf-endpoint.aspx

Also, here's some code from my server project where I can change the end point on the fly using channels. I haven't tried it from Silverlight. it does work from the server side.

    /// <summary>
    /// This class contains utility methods related to invoking WCF services.
    /// http://msdn.microsoft.com/en-us/library/ms734681.aspx
    /// </summary>
    public static class ServiceInvoker
    {
        /// <summary>
        /// Alternative to the using statement to handle exceptions thrown by the Close method
        /// by calling the Abort method to ensure the transition to the Closed state.
        /// </summary>
        /// <param name="action">
        /// The action.
        /// </param>
        /// <typeparam name="TService">
        /// The service type.
        /// </typeparam>
        public static void UsingProxy<TService>(Action<TService> action)
            where TService : class, ICommunicationObject, IDisposable, new()
        {
            // create an instance of TService and invoke the action
            TService service = new TService();
            service.InvokeAction(action);
        }
        /// <summary>
        /// Alternative to the using statement to handle exceptions thrown by the Close method
        /// by calling the Abort method to ensure the transition to the Closed state.
        /// </summary>
        /// <param name="action">
        /// The action.
        /// </param>
        /// <typeparam name="TService">
        /// The service type.
        /// </typeparam>
        public static void UsingChannel<TService>(ChannelFactory<TService> channelFactory, Action<TService> action)
            where TService : class
        {
            // create an instance of TService and invoke the action
            TService service = channelFactory.CreateChannel();
            service.InvokeAction(action);
        }
        /// <summary>
        /// Alternative to the using statement to handle exceptions thrown by the Close method
        /// by calling the Abort method to ensure the transition to the Closed state.
        /// </summary>
        /// <param name="action">
        /// The action.
        /// </param>
        /// <typeparam name="TService">
        /// The service type.
        /// </typeparam>
        public static void UsingFactory<TService>(Action<TService> action)
            where TService : class
        {
            // TODO: cache the channel factory of TService
            ChannelFactory<TService> factory = new ChannelFactory<TService>("*");
            // create an instance of TService and invoke the action
            TService service = factory.CreateChannel();
            service.InvokeAction(action);
        }
        /// <summary>
        /// Invokes an action on a service then disposes the service channel.
        /// </summary>
        /// <typeparam name="TService">
        /// The service type.
        /// </typeparam>
        /// <param name="service">
        /// The service.
        /// </param>
        /// <param name="action">
        /// The action.
        /// </param>
        private static void InvokeAction<TService>(this TService service, Action<TService> action)
            where TService : class
        {
            try
            {
                // invoke action with service as its parameter
                action(service);
            }
            catch (Exception)
            {
                // todo: add logging here
                throw;
            }
            finally
            {
                // always close or abort the service channel
                ((ICommunicationObject)service).CloseOrAbort();
            }
        }
    }

Here's how I use it:

ServiceInvoker.UsingChannel<IMyProxy>(new ChannelFactory<IMyProxy>(new NetTcpBinding(), myServer.EndPointReference.Address), server =>
            {
                result = server.CallAMethod(passSomeData);
            });