2010-04-30 10 views
7

Mi compañero de trabajo y yo estamos solucionando un problema en un servicio de WCF en el que está trabajando cuando la longitud de una cadena no se evalúa correctamente. Él se está ejecutando este método de unidad de prueba un método en su servicio WCF:Longitud de cadena evaluando incorrectamente

// Unit test method 
public void RemoveAppGroupTest() 
{ 
    string addGroup = "TestGroup"; 
    string status = string.Empty; 
    string message = string.Empty; 

    appActiveDirectoryServicesClient.RemoveAppGroup("AOD", addGroup, ref status, ref message); 
} 


// Inside the WCF service 
[OperationBehavior(Impersonation = ImpersonationOption.Required)] 
public void RemoveAppGroup(string AppName, string GroupName, ref string Status, ref string Message) 
{ 
    string accessOnDemandDomain = "MyDomain"; 

    RemoveAppGroupFromDomain(AppName, accessOnDemandDomain, GroupName, ref Status, ref Message); 
} 

public AppActiveDirectoryDomain(string AppName, string DomainName) 
{ 
    if (string.IsNullOrEmpty(AppName)) 
    { 
     throw new ArgumentNullException("AppName", "You must specify an application name"); 
    } 
} 

Tratamos de entrar en el código fuente de .NET para ver qué valor string.IsNullOrEmpty estaba recibiendo, pero el IDE impreso este mensaje cuando intentamos evalúe la variable: 'No se puede obtener el valor del' valor 'local o de argumento, ya que no está disponible en este puntero de instrucción, posiblemente porque se ha optimizado fuera de lugar'. (Ninguno de los proyectos involucrados tiene optimizaciones habilitadas). Entonces, decidimos intentar establecer explícitamente el valor de la variable dentro del método mismo, inmediatamente antes de la verificación de la longitud, pero eso no ayudó.

// Lets try this again. 
public AppActiveDirectoryDomain(string AppName, string DomainName) 
{ 
    // Explicitly set the value for testing purposes. 
    AppName = "AOD"; 

    if (AppName == null) 
    { 
     throw new ArgumentNullException("AppName", "You must specify an application name"); 
    } 

    if (AppName.Length == 0) 
    { 
     // This exception gets thrown, even though it obviously isn't a zero length string. 
     throw new ArgumentNullException("AppName", "You must specify an application name"); 
    } 
} 

Estamos realmente tirando de nuestros pelos en este caso. ¿Alguien más ha experimentado un comportamiento como este? ¿Alguna sugerencia para depurarlo?


Aquí está el MSIL para el objeto AppActiveDirectoryDomain, donde el comportamiento que está ocurriendo:

