在WCF中更改函数的返回类型,而无需更改接口返回类型

时间:2018-12-24 16:52:30

标签: c# wcf

我正在使用旧的WCF服务,该服务具有用于新系统的许多接口和服务。我想在不更改所有服务接口和实现的情况下更改函数的返回类型,如下所示:

interface OperationResult
{
    ErrorInfo Error { get; set; }
}
interface OperationResult<TResult> : OperationResult
{
    TResult Result { get; set; }
}

// old service
interface IService
{
    int TestMethod1(TestMethod1Input input);
    void TestMethod2(TestMethod2Input input);
}
// Interface that client should see
interface IService
{
    OperationResult<int> TestMethod1(TestMethod1Input input);
    OperationResult TestMethod2(TestMethod2Input input);
}

我认为我可以使用IOperationInvoker处理异常,但是我不知道如何更改实际服务的返回值,我想使用IWsdlExportExtension来更改WSDL中函数的返回类型。但是我找不到任何适用的文档或样本。

任何人都可以提出任何示例或文档或其他任何方式来节省更改太多现有服务的麻烦吗?

注意:,我还有另一种方法:创建一个自定义ServiceHost,该自定义ServiceHost为实际服务创建动态包装,并将其作为服务类型传递给var date = Date var timestamp = Timestamp with timezone if(date == timestamp){ console.log("Are the same") } 的构造函数。但这应该是最后的解决方案,因为它将生成许多动态类型。

4 个答案:

答案 0 :(得分:6)

也许您可以考虑使用IDataContractSurrogate

它有三种与序列化有关的方法。

  • GetDataContractType用于获取要序列化或反序列化的类型,或获取要导出和导入的数据合同。
  • GetObjectToSerialize用于在序列化对象之前获取要序列化的对象。
  • GetDeserializedObject用于获取已序列化的对象。

https://docs.microsoft.com/en-us/dotnet/framework/wcf/extending/data-contract-surrogates

答案 1 :(得分:1)

