Встроенные ресурсы
Использование встроенных ресурсов
Что это такое и зачем это нужно
Embedded Resources — это механизм в скриптах, который позволяет «нести с собой» любые файлы прямо рядом со скриптом: изображения, видео, ML-модели, исполняемые файлы и даже DLL.
Изображения можно использовать в вашем кастомном UI, видео — для обучающих материалов, а DLL — для дополнительного кода, который по каким-то причинам неудобно поставлять как пакет NuGet.
NuGet
Обратите внимание: если библиотека уже есть на NuGet, гораздо надежнее подключать её через #r "nuget:" и ссылаться на пакет, а не на «сырую» DLL.
Как добавить
Щелкните правой кнопкой мыши по дереву файлов скрипта (его показывает кнопка Show Files) → Add Existing File...

Либо воспользуйтесь кнопкой New.
После добавления файл появится в списке, и его можно будет выбрать.

Как удалить
Чтобы удалить ресурс, просто удалите файл из списка.
Как использовать
Файлы включаются в сборку скрипта. Провайдер даёт доступ к их содержимому; сам по себе ресурс не становится загруженной моделью, шрифтом или исполняемой DLL.
Прочитать текст через IScriptFileProvider
Добавьте через Show Files файл docs/help.txt с текстом Справка проекта.
Теперь в Script.csx можно открыть поток и вывести содержимое:
using System.IO;
using EyeAuras.Repl;
{
var files = GetService<IScriptFileProvider>();
var file = files.GetFileInfo("docs/help.txt");
if (!file.Exists)
{
Log.Warn("Ресурс docs/help.txt не найден");
return;
}
using var stream = file.CreateReadStream();
using var reader = new StreamReader(stream);
Log.Info(reader.ReadToEnd());
}
Ожидаемый результат — текст файла в журнале. Поток и reader закрываются
при выходе из блока. Провайдер получает скрипт; обычному C#-классу передайте
IScriptFileProvider явно через конструктор или параметр.
Через Blazor
Если вы строите UI через Blazor, файлы можно использовать напрямую. Например, если вы добавили в скрипт Image 12.jpg, то для отображения достаточно написать:
<img src="Image 12.jpg" />
Это разметка Razor-компонента окна, которое предоставляет файлы скрипта. Подготовка окна — в Blazor Windows. В обычной внешней HTML-странице такой путь сам по себе ресурс сборки не открывает.
Как подключаемую DLL
По аналогии с пакетами NuGet, вы можете положить нужную DLL прямо в скрипт и при этом сохранить автодополнение, определение типов и прочие удобства.
Для этого нужно:
- добавить в скрипт нужную .NET-сборку, например
MyDll.dll - в
Script.csxнаписать
#r "assemblyPath: MyDll.dll"
Готово. При компиляции скрипта EyeAuras найдет подходящую DLL среди ресурсов проекта и использует её. А при запуске скрипта загрузит её из ресурсов. Никаких отдельных файлов рядом держать не нужно.
Экспорт/импорт C# Solution
EyeAuras умеет экспортировать скрипт как полноценный C# .sln, который потом можно открыть в JetBrains Rider или Visual Studio.
Embedded resources (файлы) тоже экспортируются и помечаются как Embedded Resource.
Например, для Image 12.jpg произойдет следующее: во-первых, сам файл будет экспортирован в целевую папку, а во-вторых, в экспортированном файле проекта появится:
<ItemGroup>
<EmbeddedResource Include="Image 12.jpg" />
<None Remove="Image 12.jpg" />
</ItemGroup>
Это говорит C#-компилятору включить Image 12.jpg как embedded resource внутрь итоговой DLL.
При обратном импорте .sln в EyeAuras читаются элементы Embedded Resource, и вы увидите их как файлы в списке.
В Visual Studio/Rider это соответствует Build Action = EmbeddedResource. Если вы открыли экспортированный проект, можно:
- добавлять и удалять файлы прямо в папке проекта
- в
.csprojуказывать<EmbeddedResource Include="..."/>для каждого ресурса - при необходимости добавлять
<None Remove="..."/>, чтобы файл не копировался в выходную папку как обычный контент
При импорте обратно EyeAuras просканирует .csproj, найдет все EmbeddedResource-элементы и покажет их в списке файлов скрипта. Имена и относительные пути сохраняются — это важно для корректного доступа из кода и Blazor. Здесь используется явный Include: импорт не является полным выполнением произвольных MSBuild targets или wildcard-правил.

