2011-12-06 15 views
7

Hay muchas preguntas similares por ahí, pero he probado todas las soluciones en cada una de ellas en vano.Cómo reemplazar WebServiceHostFactory MaxReceivedMessageSize?

Tenemos un servicio web que se inicializa con WebServiceHostFactory, pero si se arrojan más de 64k, obtenemos una '400 Bad Request'. Normalmente, esto se resolvería simplemente aumentando MaxReceivedMessageSize, MaxBufferSize y MaxBufferPoolSize. El problema es que al usar WebServiceHostFactory, Web.Config se ignora por completo. Ningún cambio que realice en la sección de ServiceModel se refleja en el servicio.

Sería bueno abandonar completamente WebServiceHostFactory y configurar el web.config desde cero, pero nuestro servicio no se ejecutará sin él. Uno de los métodos tiene un parámetro de flujo así como algunos otros parámetros de cadenas. Sin la fábrica, obtenemos

System.InvalidOperationException: For request in operation Test to be a stream the operation must have a single parameter whose type is Stream 

Por lo tanto, no es una opción eliminar la fábrica. No puedo determinar exactamente qué está haciendo la fábrica que corrige este error, pero pasé 4 días y nunca llegué a ningún lado.

También he intentado anular MaxReceivedMessageSize mediante programación, con algunos ejemplos que he encontrado en todo el lugar:

protected override void OnOpening() 
     { 
      base.OnOpening(); 
      foreach (var endpoint in Description.Endpoints) 
      { 

       //var binding = endpoint.Binding as WebHttpBinding; 
       //if (binding != null) 
       //{ 
       // binding.MaxReceivedMessageSize = 20000000; 
       // binding.MaxBufferSize = 20000000; 
       // binding.MaxBufferPoolSize = 20000000; 
       // binding.ReaderQuotas.MaxArrayLength = 200000000; 
       // binding.ReaderQuotas.MaxStringContentLength = 200000000; 
       // binding.ReaderQuotas.MaxDepth = 32; 
       //} 


       //var transport = endpoint.Binding.CreateBindingElements().Find<HttpTransportBindingElement>(); 
       //if (transport != null) 
       //{ 
       // transport.MaxReceivedMessageSize = 20000000; 
       // transport.MaxBufferPoolSize = 20000000; 
       //} 


       var newTransport = new HttpTransportBindingElement(); 
       newTransport.MaxReceivedMessageSize = 20000000; 
       newTransport.MaxBufferPoolSize = 20000000; 
       endpoint.Binding.CreateBindingElements().Add(newTransport); 
      } 
     } 

La primera no funciona como la fábrica crea una customBinding que no puede ser echado a un WebHttpBinding. El segundo no funciona, ya que parece que los elementos de encuadernación son de solo lectura: independientemente de los elementos a los que establezca los elementos, nada cambia, lo que he verificado leyendo los valores después de 'cambiarlos'. El tercero fue un último intento de tratar de lanzar un nuevo elemento de enlace allí, pero, por supuesto, esto también falló.

Ahora estamos completamente perdidos. ¿Cómo podemos hacer que esto funcione? ¡Crees que sería tan simple!

Gracias chicos

Web.Config

<?xml version="1.0" encoding="UTF-8"?> 
<configuration> 
    <system.web> 
    <compilation targetFramework="4.0" /> 
    </system.web> 
    <system.serviceModel> 
    <bindings> 
     <webHttpBinding> 
     <binding name="rest" maxReceivedMessageSize="500000000" /> 
     </webHttpBinding> 
    </bindings> 
    <services> 
     <service name="SCAPIService" behaviorConfiguration="ServiceBehaviour"> 
     <endpoint address="" binding="webHttpBinding" bindingConfiguration="rest" contract="ISCAPIService" behaviorConfiguration="web"> 
     </endpoint> 
     </service> 
    </services> 
    <behaviors> 
     <serviceBehaviors> 
     <behavior name="ServiceBehaviour"> 
      <serviceMetadata httpGetEnabled="true" /> 
      <serviceDebug includeExceptionDetailInFaults="true" /> 
     </behavior> 
     </serviceBehaviors> 
     <endpointBehaviors> 
     <behavior name="web"> 
      <webHttp /> 
     </behavior> 
     </endpointBehaviors> 
    </behaviors> 
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" /> 
    </system.serviceModel> 
    <system.webServer> 
    <modules runAllManagedModulesForAllRequests="true" /> 
    </system.webServer> 