首先,如果您的返回类型是原始类型,那么我认为您不能动态更改类型。我的近似值在上面:

  • 我的客户类

        var client = new ServiceReference1.Service1Client();
    
        WcfService1.OperationResult<int> resultOk1 = client.TestMethod1(new WcfService1.TestMethod1Input { Throws = false });
        WcfService1.OperationResult<string> resultOk2 = client.TestMethod2(new WcfService1.TestMethod2Input { Throws = false });
    
        WcfService1.IOperationResult resultKo1;
        WcfService1.OperationResult resultKo2;
        try
        {
            resultOk1 = client.TestMethod1(new WcfService1.TestMethod1Input { Throws = true });
        }
        catch (FaultException<WcfService1.OperationResult<int>> ex)
        {
            resultKo1 = ex.Detail;
        }
    
        try
        {
            resultOk2 = client.TestMethod2(new WcfService1.TestMethod2Input { Throws = true });
        }
        catch (FaultException<WcfService1.OperationResult<string>> ex)
        {
            resultKo2 = ex.Detail;
        }
    
  • 我的服务

        [ErrorHandlerBehavior]
        public class Service1 : IService1
        {
           public TestMethod1Ouput TestMethod1(TestMethod1Input input)
           {
               if (input.Throws)
               {
                   throw new Exception("a error message 1");
               }
               return new TestMethod1Ouput { OrginalResult = 123 };
           }
    
           public TestMethod2Ouput TestMethod2(TestMethod2Input input)
           {
               if (input.Throws)
               {
                   throw new Exception("a error message 2");
               }
               return new TestMethod2Ouput { OrginalResult = "?"};
           }
       }
    
       [ServiceContract]
       [DataContractOperationResult]
       public interface IService1
       {
           [OperationContract]
           [FaultContract(typeof(OperationResult<int>))]
           TestMethod1Ouput TestMethod1(TestMethod1Input input);
    
           [OperationContract]
           [FaultContract(typeof(OperationResult<string>))]
           TestMethod2Ouput TestMethod2(TestMethod2Input input);
       }
    
       public interface IOperationResult
       {
           string Error { get; set; }
       }
    
       public interface IOperationResult<TResult> : IOperationResult
      {
           TResult Result { get; set; }
       }
    
       [DataContract]
       public class OperationResult : IOperationResult
       {
           [DataMember(Name = "Error")]
           public string Error { get; set; }
       }
    
       [DataContract]
       public class OperationResult<TResult> : OperationResult, IOperationResult<TResult>, IOperationResult
       {
           [DataMember(Name = "Result")]
           public TResult Result { get; set; }
       }
    
       public class TestMethod1Ouput
       {
           public int OrginalResult { get; set; }
       }
    
       public class TestMethod1Input
       {
           public bool Throws { get; set; }
       }
    
       public class TestMethod2Ouput
       {
           public string OrginalResult { get; set; }
       }
    
       public class TestMethod2Input
       {
           public bool Throws { get; set; }
       }
    
  • 更改成功响应的类:

      public sealed class DataContractOperationResultAttribute : Attribute, IContractBehavior, IOperationBehavior, IWsdlExportExtension, IDataContractSurrogate
       {
           #region IContractBehavior Members
    
           public void AddBindingParameters(ContractDescription description, ServiceEndpoint endpoint, BindingParameterCollection parameters)
           {
           }
    
           public void ApplyClientBehavior(ContractDescription description, ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime proxy)
           {
               foreach (OperationDescription opDesc in description.Operations)
               {
            ApplyDataContractSurrogate(opDesc);
               }
           }
    
    public void ApplyDispatchBehavior(ContractDescription description, ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.DispatchRuntime dispatch)
    {
        foreach (OperationDescription opDesc in description.Operations)
        {
            ApplyDataContractSurrogate(opDesc);
        }
    }
    
    public void Validate(ContractDescription description, ServiceEndpoint endpoint)
    {
    }
    
    #endregion
    
    #region IWsdlExportExtension Members
    
    public void ExportContract(WsdlExporter exporter, WsdlContractConversionContext context)
    {
        if (exporter == null)
            throw new ArgumentNullException("exporter");
    
        object dataContractExporter;
        XsdDataContractExporter xsdDCExporter;
        if (!exporter.State.TryGetValue(typeof(XsdDataContractExporter), out dataContractExporter))
        {
            xsdDCExporter = new XsdDataContractExporter(exporter.GeneratedXmlSchemas);
            exporter.State.Add(typeof(XsdDataContractExporter), xsdDCExporter);
        }
        else
        {
            xsdDCExporter = (XsdDataContractExporter)dataContractExporter;
        }
        if (xsdDCExporter.Options == null)
            xsdDCExporter.Options = new ExportOptions();
    
        if (xsdDCExporter.Options.DataContractSurrogate == null)
            xsdDCExporter.Options.DataContractSurrogate = new DataContractOperationResultAttribute();
    }
    
    public void ExportEndpoint(WsdlExporter exporter, WsdlEndpointConversionContext context)
    {
    }
    
    #endregion
    
    #region IOperationBehavior Members
    
    public void AddBindingParameters(OperationDescription description, BindingParameterCollection parameters)
    {
    }
    
    public void ApplyClientBehavior(OperationDescription description, System.ServiceModel.Dispatcher.ClientOperation proxy)
    {
        ApplyDataContractSurrogate(description);
    }
    
    public void ApplyDispatchBehavior(OperationDescription description, System.ServiceModel.Dispatcher.DispatchOperation dispatch)
    {
        ApplyDataContractSurrogate(description);
    }
    
    public void Validate(OperationDescription description)
    {
    }
    
    #endregion
    
    private static void ApplyDataContractSurrogate(OperationDescription description)
    {
        DataContractSerializerOperationBehavior dcsOperationBehavior = description.Behaviors.Find<DataContractSerializerOperationBehavior>();
        if (dcsOperationBehavior != null)
        {
            if (dcsOperationBehavior.DataContractSurrogate == null)
                dcsOperationBehavior.DataContractSurrogate = new DataContractOperationResultAttribute();
        }
    }
    
    #region IDataContractSurrogate Members
    
    public Type GetDataContractType(Type type)
    {
        // This method is called during serialization and schema export
        System.Diagnostics.Debug.WriteLine("GetDataContractType " + type.FullName);
        if (typeof(TestMethod1Ouput).IsAssignableFrom(type))
        {
            return typeof(OperationResult<int>);
        }
        if (typeof(TestMethod2Ouput).IsAssignableFrom(type))
        {
            return typeof(OperationResult<string>);
        }
    
        return type;
    }
    
    public object GetObjectToSerialize(object obj, Type targetType)
    {
        //This method is called on serialization.
        System.Diagnostics.Debug.WriteLine("GetObjectToSerialize " + targetType.FullName);
        if (obj is TestMethod1Ouput)
        {
            return new OperationResult<int> { Result = ((TestMethod1Ouput)obj).OrginalResult, Error = string.Empty };
        }
        if (obj is TestMethod2Ouput)
        {
            return new OperationResult<string> { Result = ((TestMethod2Ouput)obj).OrginalResult, Error = string.Empty };
        }
        return obj;
    }
    
    public object GetDeserializedObject(object obj, Type targetType)
    {
        return obj;
    }
    
    public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)
    {
        return null;
    }
    
    public System.CodeDom.CodeTypeDeclaration ProcessImportedType(System.CodeDom.CodeTypeDeclaration typeDeclaration, System.CodeDom.CodeCompileUnit compileUnit)
    {
        return typeDeclaration;
    }
    
    public object GetCustomDataToExport(Type clrType, Type dataContractType)
    {
        return null;
    }
    
    public object GetCustomDataToExport(System.Reflection.MemberInfo memberInfo, Type dataContractType)
    {
        return null;
    }
    
    public void GetKnownCustomDataTypes(Collection<Type> customDataTypes)
    {
    
    }
    
    #endregion
       }
    
  • 更改错误响应的类:

      public class ErrorHandlerBehavior : Attribute, IErrorHandler, IServiceBehavior
       {
           #region Implementation of IErrorHandler
    
           public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
           {
               ServiceEndpoint endpoint =  OperationContext.Current.Host.Description.Endpoints.Find(OperationContext.Current.EndpointDispatcher.EndpointAddress.Uri);
        DispatchOperation dispatchOperation = OperationContext.Current.EndpointDispatcher.DispatchRuntime.Operations.Where(op => op.Action == OperationContext.Current.IncomingMessageHeaders.Action).First();
        OperationDescription operationDesc = endpoint.Contract.Operations.Find(dispatchOperation.Name);
        var attributes = operationDesc.SyncMethod.GetCustomAttributes(typeof(FaultContractAttribute), true);
    
        if (attributes.Any())
        {
            FaultContractAttribute attribute = (FaultContractAttribute)attributes[0];
            var type = attribute.DetailType;
            object faultDetail = Activator.CreateInstance(type);
            Type faultExceptionType = typeof(FaultException<>).MakeGenericType(type);
            FaultException faultException = (FaultException)Activator.CreateInstance(faultExceptionType, faultDetail, error.Message);
            MessageFault faultMessage = faultException.CreateMessageFault();
            fault = Message.CreateMessage(version, faultMessage, faultException.Action);
        }
    }
    
    public bool HandleError(Exception error)
    {
        return true;
    }
    
    #endregion
    
    #region Implementation of IServiceBehavior
    
    public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
    }
    
    public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
    {
    }
    
    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
        foreach (ChannelDispatcherBase channelDispatcherBase in serviceHostBase.ChannelDispatchers)
        {
            ChannelDispatcher channelDispatcher = channelDispatcherBase as ChannelDispatcher;
    
            if (channelDispatcher != null)
            {
                channelDispatcher.ErrorHandlers.Add(this);
            }
        }
    }
    
    #endregion
       }
    

希望我的解决方案对您有所帮助。

答案 2 :(得分:1)

使用预期的方法更改执行不同的发行版号。通常我们不应该停止已交付的产品。如果需要,客户端/接口需要使用新的更改来更新服务。

答案 3 :(得分:0)

有点麻烦,但是您可以创建一个包含旧方法实现的基类,该基类调用较新的重载方法吗?这样,您只需要从基类继承就可以了,它不会引发任何错误。