开发者

Can't call my service method

开发者 https://www.devze.com 2023-04-08 20:36 出处:网络
I\'m getting this error: The communication object, System.ServiceModel.ChannelFactory`1[FxCurveService.IFxCurveService],

I'm getting this error:

The communication object, System.ServiceModel.ChannelFactory`1[FxCurveService.IFxCurveService], cannot be used for communication because it is in the Faulted state.

When I call this code:

using (var client = new WCFServiceChannelFactory<IFxCurveService>(new Uri("http://ksqcoreapp64int:5025/")))
                {
                    guid = client.Call(svc => svc.ReserveSnapshot(fxCurveKey));
                    DiscountFactorNew[] dfs = client.Call(svc => svc.GetDiscountFactors(guid, dates, from));
                    Assert.IsTrue(guid != null);
                }

It errors here - client.Call(svc => svc.ReserveSnapshot(fxCurveKey));

I have no idea why it is doing this. I am passing the right parameters, inputting the correct address for the service, what else should I be checking here?

Btw, WCFServiceChannelFactory is our own class we use to take care of making service calls. Outline here:

public class WCFServiceChannelFactory<T> : IDisposable
    {
        public WCFServiceChannelFactory();
        public WC开发者_开发知识库FServiceChannelFactory(Uri uri);

        public T Channel { get; }
        public System.ServiceModel.ChannelFactory<T> ChannelFactory { get; }
        public Type ChannelType { get; }

        public void Call(Action<T> f);
        public R Call<R>(Func<T, R> f);
        public void Dispose();
    }

The thing is, the problem is not with this, as this is working in the same exact fashion in every other project but this one. Basically, I have to pass the Uri directly in mine, where as others derive it from a .config file in the project, which I was unable to do here. That's the only difference.

Thanks.


You can't access details of the exception if the channel is disposed. So the lovely using pattern construction is not recommended when accessing a WCF service. In fact, the Exception properties requires to have access to the channel to extract some information about the exception (don't know if MS missed that point, or if there are technical reasons behind).

I've written a small class to simplify the call to WCF proxies (this site helps me to understand the problem and to write the class) :

using System;
using System.ServiceModel;

namespace Utility
{
    public class ServiceHelper
    {

        /// <summary>
        /// WCF proxys do not clean up properly if they throw an exception. This method ensures that the service 
        /// proxy is handeled correctly. Do not call TService.Close() or TService.Abort() within the action lambda.
        /// </summary>
        /// <typeparam name="TService">The type of the service to use</typeparam>
        /// <param name="action">Lambda of the action to performwith the service</param>
        [System.Diagnostics.DebuggerStepThrough]
        public static void UsingProxy<TService>(Action<TService> action)
            where TService : ICommunicationObject, IDisposable, new()
        {
            var service = new TService();
            bool success = false;
            try
            {
                action(service);
                if (service.State != CommunicationState.Faulted)
                {
                    service.Close();
                    success = true;
                }
            }
            finally
            {
                if (!success)
                {
                    service.Abort();
                }
            }
        }
        /// <summary>
        /// WCF proxys do not clean up properly if they throw an exception. This method ensures that the service 
        /// proxy is handeled correctly. Do not call TService.Close() or TService.Abort() within the action lambda.
        /// </summary>
        /// <typeparam name="TIServiceContract">The type of the service contract to use</typeparam>
        /// <param name="action">Action to perform with the client instance.</param>
        /// <remarks>In the configuration, an endpoint with names that maches the <typeparamref name="TIServiceContract"/> name
        /// must exists. Otherwise, use <see cref="UsingContract&lt;TIServiceContract&gt;(string endpointName, Action<TIServiceContract> action)"/>. </remarks>
        [System.Diagnostics.DebuggerStepThrough]
        public static void UsingContract<TIServiceContract>(Action<TIServiceContract> action)
        {
            UsingContract<TIServiceContract>(
                typeof(TIServiceContract).Name,
                action
                );
        }
        /// <summary>
        /// WCF proxys do not clean up properly if they throw an exception. This method ensures that the service 
        /// proxy is handeled correctly. Do not call TService.Close() or TService.Abort() within the action lambda.
        /// </summary>
        /// <typeparam name="TIServiceContract">The type of the service contract to use</typeparam>
        /// <param name="action">Action to perform with the client instance.</param>
        /// <param name="endpointName">Name of the endpoint to use</param>
        [System.Diagnostics.DebuggerStepThrough]
        public static void UsingContract<TIServiceContract>(
              string endpointName,
              Action<TIServiceContract> action)
        {
            var cf = new ChannelFactory<TIServiceContract>(endpointName);
            var channel = cf.CreateChannel();
            var clientChannel = (IClientChannel)channel;

            bool success = false;
            try
            {
                action(channel);
                if (clientChannel.State != CommunicationState.Faulted)
                {
                    clientChannel.Close();
                    success = true;
                }
            }
            finally
            {
                if (!success) clientChannel.Abort();
            }
        }
    }    
}

Then you can simply do something like this (depending if you have a service reference or the contracts :

ServiceHelper.UsingContract<IFxCurveService>(svc=>
            {
                guid = svc.ReserveSnapshot(fxCurveKey);
                DiscountFactorNew[] dfs = svc.GetDiscountFactors(guid, dates, from));
                Assert.IsTrue(guid != null);
            }),

This helpers ensure the correct closing of channels, whithout disposing it. You will be able to see the actual exception then. Edit your post when you'll find the actual exception.

(Maybe your service factory is already using this technique. If not, do not hesitate to update it like my class).

[edit] You still have to play with config. here is a probably working config for you :

        contract="The.Correct.Namespace.IFxCurveService"
        name="IFxCurveService" />


I am using 4.0 and i have configured it like as follow:

<system.serviceModel>
    <bindings>
      <webHttpBinding>
        <binding name="defaultBasicHttpBinding">
          <security mode="Transport">
            <transport clientCredentialType="None" proxyCredentialType="None"/>
            <!--<message clientCredentialType="Certificate" algorithmSuite="Default" />-->
          </security>
        </binding>
      </webHttpBinding>
    </bindings>

    <client>
      <endpoint address="https://abc1234.abc.nsroot.net/MyService/MyService.svc"
                binding="webHttpBinding"
                bindingConfiguration="defaultBasicHttpBinding"
                contract="IMyService"
                name="TestJeph"/>
    </client>
  </system.serviceModel>

FYI: I am calling the WCF Rest service, does it matter?

the following is my service interface that i have created in the web app solution:

namespace anothertest
{
    using System;
    using System.ServiceModel;


    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
    [System.ServiceModel.ServiceContractAttribute(ConfigurationName = "IMyService")]
    public interface IMyService
    {

        [OperationContract]
        test.WebService.Entity.BusinessEntity[] GetAllActiveBusiness();
    }

 public class ProductClient : ClientBase<IMyService>, IMyService
        {
            #region Members

            public test.WebService.Entity.BusinessEntity[] GetAllActiveBusiness()
            {
                return Channel.GetAllActiveBusiness();
            }

            #endregion
        }
}

the following is the code to call the service:

anothertest.Utility.ServiceHelper.UsingContract<anothertest.IMyService>
            ("TestJeph",
            svc=>
            {
                string test = svc.UpdateCMPStatus("test", "me");
            }); 


Are you targeting .Net 3.5 or .Net 4.0 ? In .Net 4.0 you get lots of defaults for the configuration of the service. In .Net 3.5 you are going to have to configure the endpoint completely in either in the App.config or programatically. For example what binding is the enpoint using ? If you are using .Net 4.0 then you are going to get a BasicHttpBinding by default since you specified an http uri. In .Net 3.5 you are going to fault as there will be no binding configured.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号