Мой коллега и я отлаживаем проблему в сервисе 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
}
Править:
Вот снимок экрана переменной в окне 'Watch' в данный момент, ArgumentNullException брошен: http://imgur.com/xQm4J.png
Кроме того, второй снимок экрана, показывающий исключение, выданное при проверке длины строки, после явного объявления его 5 строк выше: http://imgur.com/lSrk9.png
Обновление № 3: Мы пытались менять имя локальной переменной, и это проходит пустую проверку и проверку длины, но перестало работать, когда мы звоним string.IsNullOrEmpty
. См. этот снимок экрана: http://imgur.com/Z57AA.png.
Ответы:
Мы не используем инструментов, которые изменили бы MSIL. Мы выполнили очистку, и также вручную удалили все файлы из каталогов сборки и вызвали восстанавливание... того же результата.
Следующее утверждение оценивает как верное и входит если блок: if (string.IsNullOrEmpty("AOD")) { /* */ }
.
Как конструктор вызывают так:
try { using (AppActiveDirectoryDomain domain = new AppActiveDirectoryDomain(AppName, DomainName)) { } }
Это сразу в рамках самого сервисного метода WCF; AppName и DomainName являются параметрами к вызову. Даже обходя эти параметры и использование новых строк, мы все еще получаем ошибки.
Оказалось, что это проблема 64-битной версии. Или, по крайней мере, это проблема только для 64-битных ОС. В IIS 7.0 на 64-битной ОС все веб-сайты по умолчанию размещаются в 64-битных процессах. Если мы настроим службу на размещение в 32-битном процессе, проблема исчезнет.
У меня есть 2 предложения
Используйте ILDASM, чтобы взглянуть на генерируемый IL, возможно, опубликуйте IL здесь, чтобы сообщество могло взглянуть
Измените имя аргумента 'AppName' на что-то совсем другое 'xxxAppName', например.
Я знаю, что пункт 2 может показаться бессмысленным, но в прошлом я сталкивался с необъяснимыми на первый взгляд ситуациями, когда выяснялось, что что-то не компилируется или существует конфликт областей видимости, в то время как отладчик показывает то, что вы ожидаете. Да и попробовать не помешает :)
К сожалению, в ваших примерах кода не показано, где и как вызывается этот конструктор с ошибкой, т.е. где создается объект типа AppActiveDirectoryDomain
. (Не то чтобы это имело значение, учитывая последний пример кода, который вы предоставили.)
При этом я уже сталкивался с подобными нелогичными проблемами в Visual Studio раньше, однажды, когда играл с Code Contracts (который переписывает CIL инструкции, исходящие из компилятора) и в другой раз по случайной причине. Я так и не нашел истинной проблемы, я помню, как изменил какой-то код в совершенно другом месте, и внезапно проблема была решена.
Я подозреваю, что иногда отладчик каким-то образом не синхронизируется с фактическим состоянием программы.
Некоторые вопросы:
Используете ли вы какие-либо инструменты, которые переписывают код CIL (= MSIL) после запуска компилятора? (например, Code Contracts или PostSharp)
Может быть, ваши файлы символов отладчика или база данных программы не синхронизированы с скомпилированным кодом? Вы выполнили очистку проекта?
Вы можете попробовать следующее:
Выдает ли оператор if ("AOD" .Length == 0) новое исключение ArgumentNullException (...);
только бросать внутрь этого конкретного конструктора? Или проблема не исчезнет, если вы переместите этот оператор в вызывающую функцию? Он все еще сохраняется, если вы его переместите, например. к методу Main ()
?
Попробуйте использовать свой код с другим отладчиком ( ссылки на другой вопрос в StackOverflow об отладчиках MSIL ), если у вас есть один доступен.