Ссылки на проект VS нарушены по чувствительности к регистру GUID

Начиная с обновления до VS 2015, моя команда испытала случайные причудливые вещи, которые, я уверен, сейчас разрабатываются в Microsoft. Один довольно раздражает то, что мы, похоже, теряем ссылки на проекты, особенно после ветвления. Вчера я начал работать над новой ветвью нашего решения, только чтобы выяснить, что типы были не распознаны, а использование пространства имен цитировалось как ненужное (потому что они были для типов, которые внезапно стали нераспознанными).

Ссылки в проекте не показывали никаких значков, указывающих на проблему со ссылкой, но просто чтобы посмотреть, сработает ли она, я удалил и повторно добавил ссылку на проект, что привело к тому, что его типы были снова распознаны.

Это, конечно, обновило файл проекта, поэтому я посмотрел, какие изменения были внесены. Единственная разница между проектом, который не смог обнаружить ссылку, и тем, который теперь может, состоит в том, что буквенные символы в GUID были изменены с нижнего регистра на верхний. Например:

Старая, неработающая ссылка:

<ProjectReference Include="path/redacted">
    <Project>{95d34b2e-2ceb-499e-ab9e-b644b0af710d}</Project>
    <Name>Project.Name.Redacted</Name>
</ProjectReference>

Новая фиксированная ссылка:

<ProjectReference Include="path/redacted">
    <Project>{95D34B2E-2CEB-499E-AB9E-B644B0AF710D}</Project>
    <Name>Project.Name.Redacted</Name>
</ProjectReference>

Я ищу причину, по которой это происходит, и то, как я мог бы это исправить, без необходимости вручную удалять и повторно добавлять ссылки повсеместно (и без необходимости преобразовывать все GUID файла проекта в верхний регистр).

Я должен отметить, что эти "неработающие" ссылки не нарушают сборку и что они отображаются только в списке ошибок как ошибка IntelliSense, а не как ошибки сборки. Таким образом, ссылки на самом деле не сломаны, они просто сломали IntelliSense (что, возможно, еще хуже?!).

3 ответа

Решение

TL; DR

Visual Studio не совсем согласна с тем, как она назначает идентификаторы GUID для проектов или как она определяет эти идентификаторы GUID в ссылках на проекты. Я смог решить проблему, используя прописные буквы GUID с фигурными скобками для ProjectGuid элементы и нижний регистр с фигурными скобками для Project элементы (в ссылках).

Фон

