Неправильная оценка длины строки - PullRequest
7 голосов
/ 30 апреля 2010

Мой коллега и я отлаживаем проблему в службе WCF, над которой он работает, когда длина строки не оценивается правильно. Он запускает этот метод для модульного тестирования метода в своей службе WCF:

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

Мы попытались войти в исходный код .NET, чтобы увидеть, какое значение string.IsNullOrEmpty получало, но IDE напечатало это сообщение, когда мы попытались вычислить переменную: «Невозможно получить значение локального или аргумент« значение »как недоступен в этом указателе инструкций, возможно, потому что он был оптимизирован. ' (Ни в одном из задействованных проектов не включена оптимизация). Итак, мы решили попытаться явно установить значение переменной внутри самого метода, непосредственно перед проверкой длины - но это не помогло.

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

Мы действительно выдернули наши волосы на этом. Кто-нибудь еще испытывал подобное поведение? Любые советы по его отладке?


Вот MSIL для объекта AppActiveDirectoryDomain, где происходит поведение:

.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

И MSIL для 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:

Вот скриншот переменной в окне 'Watch' в момент возникновения исключения ArgumentNullException: http://imgur.com/xQm4J.png

Кроме того, второй снимок экрана показывает исключение, которое выдается при проверке длины строки после явного объявления ее на 5 строк выше: http://imgur.com/lSrk9.png

Обновление № 3: мы попытались изменить имя локальной переменной, и оно проходит проверку на нулевое значение и проверку длины, но завершается неудачно, когда мы вызываем string.IsNullOrEmpty. Смотрите этот скриншот: http://imgur.com/Z57AA.png.


Ответы:

  • Мы не используем никаких инструментов, которые могли бы изменить MSIL. Мы выполнили очистку, а также вручную удалили все файлы из каталогов сборки и принудительно восстановили ... тот же результат.

  • Следующий оператор оценивается как true и входит в блок if: if (string.IsNullOrEmpty("AOD")) { /* */ }.

  • Конструктор называется так:

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

Это непосредственно в самом методе службы WCF; AppName и DomainName являются параметрами для вызова. Даже обходя эти параметры и используя новые строки, мы все равно получаем ошибки.


Ответы [ 3 ]

2 голосов
/ 30 апреля 2010

У меня есть 2 предложения

  1. Используйте ILDASM, чтобы взглянуть на генерируемый IL, возможно, опубликуйте здесь IL, чтобы сообщество могло посмотреть

  2. Измените имя аргумента 'AppName', например, на совсем другое 'xxxAppName'.

Я знаю, что пункт 2 может показаться бессмысленным, но в прошлом я сталкивался с казалось бы необъяснимыми ситуациями, только для того, чтобы обнаружить, что что-то не компилируется или возник конфликт с областью видимости, в то время как отладчик показывает то, что вы ожидаете. И это не помешает попробовать:)

1 голос
/ 30 апреля 2010

К сожалению, ваши примеры кода не показывают, где и как вызывается этот конструктор, т.е. где создается объект типа AppActiveDirectoryDomain. (Не то чтобы это имело значение, учитывая последний приведенный вами пример кода.)

При этом я сталкивался с подобными нелогичными проблемами в Visual Studio и раньше, один раз, когда играл с Code Contracts (который переписывает инструкции CIL, генерируемые компилятором), и в другой раз по случайной причине. Я никогда не находил истинную проблему, я помню, как менял код в совершенно другом месте, и внезапно проблема была решена.

Я подозреваю, что иногда отладчик как-то не синхронизируется с фактическим состоянием программы.

Некоторые вопросы:

  • Используете ли вы какие-либо инструменты, которые переписывают код CIL (= MSIL) после запуска компилятора? (например, Code Contracts или PostSharp)

  • Может ли быть так, что ваши файлы символов отладчика или база данных программы не синхронизированы с скомпилированным кодом? Вы выполняли очистку проекта?

Вы можете попробовать это следующим образом:

  • Выдает ли оператор if ("AOD".Length == 0) throw new ArgumentNullException(...); только внутри этого конкретного конструктора? Или проблема сохраняется, если вы переместите этот оператор в функцию вызывающей стороны? Сохраняется ли оно, если вы переместите его, например, к методу Main()?

  • Попробуйте свой код с помощью другого отладчика ( ссылки на другой вопрос в StackOverflow о отладчиках MSIL ), если он у вас есть.

0 голосов
/ 04 мая 2010

Это оказалось 64-битной проблемой. Или, по крайней мере, это проблема только 64-битной версии. В IIS 7.0 в 64-разрядной ОС все веб-сайты по умолчанию размещаются в 64-разрядных процессах. Если мы установим службу для размещения в 32-разрядном процессе, проблема исчезнет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...