</configuration> 

Editar, usando un arreglo sugerido que no ha tenido éxito hasta ahora:

public class MyServiceHost : WebServiceHost 
    { 
     public MyServiceHost() 
     { 
     } 

     public MyServiceHost(object singletonInstance, params Uri[] baseAddresses) 
      : base(singletonInstance, baseAddresses) 
     { 
     } 

     public MyServiceHost(Type serviceType, params Uri[] baseAddresses) 
      : base(serviceType, baseAddresses) 
     { 
     } 

     protected override void ApplyConfiguration() 
     { 
      base.ApplyConfiguration(); 
      APIUsers.TestString += "here" + Description.Endpoints.Count.ToString(); 
      foreach (var endpoint in this.Description.Endpoints) 
      { 
       var binding = endpoint.Binding; 
       APIUsers.TestString += binding.GetType().ToString(); 
       if (binding is WebHttpBinding) 
       { 
        var web = binding as WebHttpBinding; 
        web.MaxBufferSize = 2000000; 
        web.MaxBufferPoolSize = 2000000; 
        web.MaxReceivedMessageSize = 2000000; 
       } 
       var myReaderQuotas = new System.Xml.XmlDictionaryReaderQuotas(); 
       myReaderQuotas.MaxStringContentLength = 2000000; 
       myReaderQuotas.MaxArrayLength = 2000000; 
       myReaderQuotas.MaxDepth = 32; 
       binding.GetType().GetProperty("ReaderQuotas").SetValue(binding, myReaderQuotas, null); 
      } 
     } 
    } 

    class MyWebServiceHostFactory : WebServiceHostFactory 
    { 
     protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses) 
     { 
      return new MyServiceHost(serviceType, baseAddresses); 
     } 
    } 

Respuesta

3

Si va a implementar su propio servicio de encargo anfitrión le debería poder anular un método llamado "ApplyConfiguration" donde puede asociar todas las propiedades de configuración que necesita para su enlace. Algunos ejemplos de código como se muestra a continuación:

EDIT: Adición de mi ServiceHost implementación de fábrica

public class MyServiceHost : System.ServiceModel.ServiceHost 
{ 
    public MyServiceHost() { } 

    public MyServiceHost (Type serviceType, params Uri[] baseAddresses) : base(serviceType, baseAddresses) 
    { 

    } 

    public MyServiceHost (object singletonInstance, params Uri[] baseAddresses) : base(singletonInstance, baseAddresses) 
    { 

    } 

protected override void ApplyConfiguration() 
      { 
       Console.WriteLine("ApplyConfiguration (thread {0})", System.Threading.Thread.CurrentThread.ManagedThreadId); 
       base.ApplyConfiguration(); 
       foreach (ServiceEndpoint endpoint in this.Description.Endpoints) 
       { 
        Binding binding = endpoint.Binding; 
        var binding = endpoint.Binding; 
        if(binding is WebHttpBinding) 
        { 
         var web = binding as WebHttpBinding; 
         web.MaxBufferSize = 2000000; 
         web.MaxReceivedMessageSize = 2000000; 
        } 
        var myReaderQuotas = new XmlDictionaryReaderQuotas(); 
        myReaderQuotas.MaxStringContentLength = 5242880; 
        binding.GetType().GetProperty("ReaderQuotas").SetValue(binding, myReaderQuotas, null); 
       } 
      } 

} 

Lo anterior no anular la configuración de cada unión y establece el MaxStringContentLength.

public sealed class MyServiceHostFactory : System.ServiceModel.Activation.ServiceHostFactory 
    { 
     public override System.ServiceModel.ServiceHostBase CreateServiceHost(string constructorString, Uri[] baseAddresses) 
     { 
      return base.CreateServiceHost(constructorString, baseAddresses); 
     } 

     protected override System.ServiceModel.ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses) 
     { 
      return new MyServiceHost(serviceType, baseAddresses); 
     } 
    } 