У нас есть большое решение (более 60 проектов на C#), и у нас были постоянные проблемы с решением Перестроить, так как неправильный порядок сборки может привести к невозможности разрешения ссылочных проектов, которые еще не были собраны (но должны были быть). Зависимости сборки и порядок сборки оказались правильными. Пакетная сборка MSBuild работала нормально, это была проблема только при восстановлении из Visual Studio.

Преобразование всех GUID проекта в верхний регистр с фигурными скобками и все ссылки на GUID проекта в нижний регистр с фигурными скобками решило проблему. Обычно Visual Studio генерирует эти GUID, но не всегда.

Проводя некоторые исследования в новом тестовом решении, выясняется, что:

  1. Сгенерированные GUID для проектов консольных приложений пишутся в верхнем регистре с фигурными скобками.
  2. Сгенерированные идентификаторы GUID для проектов библиотек классов изначально строчные, без скобок.
  3. Если добавлена ​​новая ссылка на проект проекта библиотеки классов с GUID в нижнем регистре, то не только добавляется GUID ссылки, но GUID проекта преобразуется в верхний регистр с фигурными скобками.
  4. Если создается копия проекта библиотеки классов, а затем добавляется в решение, ее GUID заменяется новым, в котором используются верхний регистр и фигурные скобки. (Но если копия сделана и ее GUID удален вручную, Visual Studio не вставит заменяющий GUID в файл.csproj.)
  5. Ссылки на проекты GUID обычно используют строчные и фигурные скобки, но каким-то образом наш проект накопил кучу ссылок на GUID в верхнем регистре.
  6. GUID в.sln всегда используют верхний регистр и фигурные скобки.

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

Поиск и замена регулярных выражений

Чтобы исправить это, я использовал поиск и замену в файлах на основе регулярных выражений в Notepad++, чтобы все ProjectGuids в файлах.csproj были в верхнем регистре с фигурными скобками (по умолчанию для консольных приложений, и стиль Visual Studio будет применяться после добавления любой ссылки на проект к проект):

Find what: (<ProjectGuid>)\{?([0-9a-f-]+)\}?(</ProjectGuid>)
Replace with: \1{\U\2}\E\3
Search in: *.csproj

Обязательно включите поиск по регулярному выражению и отключите регистр совпадений. И не ищите все файлы, или вы можете вносить изменения, которые вам не нужны, например, в файлы *.xproj, как отмечает @AspNyc. (См. Этот ответ для дополнительной информации об использовании регулярных выражений для изменения регистра.)

Затем я заменил все ссылки на проекты, чтобы использовать строчные буквы с фигурными скобками (что обычно делает Visual Studio):

Find what: (<Project>)\{?([0-9a-f-]+)\}?(</Project>)
Replace with: \1{\L\2}\E\3
Search in: *.csproj

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

Я столкнулся с аналогичной проблемой при обновлении VS2017 v15.7 до v15.9.

Ответ для меня был закрыть VS, очистить скрытое .vs папку в корневом каталоге моего решения и перезапустите VS.

Кажется, есть ошибка в системе проектов. Этот powershell будет перебирать проекты и делать все ссылки прописными:

#FixGuids

get-childitem -recurse | ?{ @('.sln', '.csproj', '.vbproj') -contains $_.Extension } | %{
   [regex]::Replace((gc $_.FullName), '[{(]?[0-9A-Fa-f]{8}[-]?([0-9A-Fa-f]{4}[-]?){3}[0-9A-Fa-f]{12}[)}]?', { return ([string]$args[0]).ToUpperInvariant() }) |
      Out-File $_.FullName
}

Это довольно упрощенно. Если вы полагаетесь на руководства в своих проектах для чего-то иного, чем ссылки, вы можете захотеть превратить это в нечто более умное.

Чтобы использовать ответ в git hook, мне пришлось преобразовать его в синтаксис регулярного выражения sed:

sed -r "s/(<Project>\{?)([0-9A-F-]+)(\}?<\/Project>)/\1\L\2\E\3/g" sample.csproj

Может быть, кто-то найдет это полезным.

Сегодня в Visual Studio 2019 (16.2.2) я столкнулся с проблемами системы сборки, когда проекты без необходимости (и неоднократно) перестраивались всякий раз, когда я инициировал сборку своего решения.

(Ни один из обычных шагов не решил проблемы сборки, например, удаление папки ".vs" или удаление каталогов "bin" и "obj" и т. Д.).

Сначала казалось, что это связано с оболочкой символов руководств, потому что я смог решить проблему, отредактировав направляющие и сохранив проект. Однако это был отвлекающий маневр... Я невольно решил проблему, изменив временные метки всех моих файлов csproj. Система сборки казалась намного более счастливой после того, как эти временные метки были обновлены, и она остановила раздражающее поведение (постоянные попытки пересобрать проекты, которые уже были построены).

Оболочка персонажей в руководствах вообще не была проблемой. Насколько я могу судить, VS 2019 подходит как для заглавных, так и для строчных букв. Они могут даже не соответствовать исходному проекту, основываясь на моих экспериментах (например, исходный исходный проект может использовать верхний регистр, а ссылающиеся проекты могут использовать нижний регистр).

Если это кому-то будет полезно, ниже представлена ​​оболочка PowerShell, которая позволит вам настроить время записи всех файлов csproj по пути. Это будет еще один трюк, который я использую всякий раз, когда система msbuild работает некорректно в Visual Studio.

$datenow = get-date

$files = Get-ChildItem -path "C:\Data\Workspaces\LumberTrack\" -recurse | where { $_.PSIsContainer -eq $false} 

foreach ($file in $files)
{
if ($file.Extension -eq ".csproj") 
{
   Set-ItemProperty $file.FullName LastWriteTime $datenow
   Write-Output $file.FullName
}    
}    
}    
Другие вопросы по тегам