.method public hidebysig specialname rtspecialname instance void .ctor(string AppName, string DomainName) cil managed 
{ 
.maxstack 5 
.locals init (
    [0] class [System]System.Net.NetworkCredential ldapCredentials, 
    [1] string[] creds, 
    [2] string userName, 
    [3] class [mscorlib]System.ArgumentNullException exc, 
    [4] class [System.DirectoryServices]System.DirectoryServices.ActiveDirectory.DirectoryContext directoryContext, 
    [5] class [System.DirectoryServices]System.DirectoryServices.ActiveDirectory.Domain domain, 
    [6] class [System.DirectoryServices.Protocols]System.DirectoryServices.Protocols.LdapException V_6, 
    [7] class [mscorlib]System.Exception V_7, 
    [8] bool CS$4$0000, 
    [9] char[] CS$0$0001, 
    [10] string[] CS$0$0002) 
L_0000: ldarg.0 
L_0001: ldsfld string [mscorlib]System.String::Empty 
L_0006: stfld string MyNamespace.MyClass.AppActiveDirectoryDomain::appOU 
L_000b: ldarg.0 
L_000c: call instance void [mscorlib]System.Object::.ctor() 
L_0011: nop 
L_0012: nop 
L_0013: ldstr "AOD" 
L_0018: call bool [mscorlib]System.String::IsNullOrEmpty(string) 
L_001d: ldc.i4.0 
L_001e: ceq 
L_0020: stloc.s CS$4$0000 
L_0022: ldloc.s CS$4$0000 
L_0024: brtrue.s L_0037 
L_0026: nop 
L_0027: ldstr "AppName" 
L_002c: ldstr "You must specify an application name" 
L_0031: newobj instance void [mscorlib]System.ArgumentNullException::.ctor(string, string) 
L_0036: throw 

Y el MSIL para la llamada string.IsNullOrEmpty:

.method public hidebysig static bool IsNullOrEmpty(string 'value') cil managed 
{ 
    .maxstack 8 
    L_0000: ldarg.0 
    L_0001: brfalse.s L_000d 
    L_0003: ldarg.0 
    L_0004: callvirt instance int32 System.String::get_Length() 
    L_0009: ldc.i4.0 
    L_000a: ceq 
    L_000c: ret 
    L_000d: ldc.i4.1 
    L_000e: ret 
} 

Editar:

Aquí está una captura de pantalla de la variable en la ventana de 'reloj' en el momento en que se lanza el ArgumentNullException: http://imgur.com/xQm4J.png

Además, una segunda pantalla que muestra la excepción que se produce cuando el control de la longitud de la cadena, después de declarar de manera explícita 5 líneas arriba: http://imgur.com/lSrk9.png

Actualización # 3: Intentamos cambiar el nombre de la variable local y pasa la comprobación nula y la verificación de longitud, pero falla cuando llamamos al string.IsNullOrEmpty. Ver esta captura de pantalla: http://imgur.com/Z57AA.png.


respuestas:

  • no usamos ninguna herramienta que modificarían el MSIL. Hemos realizado una limpieza y también hemos eliminado manualmente todos los archivos de los directorios de compilación y hemos forzado una reconstrucción ... el mismo resultado.

  • La siguiente declaración se evalúa como verdadera y entra en el bloque if: if (string.IsNullOrEmpty("AOD")) { /* */ }.

  • El constructor se llama así:

    try { using (AppActiveDirectoryDomain domain = new AppActiveDirectoryDomain(AppName, DomainName)) { } }

Esta es inmediatamente dentro del método de servicio WCF sí mismo; AppName y DomainName son parámetros de la llamada. Incluso pasando por alto estos parámetros y utilizando nuevas cadenas, seguimos recibiendo los errores.


+1

Si coloca un punto de interrupción en la excepción que arroja, y agrega 'AppName' y' AppName.Length' a los relojes, ¿qué muestran? –

+0

Muestran 'AppName == "AOD"' y 'AppName.Length == 3', por lo que se evalúan correctamente. He adjuntado una captura de pantalla al final de mi pregunta. –

+1

Puede hacer que la prueba sea aún menos ambigua: 'if (" AOD ".Length == 0) throw ...'. Esto descartaría la variable 'AppName' como fuente de sus problemas. Si aún se lanza la excepción, puede que no haya nada que puedas hacer al respecto. – stakx

Respuesta

0

Esto resultó ser un problema de 64 bits. O al menos es solo un problema que se ejecuta en 64 bits. En IIS 7.0 en un sistema operativo de 64 bits, todos los sitios web están alojados en procesos de 64 bits de forma predeterminada. Si configuramos el servicio para que se aloje en un proceso de 32 bits, el problema desaparece.

1

Desafortunadamente, los ejemplos de su código no muestran dónde y cómo se llama a ese constructor que falla, es decir. donde se crea una instancia de un objeto del tipo AppActiveDirectoryDomain. (No es que importe, dado el último ejemplo de código que proporcionó)

Dicho esto, me he encontrado con problemas no lógicos similares en Visual Studio antes, una vez cuando jugaba con Code Contracts (que vuelve a escribir el Instrucciones CIL emitidas por el compilador) y otra vez por una razón aleatoria. Nunca encontré el verdadero problema, recuerdo haber cambiado un código en un lugar completamente diferente y, de repente, el problema se resolvió.

Sospecho que a veces, el depurador de alguna manera se desincroniza con el estado real del programa.

Algunas preguntas:

  • ¿Utiliza herramientas que re-escribir el código CIL (= MSIL) después de que el compilador ha quedado? (como, por ejemplo, Contratos de código o PostSharp)

  • ¿Podría ser que los archivos de símbolos del depurador o la base de datos del programa no estén sincronizados con el código compilado? ¿Hiciste una limpieza de proyecto?

Usted podría intentar esta próxima:

  • ¿El documento if ("AOD".Length == 0) throw new ArgumentNullException(...); sólo un tiro dentro de ese constructor particular? ¿O persiste el problema si mueve esa declaración a la función de llamante? ¿Aún persiste si lo mueve, p. al método Main()?

  • Pruebe su código con una different debugger (enlaces a otra pregunta en StackOverflow sobre MSIL depuradores), si lo tiene disponible.

+0

He agregado respuestas a mi pregunta. –

2

tengo 2 sugerencias

  1. Uso ILDASM a echar un vistazo a la IL que se está generando, posiblemente Deja la IL aquí que la comunidad pueda echar un vistazo

  2. Cambio el nombre del argumento 'AppName' a algo muy diferente 'xxxAppName' por ejemplo.

sé el punto 2 podría parecer inútil, pero tengo en el pasado encontrarse en situaciones aparentemente inexplicables, sólo para encontrar que algo no se está compilando o había un conflicto alcance, mientras que el depurador muestra lo que espera . Y, no va a doler intentar :)

+0

Intentamos usar un nombre diferente para la variable local, y tiene éxito la comprobación manual nula, así como la verificación de longitud, pero luego falla en 'string.IsNullOrEmpty'. Ver el tercer enlace de captura de pantalla que estoy publicando. –

+0

Publiqué el IL. A ninguno de nosotros nos parece sospechoso, pero ninguno de nosotros somos expertos en IL. –

+0

@Justin, la IL parece estar bien ... ¿Puede escribir algunos mensajes en la ventana de depuración (Diagnostics.Debug.WriteLine). Vemos lo que está viendo el depurador, pero esto podría ayudar a ver qué está viendo el código de ejecución. –