2010-04-30 15 views
7

Mój współpracownik i ja debugujemy problem w usłudze WCF, nad którą pracuje, gdzie długość łańcucha nie jest poprawnie oceniana. On pracuje tej metody do testów jednostkowych metody w służbie WCF:Niepoprawna długość łańcucha znaków

// 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"); 
    } 
} 

Próbowaliśmy wejść do kodu źródłowego .NET, aby zobaczyć, jakie wartości string.IsNullOrEmpty otrzymywał, ale IDE drukowane tę wiadomość, gdy staraliśmy się oceń zmienną: "Nie można uzyskać wartości wartości lokalnej lub argumentowej", ponieważ nie jest dostępna w tym wskaźniku instrukcji, prawdopodobnie dlatego, że została zoptymalizowana ". (Żaden z zaangażowanych projektów nie ma włączonych optymalizacji). Postanowiliśmy więc spróbować jawnie ustawić wartość zmiennej wewnątrz samej metody, tuż przed sprawdzeniem długości - ale to nie pomogło.

// 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"); 
    } 
} 

Naprawdę wyciągamy włosy z tego. Czy ktoś inny doświadczył takiego zachowania? Wszelkie wskazówki dotyczące debugowania go?


Oto MSIL dla obiektu AppActiveDirectoryDomain, gdzie zachowanie ma miejsce:

.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 

A MSIL dla wywołania 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 
} 

Edit:

Oto zrzut ekranu zmiennej w oknie „oglądać” w momencie ArgumentNullException jest wyrzucany: http://imgur.com/xQm4J.png

Również drugi zrzut ekranu przedstawiający wyjątek wyrzucane podczas sprawdzania długości łańcucha, po wyraźnie oświadczając to 5 linii powyżej: http://imgur.com/lSrk9.png

Aktualizacja # 3: Próbowaliśmy zmienić nazwę zmiennej lokalnej, która przejdzie kontrolę zerową i kontrolę długości, ale nie powiedzie się, gdy zadzwonimy pod numer string.IsNullOrEmpty. Zobacz ten zrzut ekranu: http://imgur.com/Z57AA.png.


Odpowiedzi:

  • nie używamy żadnych narzędzi, które modyfikują MSIL. Przeprowadziliśmy czyszczenie, a także ręcznie usunęliśmy wszystkie pliki z katalogów kompilacji i wymusiliśmy przebudowę ... ten sam wynik.

  • Następująca instrukcja jest wartością true i wchodzi do bloku if: if (string.IsNullOrEmpty("AOD")) { /* */ }.

  • Konstruktor nazywa się tak:

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

To natychmiast wewnątrz metody usługi WCF samego; AppName i DomainName są parametrami połączenia. Nawet pomijając te parametry i używając nowych ciągów, nadal otrzymujemy błędy.


+1

Jeśli umieścisz punkt przerwania na wyjątku, który zostanie zgłoszony, i dodasz do listy zegarków nazwy "AppName" i "AppName.Length", co one pokazują? –

+0

Pokazują one "AppName ==" AOD "" i "AppName.Length == 3" - więc oceniają poprawnie. Załączam zrzut ekranu na dole mojego pytania. –

+1

Możesz sprawić, że test będzie jeszcze mniej niejednoznaczny: 'if (" AOD ".Length == 0) throw ...'. Wyklucza to zmienną 'AppName' jako źródło twoich problemów. Jeśli wyjątek jest nadal rzucany, nic nie można z tym zrobić. – stakx

Odpowiedz

0

Okazało się, że jest to problem 64-bitowy. Lub przynajmniej jest to tylko problem z 64-bitowym. W IIS 7.0 w 64-bitowym systemie operacyjnym wszystkie witryny są domyślnie hostowane w procesach 64-bitowych. Jeśli ustawimy usługę hostowaną w procesie 32-bitowym, problem zniknie.

1

Niestety, przykłady kodu nie pokazują, gdzie iw jaki sposób wywoływany jest nieudany konstruktor, tj. gdzie tworzony jest obiekt typu AppActiveDirectoryDomain. (Nie powinno to mieć znaczenia, biorąc pod uwagę ostatni przykład kodu podany przez ciebie).

Mimo to, spotkałem się z podobnymi zagadnieniami nielogicznymi w Visual Studio wcześniej, raz podczas zabawy z Code Contracts (który ponownie pisze Instrukcje CIL emitowane z kompilatora) i inny czas z przypadkowego powodu. Nigdy nie znalazłem prawdziwego problemu, pamiętam zmianę kodu w zupełnie innym miejscu i nagle problem został rozwiązany.

Podejrzewam, że czasami debugger w jakiś sposób nie jest zsynchronizowany ze stanem rzeczywistego programu.

Niektóre pytania:

  • Używasz żadnych narzędzi, które re-write CIL (MSIL) = kod po kompilator uruchomić? (np. Code Contracts lub PostSharp)

  • Czy to możliwe, że pliki symboli debuggera lub baza danych programu są niezsynchronizowane ze skompilowanym kodem? Czy wykonałeś czyszczenie projektu?

można spróbować to następny:

  • Czy oświadczenie if ("AOD".Length == 0) throw new ArgumentNullException(...); rzucać tylko wewnątrz danego konstruktora? Czy problem nadal występuje, jeśli przeniesiesz to oświadczenie do funkcji osoby dzwoniącej? Czy nadal się utrzymuje, jeśli przeniesiesz go np. do metody Main()?

  • Spróbuj kod z different debugger (linki do innego pytania na StackOverflow o MSIL Debuggers), jeśli jest dostępna.

+0

Dodałem odpowiedzi na moje pytanie. –

2

mam 2 propozycje

  1. Zastosowanie ILDASM aby przyjrzeć IL, który jest generowany, ewentualnie zakładać IL tu więc wspólnota może spojrzeć

  2. Zmień nazwa argumentu "AppName" na coś zupełnie innego "xxxAppName" na przykład.

wiem pkt 2 może wydawać się bezsensowne, ale mam w przeszłości natknąć się sytuacje pozornie niewyjaśnione, tylko do stwierdzenia, że ​​coś nie jest kompilowany lub wystąpił konflikt zakres, natomiast debugger pokazuje, co można oczekiwać . I nie zaszkodzi spróbować :)

+0

Próbowaliśmy użyć innej nazwy dla zmiennej lokalnej, i powiedzie się ręczne zerowanie, a także sprawdzanie długości, ale następnie kończy się niepowodzeniem w 'string.IsNullOrEmpty'. Zobacz trzeci zrzut ekranu, który publikuję. –

+0

Wysłałem IL. Dla każdego z nas nie wygląda to podejrzanie, ale żaden z nas nie jest ekspertem od IL. –

+0

@Justin, IL wygląda dobrze ... Czy możesz napisać wiadomości do okna debugowania (Diagnostics.Debug.WriteLine). Widzimy, co widzi debugger, ale może to pomóc w zobaczeniu, co widzi kod wykonawczy. –

Powiązane problemy