网站首页 > 技术文章 正文
最近,我的任务是编写一个数据提取/报告框架,该框架将使客户端应用程序能够定义要执行的存储过程,并将结果通过电子邮件发送给感兴趣的各方。使该项目变得有趣的是允许用户从一组查找表,Web服务数据和应用程序派生数据中指定参数值的要求。在没有任何外部引用的情况下,检索Web服务或应用程序派生数据的要求需要一种机制来执行未引用程序集中的方法。
本文概述了我的方法,该方法允许框架代码通过部分类型名解析来执行未引用程序集中的方法。
问题
查找表数据的预填充非常简单-从表中选择Id和Value字段-这是通过一个简单的ParameterLookup表实现的。但是,当有时间用Web服务数据或特定于应用程序的数据预先填充查找时,我们需要某种机制来执行客户端应用程序中的方法。在没有对客户端应用程序的任何引用的情况下,我们需要一种机制来解析和执行客户端应用程序代码。我们实现的方法是LookupClass在ParameterLookup表中包含一个字段,并让框架代码解析类型并执行该search方法。
有许多常用的方法允许.NET代码解析和执行未引用类型的方法,尽管并非所有方法都适用于所有环境。当宿主进程是COM+进程时,Type.GetType()可以将调用与部分限定的类型名称(即MyCompany.MyApplication.Search.Lookup.SomeLookup,MyCompany.MyApplication)一起使用。但是,当主机进程是IIS时,必须指定完全限定的类型名称(“类型名称”,“程序集名称”,“版本”,“区域性”,“公钥”),或者该程序集必须存在于Web程序包的/bin目录中。
不希望ParameterLookup每次执行新版本的应用程序时都强制客户端应用程序更新表,我们需要一种仅使用Type和Assembly名称部分的机制。
解决方案
如果您没有完全限定的类型名称(类型名称,程序集类型,版本,区域性和公共密钥),则在未引用的程序集中定义的类型可以是正确的PITA。
例如,类型Microsoft.Build.BuildEngine.Engine类型的完全限定名称为Microsoft.Build.BuildEngine.Engine,Microsoft.Build.Engine,Culture=neutral,Version=4.0.0.0,PublicKeyToken=b03f5f7f11d50a3a(真是令人讨厌!)。对于相对稳定的组件,可以在配置表/文件中指定完全限定的类型名称,但是对于版本号更改为相对不稳定的不稳定组件(例如,来自客户端应用程序的组件),您该怎么做?每个版本?
Type.GetType()
该Type.GetType()功能可用于加载在项目中引用或在程序集搜索路径中的程序集。
Type t = Type.GetType("MyCompany.MyApplication.Lookup.SomeLookup, MyCompany.MyApplication "); 1复制代码类型:[html]
由于框架代码不会引用该程序集,因此我们唯一的希望是该程序集位于程序集搜索路径中。尽管这种情况是可能的,但肯定不能保证。
该解决方案必须丢弃。
如果我们为Type.GetType()函数提供完全限定的类型名称该怎么办?
Type t = Type.GetType("MyCompany.MyApplication.Lookup.SomeLookup,
MyCompany.MyApplication, Culture=neutral, Version=1.0.0.3245, PublicKey= 1e9a8d893e3afa78");12复制代码类型:[html]
此调用肯定有效,并且类型引用已成功返回。所需要做的就是将此完全合格的类型传递给框架代码-相对简单的练习。las,当重新编?译程序集时,将使用不同的内部版本号(假定您使用的是内部版本号)创建该组件的新版本,该调用将返回原始程序集;否则,将返回原始程序集。不是更新的程序集。这是不理想的。
该解决方案必须丢弃。
我需要的是一种无需指定版本号或无需依靠程序集搜索路径中的程序集即可解决类型的方法。
Assembly.LoadFrom()
该Assembly.LoadFrom()方法看起来很有希望,因为我可以使用此函数来加载程序集而无论其位置如何,并遍历该程序集中定义的每种类型。
internal static Type GetTypeFromAssembly(string assemblyName, string typeName)
{
Assembly assembly = Assembly.LoadFrom(gacPath);
Type[] types = assembly.GetTypes();
foreach (Type type in types)
{
if (type.FullName == typeName))
return type;
}
return null;
}123456789101112复制代码类型:[html]
条件if(type.FullName==typeName)使我们不必指定完全限定类型的版本,区域性或公钥属性。现在,我们可以加载程序集并提取适当的类型以供使用。
但是,这导致了代码和安装实现之间的依赖性。如果支持操作员决定修改应用程序安装参数,则可以将软件安装到未知位置。可以想象,我们可能要求将软件安装到特定位置(例如ProgramFiles),但这会导致32位/64位问题(ProgramFiles是指“ProgramFiles”还是“ProgramFiles(x86)”?)。
但我想我快到了。
融合
如果将拥有我们所要类型的程序集安装到全局程序集缓存中,则我们可以利用CLR程序集管理系统为我们加载并找到我们的程序集。
使用Fusion.dll程序集(有关详细信息,请参阅此链接),我们可以创建对适当的GAC(32位或64位)的引用,并使用该Assembly.LoadFrom()方法安全地加载我们的程序集。
[DllImport("fusion.dll")]
internal static extern int CreateAssemblyCache(out IAssemblyCache ppAsmCache, int reserved);12复制代码类型:[html]
private static string GetAssemblyPath(string name)
{
if (name == null)
throw new ArgumentNullException("name");
string finalName = name;
AssemblyInfo aInfo = new AssemblyInfo();
aInfo.cchBuf = 1024; // should be fine...
aInfo.currentAssemblyPath = new String('\0', aInfo.cchBuf);
IAssemblyCache ac;
int hr = CreateAssemblyCache(out ac, 0);
if (hr >= 0)
{
hr = ac.QueryAssemblyInfo(0, finalName, ref aInfo);
if (hr < 0)
return null;
}
return aInfo.currentAssemblyPath;
}123456789101112131415161718192021复制代码类型:[html]
public static Type ResolveType(string assemblyName, string typeName)
{
string gacPath = GetAssemblyPath(assemblyName);
return GetTypeFromAssembly(assemblyName, typeName);
}12345复制代码类型:[html]
仅使用类型和程序集的名称,代码将搜索适合于应用程序环境(x86/x64)的GAC目录,并返回对该类型的引用。引用正确的类型后,对的调用Activator.CreateInstance()将创建要使用的可用类型。
使用代码
框架定义了要解析的类型必须实现的接口。
namespace FrameworkApp
{
public interface ILookup
{
string Search();
}
}1234567复制代码类型:[html]
...当类实现接口时。
public class SearchType1 : ILookup
{
public string Search()
{
return string.Format("SearchType1.Search() from version {0}",
Assembly.GetExecutingAssembly().GetName().Version);
}
}12345678复制代码类型:[html]
现在,我们只需要调用TypeResolver.ResolveType(),创建一个返回类型的实例(强制进入选择的接口),最后执行所需的方法。
Type type = TypeResolver.ResolveType("ClientApp.SearchType1,ClientApp");
ILookup lookup = Activator.CreateInstance(type) as ILookup;
lookup.Search();123复制代码类型:[html]
局限性
程序集必须在GAC中注册才能使此解决方案生效
不会加载不兼容的.NET版本程序集
将解析为最高版本,而不是最新编译的版本
猜你喜欢
- 2024-09-27 微软发布 .Net Core 3.0 版重大更新,对开发者来说意味着什么?
- 2024-09-27 生信分析过程中这些常见文件的格式以及查看方式你都知道吗?
- 2024-09-27 .NET 各种混淆的含义、原理、实际效果和不同级别的差异
- 2024-09-27 Swashbuckle.AspNetCore3.0的二次封装与使用
- 2024-09-27 《Inside C#》笔记(十四)反射(c# dynamic 反射)
- 2024-09-27 Wasmer可以在各种操作系统上运行WebAssembly
- 2024-09-27 基于Log4Net组件快速实现日志记录
- 2024-09-27 Serilog源码解析——使用方法(sed源码)
- 2024-09-27 聊一聊被 .NET程序员 遗忘的 COM 组件
- 2024-09-27 如何在VS2019中编译C#.NET Core控制台应用程序并分析生成后的文件
- 1515℃桌面软件开发新体验!用 Blazor Hybrid 打造简洁高效的视频处理工具
- 577℃Dify工具使用全场景:dify-sandbox沙盒的原理(源码篇·第2期)
- 514℃MySQL service启动脚本浅析(r12笔记第59天)
- 487℃服务器异常重启,导致mysql启动失败,问题解决过程记录
- 486℃启用MySQL查询缓存(mysql8.0查询缓存)
- 470℃「赵强老师」MySQL的闪回(赵强iso是哪个大学毕业的)
- 450℃mysql服务怎么启动和关闭?(mysql服务怎么启动和关闭)
- 448℃MySQL server PID file could not be found!失败
- 最近发表
-
- 宝塔面板Nginx如何提高网站访问速度?
- 接口调试工具ApiPost中form-data/x-www-form-urlencoded/raw区别
- 高并发场景下,Nginx性能如何提升10倍?
- 高并发场景下,Nginx如何抗住千万级流量?
- 浏览器中在线预览pdf文件,pdf.mjs插件实现web预览pdf
- 为什么你的网站加载慢?90%的人忽略了这2个设置。
- 别再无脑复制Nginx配置了!掌握这10个"性能核弹"级参数
- 你的Nginx配置,可能就是你网站最慢的一环,注意这几个优化参数
- 深入浅出HTTP压缩技术(http2压缩)
- C程序设计之:1-1/2+1/3-... + 1/n 的和
- 标签列表
-
- cmd/c (90)
- c++中::是什么意思 (83)
- 主键只能有一个吗 (66)
- c#console.writeline不显示 (75)
- pythoncase语句 (81)
- es6includes (73)
- windowsscripthost (67)
- apt-getinstall-y (86)
- node_modules怎么生成 (76)
- c++int转char (75)
- static函数和普通函数 (76)
- el-date-picker开始日期早于结束日期 (70)
- js判断是否是json字符串 (67)
- checkout-b (67)
- c语言min函数头文件 (68)
- asynccallback (71)
- localstorage.removeitem (74)
- vector线程安全吗 (70)
- & (66)
- java (73)
- js数组插入 (83)
- mac安装java (72)
- eacces (67)
- 查看mysql是否启动 (70)
- 无效的列索引 (74)