Ahora mi archivo de anotación Service.svc tiene esta:

<%@ ServiceHost Language="C#" Debug="true" Factory="Sample.MyServiceHostFactory" Service="Sample.ReaderQuotasService" CodeBehind="ReaderQuotasService.svc.cs" %> 
+0

Gracias Rajesh. Sin embargo, esto todavía no funciona para mí, por alguna razón no hay puntos finales cuando se llama a ApplyConfiguration, por lo que nunca llega al bucle foreach. Como la fábrica está haciendo toda la creación de la descripción, ¿cómo haría para que cree el punto final antes de que se active ApplyConfiguration? –

+0

Es posible que publique el código de fábrica que tiene para proporcionar más ayuda. – Rajesh

+0

Claro, he actualizado la pregunta con el código. La declaración se ve así: <% @ ServiceHost Language = "C#" Debug = "true" Service = "APINamespace.Service" CodeBehind = "Service.svc.cs" Factory = "WcfService1.MyWebServiceHostFactory"%> En mi código , el APIUsers.TestString se imprime, solo para asegurarse de que este código se esté ejecutando. El resultado fue aquí0 (0 = recuento de puntos finales) –

7

Para el beneficio de los demás, la respuesta anterior se va por el camino correcto, pero tiene muchos errores en ella y no funcionará sin cambios (por lo tanto, el OP no pudo lograr que la solución funcionara). Use lo siguiente y funcionará para usted 'listo para usar'.

Nota - Es extremadamente importante proporcionar a todos los constructores a continuación y utilizar la sobrecarga del constructor demostrado en la fábrica, de lo contrario obtendrá el siguiente mensaje de error:

InitializeRuntime requiere que la propiedad Description ser inicializado O bien proporcionar una ServiceDescription válida en el método CreateDescription o anular el método InitializeRuntime para proporcionar una implementación alternativa aplicación

ServiceHost personalizado: aplicación

public class CustomServiceHost : ServiceHost 
{ 
    public CustomServiceHost() 
    { 
    } 

    public CustomServiceHost(object singletonInstance, params Uri[] baseAddresses) : base(singletonInstance, baseAddresses) 
    { 
    } 

    public CustomServiceHost(Type serviceType, params Uri[] baseAddresses) : base(serviceType, baseAddresses) 
    { 
    } 

    protected override void OnOpening() 
    { 
     base.OnOpening(); 

     foreach (var endpoint in this.Description.Endpoints) 
     { 
      var binding = endpoint.Binding; 
      var web = binding as WebHttpBinding; 

      if (web != null) 
      { 
       web.MaxBufferSize = 2147483647; 
       web.MaxReceivedMessageSize = 2147483647; 
      } 

      var myReaderQuotas = new XmlDictionaryReaderQuotas { MaxStringContentLength = 2147483647 }; 

      binding.GetType().GetProperty("ReaderQuotas").SetValue(binding, myReaderQuotas, null); 
     } 
    } 
} 

ServiceHostFactory personalizado:

public sealed class CustomServiceHostFactory : ServiceHostFactory 
{ 
    protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses) 
    { 
     return new CustomServiceHost(serviceType, baseAddresses); 
    } 
} 

El Service1.svc el marcado arriba funcionará siempre que el tipo sea correcto. De hecho, utilicé esta técnica para anular WebServiceHost y permitir tamaños de respuesta JSON más grandes, pero los principios también deberían aplicarse a ServiceHost.

+0

No estoy tratando de anular ninguna de las propiedades de enlace, pero +1 por la nota sobre constructores y el error InitializeRuntime. ¡Era justo lo que estaba buscando! – AdmSteck

+0

En VB .Net, ¿usaría la palabra clave .New() para los constructores? – Thomas

+0

Muy útil. Estaba tratando de usar 'ApplyConfiguration()' para agregar comportamientos de punto final, pero 'WebServiceHost' no crea sus puntos finales hasta' OnOpening() ', así que esto me puso en marcha. – kenchilada

Cuestiones relacionadas