Неверная Оценка Длины Строки
мой коллега и я отлаживаем проблему в службе 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 для , где поведение происходит:
.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 CS00,
[9] char[] CS01,
[10] string[] CS02)
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 CS00
L_0022: ldloc.s CS00
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
Update #3: мы попытались изменить имя локальной переменной, и она проходит проверку null и проверку длины, но не удается, когда мы вызываем 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 предложения
используйте ILDASM, чтобы взглянуть на Il, который генерируется, возможно, опубликуйте IL здесь, чтобы сообщество могло взглянуть
измените имя аргумента "AppName" на что-то совсем другое "xxxAppName", например.
Я знаю, что пункт 2 может показаться бессмысленным, но в прошлом я сталкивался с кажущимися необъяснимыми ситуациями, только чтобы обнаружить, что что-то не было скомпилирован или произошел конфликт области, в то время как отладчик показывает, что вы ожидаете. И, не помешает попробовать:)
к сожалению, ваши примеры не показывают, где и как это не вызывается конструктор, то есть. где объект типа AppActiveDirectoryDomain
инстанцируется. (Не то, чтобы это имело значение, учитывая последний пример кода, который вы предоставили.)
это, как говорится, я сталкивался с подобными нелогичными проблемами в Visual Studio раньше, один раз, когда играл с контрактами кода (который переписывает инструкции CIL, выпущенные из компилятора), а в другой раз по случайной причине. Я так и не нашел истинная проблема, я помню, что изменил какой-то код в совершенно другом месте, и внезапно проблема была решена.
Я подозреваю, что иногда отладчик каким-то образом выходит из синхронизации с фактическим состоянием программы.
несколько вопросов:
используете ли вы какие-либо инструменты, которые перезаписывают код CIL (=MSIL) после запуска компилятора? (например, кодовые контракты или PostSharp)
может быть, что ваш файлы символов отладчика или база данных программы не синхронизирована со скомпилированным кодом? Вы выполнили проект очистки?
вы можете попробовать следующее:
слова
if ("AOD".Length == 0) throw new ArgumentNullException(...);
только бросить внутри этого конкретного конструктора? Или проблема сохраняется, если вы переместите этот оператор в функцию вызывающего абонента? Он все еще сохраняется, если вы переместите его, например, вMain()
способ?попробовать свои код с разные отладчик (ссылки на другой вопрос о StackOverflow об отладчиках MSIL), если у вас есть один доступный.
Это оказалось 64-битной проблемой. Или, по крайней мере, это только проблема с 64-разрядными. В IIS 7.0 на 64-разрядной ОС все веб-сайты по умолчанию размещаются в 64-разрядных процессах. Если мы установим службу для размещения в 32-разрядном процессе, проблема исчезнет.