You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

通过CSI.EXE运行脚本加载自定义app.config<section>遇System.Configuration异常

解决CSI.exe中ConfigurationManager无法加载脚本目录下自定义配置节程序集的问题

问题背景

我有程序集X,它依赖包含自定义<section>的app.config文件,在常规宿主应用中引用X时一切正常。app.config内容如下:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <section name="Foo" type="Foo.Common.FooConfigurationSectionHandler, Foo.Common" />
  </configSections>
  <Foo xmlns="http://mycompany.com/foo/configuration">
    <property name="Foo.Server.ConnectionString" value="zipzap" />
  </Foo>
</configuration>

现在通过C#脚本(csi.exe)引用X,脚本目录下有Foo.Common.dll,脚本中可以正常引用并使用该程序集的类,但当执行加载上述app.config的代码时,ConfigurationManager抛出错误,无法找到Foo.Common程序集。错误信息如下:

An error occurred creating the configuration section handler for Foo: Could not load file or assembly 'Foo.Common' or one of its dependencies. The system cannot find the file specified. (C:\stuff\desk\script\App.config line 4)
   at System.Configuration.BaseConfigurationRecord.FindAndEnsureFactoryRecord(String configKey, Boolean& isRootDeclaredHere)
   at System.Configuration.BaseConfigurationRecord.GetSectionRecursive(String configKey, Boolean getLkg, Boolean checkPermission, Boolean getRuntimeObject, Boolean requestIsHere, Object& result, Object& resultRuntimeObject)
   at System.Configuration.BaseConfigurationRecord.GetSection(String configKey)
   at System.Configuration.ConfigurationManager.GetSection(String sectionName)

测试发现将DLL放到csi.exe目录下可以正常运行,但实际场景中存在程序集冲突,无法这么做,需要解决办法。

解决方案

方法1:注册程序集解析事件

在脚本开头注册AppDomain.CurrentDomain.AssemblyResolve事件,当ConfigurationManager找不到程序集时,从脚本所在目录加载目标程序集。这种方法灵活性高,无需修改app.config:

#r "Foo.Common.dll"
#r "System.Configuration.dll"

using System;
using System.Configuration;
using System.Reflection;
using Foo.Common;

// 注册程序集解析事件,找不到程序集时从脚本目录加载
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
    var assemblyName = new AssemblyName(args.Name).Name;
    var scriptDir = AppDomain.CurrentDomain.BaseDirectory;
    var assemblyPath = System.IO.Path.Combine(scriptDir, $"{assemblyName}.dll");
    
    if (System.IO.File.Exists(assemblyPath))
    {
        return Assembly.LoadFrom(assemblyPath);
    }
    return null; // 返回null让系统继续执行默认查找逻辑
};

// 加载并使用配置节
var fooSection = ConfigurationManager.GetSection("Foo") as FooConfigurationSectionHandler;
// 后续业务代码...

方法2:修改app.config添加程序集探测路径

在app.config的<configuration>节点下添加<runtime>配置,指定脚本目录作为程序集探测路径。注意如果运行csi时的工作目录不是脚本所在目录,需要使用绝对路径:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <section name="Foo" type="Foo.Common.FooConfigurationSectionHandler, Foo.Common" />
  </configSections>
  <!-- 添加程序集探测配置 -->
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <!-- privatePath可填相对路径(基于csi运行工作目录)或绝对路径 -->
      <probing privatePath="C:\stuff\desk\script" />
    </assemblyBinding>
  </runtime>
  <Foo xmlns="http://mycompany.com/foo/configuration">
    <property name="Foo.Server.ConnectionString" value="zipzap" />
  </Foo>
</configuration>

方法3:返回已加载的程序集

脚本已经通过#r "Foo.Common.dll"加载了程序集,可在解析事件中直接返回该已加载的程序集,避免重复加载:

#r "Foo.Common.dll"
#r "System.Configuration.dll"

using System;
using System.Configuration;
using System.Reflection;
using Foo.Common;

// 预先加载并保存程序集引用
var loadedFooAssembly = Assembly.LoadFrom("Foo.Common.dll");

AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
    var requestedAssembly = new AssemblyName(args.Name);
    if (requestedAssembly.Name == loadedFooAssembly.GetName().Name)
    {
        return loadedFooAssembly;
    }
    return null;
};

// 加载配置节
var fooSection = ConfigurationManager.GetSection("Foo") as FooConfigurationSectionHandler;

内容的提问来源于stack exchange,提问作者Mud

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 08:32:29