Как я могу загрузить сборку из byte[] для использования в представлении Razor в ASP.NET Ядро?

#c# #.net-core #.net-assembly

#c# #.net-core #.net-сборка

Вопрос:

Фон Привет! Наше веб-приложение .NET 5 следует «подключаемой» архитектуре. У нас есть разные модули по всему веб-сайту, которые достаточно уникальны, чтобы гарантировать их собственные проекты внутри нашего решения. При запуске приложения наше «основное» приложение загружает скомпилированные библиотеки DLL. В настоящее время мы находимся в процессе переноса нашего приложения в облако и переходим к микросервисной архитектуре. В конечном счете, мы пытаемся сохранить скомпилированные библиотеки DLL в AWS S3, загрузить их при запуске приложения и загрузить их.

Методы, которые мы можем получить DLL из S3 без проблем. Насколько мы можем судить, существует 3 возможных способа загрузки DLL. Поток объектов из S3 называется «dll».

(Предпочтительно):

 var assm = System.Runtime.Loader.AssemblyLoadContext.Default.LoadFromStream(dll);
mvcBuilder.AddApplicationPart(assm);
  

(Менее благоприятно, но неплохо):

 using (var memoryStream = new MemoryStream())
{
     dll.CopyTo(memoryStream);
     var b =  memoryStream.ToArray();
     var assm = Assembly.Load(b);
     mvcBuilder.AddApplicationPart(assm);
}
  

(Наименее благоприятный):

 using (var fileStream = new FileStream(Environment.CurrentDirectory   "/tmp/Anon.dll", FileMode.Create,FileAccess.Write))
{
     dll.CopyTo(fileStream);
     var assm = Assembly.LoadFile(name);
     mvcBuilder.AddApplicationPart(assm);
}
  

Ошибки / неожиданное поведение
Проблема в том, что методы 1 и 2, которые являются наиболее благоприятными, не работают. Приложение возвращает подробное сообщение об ошибке (трассировка стека приведена ниже). Если мы используем ту же самую DLL из S3 и просто загружаем ее во временное местоположение, DLL загружается, как и ожидалось. Также стоит упомянуть, что если DLL уже хранится локально в виде файла, преобразована в поток и предпринята попытка загрузки — мы получаем ту же ошибку. Мы делаем что-то не так? Есть ли ограничение, о котором мы не знаем?

 ArgumentException: Empty path name is not legal. (Parameter 'path')
System.IO.FileStream..ctor(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options)
Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation.RazorReferenceManager.CreateMetadataReference(string path)
System.Linq.Enumerable SelectListIterator<TSource, TResult>.ToList()
System.Linq.Enumerable.ToList<TSource>(IEnumerable<TSource> source)
Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation.RazorReferenceManager.GetCompilationReferences()
System.Threading.LazyInitializer.EnsureInitializedCore<T>(ref T target, ref bool initialized, ref object syncLock, Func<T> valueFactory)
System.Threading.LazyInitializer.EnsureInitialized<T>(ref T target, ref bool initialized, ref object syncLock, Func<T> valueFactory)
Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation.RazorReferenceManager.get_CompilationReferences()
Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation.LazyMetadataReferenceFeature.get_References()
Microsoft.CodeAnalysis.Razor.CompilationTagHelperFeature.GetDescriptors()
Microsoft.AspNetCore.Razor.Language.DefaultRazorTagHelperBinderPhase.ExecuteCore(RazorCodeDocument codeDocument)
Microsoft.AspNetCore.Razor.Language.RazorEnginePhaseBase.Execute(RazorCodeDocument codeDocument)
Microsoft.AspNetCore.Razor.Language.DefaultRazorEngine.Process(RazorCodeDocument document)
Microsoft.AspNetCore.Razor.Language.DefaultRazorProjectEngine.ProcessCore(RazorCodeDocument codeDocument)
Microsoft.AspNetCore.Razor.Language.RazorProjectEngine.Process(RazorProjectItem projectItem)
Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation.RuntimeViewCompiler.CompileAndEmit(string relativePath)
Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation.RuntimeViewCompiler.OnCacheMiss(string normalizedPath)
Microsoft.AspNetCore.Mvc.Razor.Compilation.DefaultRazorPageFactoryProvider.CreateFactory(string relativePath)
Microsoft.AspNetCore.Mvc.Razor.RazorViewEngine.CreateCacheResult(HashSet<IChangeToken> expirationTokens, string relativePath, bool isMainPage)
Microsoft.AspNetCore.Mvc.Razor.RazorViewEngine.OnCacheMiss(ViewLocationExpanderContext expanderContext, ViewLocationCacheKey cacheKey)
Microsoft.AspNetCore.Mvc.Razor.RazorViewEngine.LocatePageFromViewLocations(ActionContext actionContext, string pageName, bool isMainPage)
Microsoft.AspNetCore.Mvc.Razor.RazorViewEngine.FindView(ActionContext context, string viewName, bool isMainPage)
Microsoft.AspNetCore.Mvc.ViewEngines.CompositeViewEngine.FindView(ActionContext context, string viewName, bool isMainPage)
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewResultExecutor.FindView(ActionContext actionContext, ViewResult viewResult)
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewResultExecutor.ExecuteAsync(ActionContext context, ViewResult result)
Microsoft.AspNetCore.Mvc.ViewResult.ExecuteResultAsync(ActionContext context)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResultFilterAsync>g__Awaited|29_0<TFilter, TFilterAsync>(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext<TFilter, TFilterAsync>(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters()
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
Microsoft.AspNetCore.Builder.RouterMiddleware.Invoke(HttpContext httpContext)
Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)
Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)
Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
  

Спасибо за ваше время!

Комментарии:

1. Это известная проблема .

2. @JeroenMostert Это помогло решить нашу проблему. Большое вам спасибо!

Ответ №1:

Запускать сборки непосредственно из Интернета не очень хорошая идея. Для начала, если S3 не работает, все ваши приложения не работают. Если S3 работает медленно, все ваши приложения запускаются медленно. Если ваша учетная запись S3 будет скомпрометирована, все ваши приложения будут скомпрометированы. Если вы замените сборку на последнюю версию, все приложения будут мертвы. Список проблем бесконечен, с какими преимуществами?

При этом ваша проблема заключается в том, что Razor запускает собственную компиляцию, и, по-видимому, с учетом трассировки стека хочет загрузить метаданные сборки с диска. В вашей сборке из потока где-то есть путь, который равен null.

Немедленное решение: кэшировать их на диске, что является хорошим шагом в направлении, которое вы должны предпринять в любом случае, или вы действительно уверены, что хотите снова загружать все библиотеки DLL для каждой переработки пула приложений?

Комментарии:

1. На данный момент в нашем приложении около 10 модулей — мы будем размещать их либо на Elastic Beanstalk, либо в контейнерах. Наша цель — иметь возможность обновлять один из этих модулей плагинов, не затрагивая остальную часть сайта, поэтому размещение его за пределами сервера приложений и запуск обновления, по-видимому, пока лучший способ. Есть ли у вас какие-либо другие рекомендации о том, как мы могли бы это сделать?