首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >将更多dll加载到代码中会导致崩溃

问将更多dll加载到代码中会导致崩溃
EN

Stack Overflow用户
提问于 2017-02-01 17:13:10
回答 2查看 425关注 0票数 6

我正在帮助开发的系统的一个需求是能够导入文件,我们有一组适配器来处理不同类型的文件(csv,xml等)。我们预计会遇到。在开发的早期阶段,我们通过引用和使用命令对数据适配器进行硬编码。显然,当它上线时,我们想要的情况是,我们可以只编写一个新的适配器,并将dll放入文件夹中,然后在不重新编译代码的情况下运行过程。

为了实现这一点,我改编了this question中的代码。有问题的代码位于构造函数中,如下所示

代码语言:javascript
复制
string dllLocation = @"C:MyLocation\dllLocation";
DirectoryInfo dir = new DirectoryInfo(dllLocation);
var tempfiles = dir.GetFiles("*Adapter*.dll", SearchOption.AllDirectories);     // This will need to be changed when we go live

    foreach (var file in tempfiles)
    {
        Assembly tempAssembly = null;

        //Before loading the assembly, check all current loaded assemblies in case already loaded
        //has already been loaded as a reference to another assembly
        //Loading the assembly twice can cause major issues
        foreach (Assembly loadedAssembly in AppDomain.CurrentDomain.GetAssemblies())
        {
            //Check the assembly is not dynamically generated as we are not interested in these
            if (loadedAssembly.ManifestModule.GetType().Namespace != "System.Reflection.Emit")
            {
                //Get the loaded assembly filename
                string loadedFilename = loadedAssembly.CodeBase.Substring(loadedAssembly.CodeBase.LastIndexOf('/') + 1);

                //If the filenames match, set the assembly to the one that is already loaded
                if (loadedFilename.ToUpper() == file.Name.ToUpper())
                {
                    tempAssembly = loadedAssembly;
                    break;
                }
            }
        }

        //If the assembly is not aleady loaded, load it manually
        if (tempAssembly == null)
        {
            tempAssembly = Assembly.LoadFrom(file.FullName);
        }

        Assembly a = tempAssembly;

稍后,当该方法运行时,我们将获得以下内容

代码语言:javascript
复制
private IEnumerable<IUniversalDataAdapter> DataAdapters
{
    get
    {
        foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
        {
            foreach (var type in asm.GetTypes().Where(x => x.GetInterfaces().Contains(typeof(IUniversalDataAdapter))))
            {
                if (type.IsAbstract) continue; // can't create abstract classes

                if (!dataAdapters.Any(y => y.GetType().Equals(type)))
                {
                    IUniversalDataAdapter adapter = (IUniversalDataAdapter)Activator.CreateInstance(type);
                    dataAdapters.Add(adapter);
                }
            }
        }
        return dataAdapters;
    }
}

它成功地加载了数据适配器,并允许按照我所期望的那样使用它们。

现在问题来了,在第一段代码中,我们有一行

代码语言:javascript
复制
var tempfiles = dir.GetFiles("*Adapter*.dll", SearchOption.AllDirectories);

如果我将其更改为

代码语言:javascript
复制
var tempfiles = dir.GetFiles("*.dll", SearchOption.AllDirectories);

在此例程运行第二段代码之前,例程崩溃

代码语言:javascript
复制
private IEnumerable<IDataValidator> DataValidators
{
    get
    {
        if (validators.Count == 0)
        {
            foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
            {
                foreach (var type in asm.GetTypes().Where(x => x.GetInterfaces().Contains(typeof(IDataValidator))))
                {
                    if (!type.IsAbstract)
                    {
                        var validator = (IDataValidator)Activator.CreateInstance(type, context);
                        validators.Add(validator);
                    }
                }
            }
        }
        return validators;
    }
}

编辑:添加异常

代码语言:javascript
复制
System.Reflection.ReflectionTypeLoadException was unhandled
  HResult=-2146232830
  Message=Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information.
  Source=mscorlib
  StackTrace:
     at System.Reflection.RuntimeModule.GetTypes(RuntimeModule module)
     at System.Reflection.RuntimeModule.GetTypes()
     at System.Reflection.Assembly.GetTypes()
     at TTi.Data.Pipeline.Server.Common.DataPipeline.get_DataValidators() in C:\Users\anorcross\Source\Workspaces\Universal System\Data\Main\TTi.Data\TTi.Data.Pipeline.Server.Common\DataPipeline.cs:line 124
     at TTi.Data.Pipeline.Server.Common.DataPipeline.CheckConfiguration(DataConfiguration config) in C:\Users\anorcross\Source\Workspaces\Universal System\Data\Main\TTi.Data\TTi.Data.Pipeline.Server.Common\DataPipeline.cs:line 528
     at TTi.Data.Pipeline.Server.Common.DataPipeline.ProcessDataSource(IDataSource dataSource, DataConfiguration config) in C:\Users\anorcross\Source\Workspaces\Universal System\Data\Main\TTi.Data\TTi.Data.Pipeline.Server.Common\DataPipeline.cs:line 213
     at TTi.Data.Test.Program.ImportTest(String testFolders) in C:\Users\anorcross\Source\Workspaces\Universal System\Data\Main\TTi.Data\TTi.Data.Test\Program.cs:line 362
     at TTi.Data.Test.Program.Main(String[] args) in C:\Users\anorcross\Source\Workspaces\Universal System\Data\Main\TTi.Data\TTi.Data.Test\Program.cs:line 48
     at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
     at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
     at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
     at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
     at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
     at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
     at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
     at System.Threading.ThreadHelper.ThreadStart()
InnerException: 

EndEdit:

DataValidators绝对会在DataAdapters例程之前运行。DataValidators只是包含在主代码库中的几位代码,并进行检查以确保导入的数据具有预期的格式。在这一点上,我们只是加载它们,这样我们就可以确保所需的文件存在。

查看加载的程序集,两个版本的代码都根据需要加载适配器,但第二个版本的加载比第一个版本更多,正如我所预期的那样。

那么,为什么第二个版本的tempfiles会在看起来完全不相关的代码部分崩溃呢?如果我们添加了足够多的数据适配器,会导致代码崩溃吗?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-02-01 17:45:18

此异常说明找不到所需的dll,因此无法加载它。现在,他正在寻找哪个dll,为什么它在编译时没有失败?要回答第一个问题,您需要更深入地研究,最终您会发现您的代码加载了一些您不使用或不需要的奇怪的dll。这可能是因为代码迭代了所有加载的程序集中的所有类型。可以有一个dll作为引用,该dll公开使用您未引用其他dll中的某些类型的类型。只要你不使用那个类型就可以了。

为了解决这个问题,让你的循环更具体。很明显,您的适配器不会在任何系统dll中声明,那么为什么要迭代这些dll呢?

代码语言:javascript
复制
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies().Where(IsMyAssembly))
            {
                foreach (var type in asm.GetTypes().Where(x => x.GetInterfaces().Contains(typeof(IDataValidator))))
                {
//...

使用Adapter的第一次文件扫描比没有使用它的文件扫描更好,因为它更具体。我建议让你的代码更具体,例如在一些配置文件中显式注册类型,这将完全消除dll扫描。

票数 1
EN

Stack Overflow用户

发布于 2017-02-01 17:46:32

当我们编写一个从dll获取所有匹配类型的函数时,我们遇到了完全相同的问题。请注意try catch。

代码语言:javascript
复制
   private static IEnumerable<Type> MatchingTypesFromDll<TBaseType>(string dllPath)
    {
        try
        {

            return Assembly
                .LoadFrom(dllPath)
                .GetExportedTypes()
                .Where(TypeSatisfies<TBaseType>);

        }
        catch (Exception e)
        {
                Debug.WriteLine($"Exception {e} when trying to load from {dllPath}");
                return new Type[] {};
        }
    }

我们只是忽略了异常。即使在nuget包中,也有一些原生dll或可能名为.dll的文件并不是您所期望的。仅仅捕获异常并忽略损坏的文件要比试图抢先找出哪些是托管的dlls,哪些不是。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/41975873

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档