По умолчанию VS при компиляции кладет все в одну директорию рядом с EXE.
Т.е и сборки и все остальное лежат в одной папке.
Как создать свою собственную структуру?
Например, я хочу, что бы одни сборки клались в одну папку, другие в другую, а в корне был EXE.
Ответ
Ну это как бы можно
Для начала, вы добавляете каталоги, в которых будут лежать ваши библиотеки, в assembly probing path. Для этого отредактируйте ваш app.config, добавьте в него следующее:
(Документация.)
Затем, в свойствах проекта в зависимостях укажите CopyLocal = False:
Затем, в свойствах проекта-библиотеки установите Output Path в каталог, где вы хотите получить эту DLL (для каждого из ваших target platform/processor):
Ну или в post-build step каждого из зависимых проектов укажите вручную копирование куда надо.
Всё, должно работать.
Как советует @Andrew B в комментариях, существует ещё один путь, описанный в документации: подписаться на событие AssemblyResolve, которое отправляется, если .NET не может загрузить сборку из вашего каталога.
Для этого нужен код наподобие такого:
AppDomain.CurrentDomain.AssemblyResolve += OnAssemblyResolve;
где метод OnAssemblyResolve определён так:
Assembly OnAssemblyResolve(object sender, ResolveEventArgs args)
{
var name = args.Name.Substring(0, args.Name.IndexOf(","));
// проверка на то, есть ли загружаемая сборка среди наших статических ссылок
// взята из https://support.microsoft.com/ru-ru/kb/837908
// но вам может быть и не нужна, подумайте сами
if (!Assembly.GetExecutingAssembly().GetReferencedAssemblies()
.Any(asmName => asmName.FullName.Substring(0, asmName.FullName.IndexOf(","))
== name))
{
// это не assembly из наших ссылок
return null;
}
var applicationDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
foreach (var subfolder in new[] { @"some\subfolder", @"another\subfolder" })
{
var path = Path.Combine(applicationDir, subfolder, name + ".dll";
if (File.Exists(path))
return Assembly.LoadFrom(path);
}
return null;
}
(Не тестировал.)
Достоинство этого решения — то, что список путей можно изменять динамически, если это вдруг понадобилось.
Недостаток — то, что у вас некоторые сборки будут грузиться в assembly binding-контексте LoadFrom в отличие от обычной загрузки в контексте Load. Типы, загруженные в таком контексте, будут несовместимы с типами, загруженными в обычном контексте. Особенно это может «клюнуть», если у вас в разных местах лежат разные версии одной и той же сборки. [Это может привести к эффекту, когда один и тот же статический класс существует в двух экземплярах.] Будьте осторожны и готовы к неожиданностям! (Вопрос также обсуждался здесь.)
Если ваши сборки приходят с ресурсами, возможно, вам нужно учесть эту модификацию
Дополнительное чтение по теме assembly binding/load context:
Best Practices for Assembly Loading
Suzanne Cook, LoadFile vs. LoadFrom
Для автоматического копирования сборок в целевые каталоги посредством MSBuild посмотрите ответы к этому вопросу
Но в целом, я бы порекомендовал воспользоваться советом @TomTom и не заморачиваться, а оставить всё как есть. То, что файлы лежат в одном каталоге, пользователь не увидит, если вы правильным образом поместите ярлыки в системное меню при инсталляции (а вы в любом случае должны это сделать).