【问题标题】:How can I load an assembly from a byte[] for use in a Razor view in ASP.NET Core?如何从 byte[] 加载程序集以在 ASP.NET Core 的 Razor 视图中使用?
【发布时间】:2020-11-18 19:08:23
【问题描述】:

背景 你好!我们的 .NET 5 Web 应用程序遵循“插件”架构。我们在整个网站上都有不同的模块,它们的独特性足以保证他们在我们的解决方案中拥有自己的项目。在应用程序启动时,我们的“主”应用程序会加载已编译的 DLL。目前,我们正在将我们的应用程序迁移到云端并转向微服务架构。最终,我们尝试将编译后的 DLL 存储在 AWS S3 中,在应用程序启动时下载并加载它们。

方法 我们能够毫无问题地从 S3 获取 DLL。据我们所知,有 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 不起作用。应用程序返回详细的错误消息(堆栈跟踪位于下方)。如果我们使用与 S3 完全相同的 DLL 并将其加载到临时位置,则 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)

感谢您的宝贵时间!

【问题讨论】:

  • 这是known issue
  • @JeroenMostert 这有助于解决我们的问题。非常感谢!

标签: c# .net-core .net-assembly


【解决方案1】:

直接从 Web 运行程序集不是一个好主意。对于初学者来说,如果 S3 宕机,您的所有应用程序都会宕机。如果 S3 很慢,那么您的所有应用程序都会缓慢启动。如果您的 S3 帐户被盗用,您的所有应用程序都会被盗用。如果您将程序集替换为破坏版本,则所有应用程序都将失效。问题层出不穷,有什么好处?

话虽如此,您的问题是 Razor 运行自己的编译,并且显然,考虑到堆栈跟踪,它想要从磁盘加载程序集元数据。您的 assembly-from-stream 在其中某处为空的路径。

直接的解决方案:将它们缓存在磁盘上,这是朝着您应该采取的方向迈出的良好一步,或者您真的确定要为每个应用程序池回收再次下载所有 DLL?

【讨论】:

  • 我们的应用程序此时有大约 10 个模块 - 我们将在 Elastic Beanstalk 或容器上托管它们。我们的目标是能够在不影响站点其余部分的情况下更新这些插件模块之一,因此将其托管在应用程序服务器之外并触发更新似乎是迄今为止最好的方式。对于我们如何做到这一点,您还有其他建议吗?
猜你喜欢
  • 2017-03-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多