Как это устроено внутри
При сборке скриптов EyeAuras старается максимально следовать логике официального компилятора: добавленные вами файлы включаются в итоговую DLL как Embedded Resources и доступны через Assembly.GetManifestResourceNames и Assembly.GetManifestResourceStream.
Кратко о .NET-методах:
Assembly.GetManifestResourceNames()— возвращает полный список имен ресурсов, например"DefaultNamespace.folder.file.ext"Assembly.GetManifestResourceStream(fullName)— открывает поток для чтения ресурса по его ПОЛНОМУ имени
Чтобы увидеть исходные имена, выполните в Script.csx:
foreach (var name in typeof(Script).Assembly.GetManifestResourceNames())
{
Log.Info(name);
}
Script здесь — сгенерированный тип submission, а не глобальный тип для
обычной библиотеки. Для открытия через GetManifestResourceStream нужно
точное имя из этого списка и закрытие полученного потока. Обычно задача
проще решается провайдером из примера выше.
В EyeAuras удобнее использовать IScriptFileProvider, потому что он сам сопоставляет пути и находит нужный ресурс.
Именование
У C# Embedded Resources есть одна особенность: когда ресурсы попадают в DLL, их относительный путь «сворачивается» и превращается в namespace.folder.filename.
Как формируется полное имя ресурса во время сборки:
- берется
DefaultNamespaceпроекта (если он не задан — имя проекта) - слеши в относительном пути файла заменяются на точки
- результат:
FullName = DefaultNamespace + "." + relative/path/with/dots
Пример: проект с namespaceMyScriptи файлImages/logo.png→ ресурс:MyScript.Images.logo.png
Как искать ресурс в срезе EyeAuras 20fd3d4:
- Полное manifest name и полная форма со слешами принимаются без учёта регистра.
- Путь с подпапкой, например
Images/logo.png, допускает поиск по границе суффикса manifest name. Дополнительно рассматриваются путиwwwroot. - Произвольное имя
logo.pngне гарантирует поиск по любой папке. Используйте однозначный путь с подпапкой или полную идентичность. - Если кандидатов несколько, провайдер пишет предупреждение и возвращает
IFileInfoсExists == false. Первый случайный ресурс не выбирается.
Например, для manifest name MyScript.Images.logo.png используйте полное
имя или MyScript/Images/logo.png; Images/logo.png пригоден, если совпадение
однозначно. Не считайте произвольный частичный путь с точками эквивалентом
пути со слешами.
GetDirectoryContents показывает диагностическую проекцию полных имён в
дерево. Это не обещание восстановить исходную структуру папок проекта.
Прочитать большой ресурс без дополнительного массива
Добавьте файл video/tutorial.mp4 через Show Files. Этот пример не
воспроизводит видео: он подсчитывает прочитанные байты, не создавая массив
размером со весь файл. Выполните в Script.csx:
using System.IO;
using EyeAuras.Repl;
{
var file = GetService<IScriptFileProvider>().GetFileInfo("video/tutorial.mp4");
if (!file.Exists) { Log.Warn("Видео не найдено"); return; }
using var stream = file.CreateReadStream();
var buffer = new byte[8192];
long total = 0;
int count;
while ((count = stream.Read(buffer, 0, buffer.Length)) > 0)
{
cancellationToken.ThrowIfCancellationRequested();
total += count;
}
Log.Info($"Прочитано {total} байт");
}
Для JSON, шрифта или ML-модели чтение — только первый шаг. Далее нужен
конкретный тип конфигурации или API библиотеки-потребителя. Не подменяйте
загрузку модели получением массива байтов. ReadAllBytes подходит, когда
потребитель требует именно весь массив.
Загрузка DLL из ресурсов — как это работает
-
На этапе компиляции
- В
Script.csxдобавьте директиву:#r "assemblyPath: MyLib.dll" - EyeAuras найдет файл
MyLib.dllв ресурсах и добавит его как ссылку. Это даст автодополнение, определение типов и успешную компиляцию.
- В
-
Во время выполнения
- EyeAuras подписывается на
AssemblyLoadContext.Resolvingи пытается загрузить сборку из embedded resources.
- EyeAuras подписывается на
Если вы уже добавили совместимую Newtonsoft.Json.dll в файлы скрипта, можно проверить её вызовом:
#r "assemblyPath: Newtonsoft.Json.dll"
using Newtonsoft.Json;
Log.Info(JsonConvert.SerializeObject(new { Hello = "World" }));
Как вывести список всех доступных имен
- Через .NET API (полные имена):
var asm = typeof(Script).Assembly;
foreach (var name in asm.GetManifestResourceNames())
{
Log.Info($"Resource: {name}");
}
- Через file provider (
IFileProvider):
var files = GetService<IScriptFileProvider>();
foreach (var fi in files.GetDirectoryContents("/"))
{
Log.Info($"Found: {fi.Name} ({fi.Length} b)");
}
GetService<IScriptFileProvider>() может вернуть CompositeScriptFileProvider:
провайдер ресурсов сборки объединяется с существующим провайдером. У первого
имеющего файл провайдера есть приоритет, но внутри embedded-провайдера
неоднозначный поиск остаётся NotFound. Поэтому приведение сервиса к
ScriptEmbeddedResourceFileProvider не гарантирует список manifest names;
для сборки используйте пример выше.
FAQ и нюансы
-
Файл не находится
- Убедитесь, что он добавлен в список файлов скрипта
- Попробуйте указать подпапку: вместо
"logo.png"→"Images/logo.png" - Проверьте регистр, если используете полное имя
-
Дубликаты имен
- Если есть два файла с одинаковым окончанием пути (
a/logo.pngиb/logo.png), неоднозначный запрос вернётNotFound. Уточняйте путь через подпапку или полное manifest name.
- Если есть два файла с одинаковым окончанием пути (
-
Большие файлы
- Читайте их как поток через
CreateReadStream()— не держите всё целиком в памяти
- Читайте их как поток через
-
Я изменил файл, но не вижу обновлений
- Embedded resources вшиваются в DLL. Обновите ресурс и перекомпилируйте скрипт, чтобы новые байты попали в сборку.
Watchembedded-провайдера возвращаетNullChangeTokenи не следит за изменением старой сборки.
- Embedded resources вшиваются в DLL. Обновите ресурс и перекомпилируйте скрипт, чтобы новые байты попали в сборку.
-
DLL не загружается
- Проверьте, что
#r "assemblyPath: ..."указывает на правильный файл.
- Проверьте, что
-
Точки или слеши в путях?
- Используйте полное manifest name либо путь со слешами. Не обещайте одинаковый поиск по суффиксу для любого частичного имени с точками.
Контракты — EyeAuras
20fd3d4, PoeEye0e02c98; точные сигнатуры сверяйте с SDK/XML своей версии.
- Используйте полное manifest name либо путь со слешами. Не обещайте одинаковый поиск по суффиксу для любого частичного имени с точками.
Контракты — EyeAuras