Assembly.GetManifestResourceStream加载嵌入资源失败,StreamReader报空引用异常
我之前也踩过完全一样的坑!明明看起来路径拼对了,资源属性也设对了,就是读不到——咱们一步步来定位和解决:
第一步:先拿到资源的真实嵌入名称
手动拼接命名空间+文件名很容易出错(比如文件夹层级、大小写、命名空间嵌套),不如直接让程序把所有嵌入资源列出来。运行这段代码,复制输出里对应SQL文件的完整名称:var assembly = Assembly.GetExecutingAssembly(); foreach (var resource in assembly.GetManifestResourceNames()) { Console.WriteLine(resource); }把复制的名称直接替换你拼接的
resourceName,先验证能不能正常读取——这一步90%能解决问题,因为很多时候我们以为的路径和实际嵌入的路径不一样。第二步:再核对资源的Build Action属性
别光看属性面板显示的是Embedded Resource,右键资源文件→属性,确认Build Action下拉框真的选了Embedded Resource,不是Content或者None。如果资源在子文件夹里,嵌入名称会自动把文件夹路径转成点分隔(比如MyNamespace.SubFolder.myquery.sql),这时候手动拼接要把文件夹名也加上。第三步:确认程序集是否匹配
你用Assembly.GetExecutingAssembly()获取当前运行的程序集,但要注意:如果你的SQL资源是放在另一个类库项目里的,那应该用那个类库的Assembly来读取,比如:// 用类库中任意一个类的Type来获取对应的程序集 var assembly = Assembly.GetAssembly(typeof(ClassInResourceProject));因为
GetExecutingAssembly()取的是当前执行代码所在的程序集,如果资源不在这个项目里,肯定读不到。第四步:处理大小写和特殊字符
.NET对嵌入资源名称的大小写是敏感的!比如你拼接的是myquery.sql,但实际文件名叫MyQuery.sql,就会匹配失败。另外文件名里的空格、下划线也要注意,确保拼接后的名称和实际嵌入名称完全一致。最后:给代码加空值保护
为了避免后续再出现ArgumentNullException,建议在创建StreamReader前先判断流是否为空,比如:var namespace1 = typeof(Toolbox).Namespace; var name1 = name.Replace('\\', '.'); string resourceName = $"{namespace1}.{name1}"; var assembly = Assembly.GetExecutingAssembly(); using (Stream manifestResourceStream = assembly.GetManifestResourceStream(resourceName)) { if (manifestResourceStream == null) { throw new InvalidOperationException($"找不到嵌入式资源:{resourceName}\n可用资源列表:\n{string.Join("\n", assembly.GetManifestResourceNames())}"); } using (StreamReader streamReader = new StreamReader(manifestResourceStream)) { return streamReader.ReadToEnd(); } }这样如果资源找不到,错误信息里会直接列出所有可用的嵌入资源,方便你快速对比问题所在。
内容的提问来源于stack exchange,提问作者Zabih Khaliqi

