使用同一个 dll 的 2 个不同版本?
Using 2 different versions of the same dll?
我得到了 2 个预编译的 dll:
Common.Data, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f3b12eb6de839f43, processorArchitecture=MSIL
Common.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=f3b12eb6de839f43, processorArchitecture=MSIL
API 中的差异示例:
我正在尝试将两者加载到一个项目中以执行如下操作:
extern alias v10;
extern alias v20;
private static void UpgradeUser()
{
// Load old user
var userOld = new v10::Common.Data.UserData();
userOld.loadData("user.dat");
// Create new user
var userNew = new v20::Common.Data.UserData();
// Copy properties
userNew.FirstName = userOld._firstName;
userNew.LastName = userOld._lastName;
userNew.Age = userOld._age;
// Invoke method from v10 and v20 API
userOld.version();
userNew.DisplayVersion();
if (userNew.GetUserInfo() != userOld.getInfo())
{
throw new Exception("Discrepencies in upgrade ");
}
Console.WriteLine("Upgrade done!");
}
我已经设置了我的项目引用和 app.config
以下内容。我还手动将 dll 复制到我的输出文件夹中,匹配 appconfig hrefs
<!-- [Edited: see history for previous version] -->
<Reference Include="Common.Data, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f3b12eb6de839f43, processorArchitecture=MSIL">
<HintPath>libs\Common.Data.1_0_0_0.dll</HintPath>
<Aliases>v10</Aliases>
</Reference>
<Reference Include="Common.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=f3b12eb6de839f43, processorArchitecture=MSIL">
<HintPath>libs\Common.Data.2_0_0_0.dll</HintPath>
<Aliases>v20</Aliases>
</Reference>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Common.Data" publicKeyToken="f3b12eb6de839f43" culture="neutral" />
<codeBase version="1.0.0.0" href="libs\Common.Data.1_0_0_0.dll" />
<codeBase version="2.0.0.0" href="libs\Common.Data.2_0_0_0.dll" />
</dependentAssembly>
</assemblyBinding>
</runtime>
我已经成功构建了。
但是当我尝试 运行 它时,我得到了 UserData.loadData
的 MissingMethodException
。
我浏览了多篇 Whosebug 帖子、msdn 和 codeproject 文章,但似乎无法让它发挥作用。
,
Link 2,
,
Link 4
我想我错过了一个重要的步骤,但不知道是什么,我真的需要一些帮助。
[编辑1]
我试过单独使用 dll,它们可以工作。 (删除了混乱。查看屏幕截图的版本历史记录)
[编辑2]
我尝试了 Mukesh Kumar 的提议,并将我的 app.config 更改为:
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Common.Data"
publicKeyToken="f3b12eb6de839f43"
culture="neutral" />
<bindingRedirect oldVersion="1.0.0.0"
newVersion="2.0.0.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
但是还是不行。我现在 FileNotFoundException
.
[Edit3]
好的,我几乎可以肯定 <bindingRedirect>
不正确,应该是 <codeBase>
。
我尝试从 CLI 编译:
csc Program.cs /reference:v10=libs/Common.Data.1_0_0_0.dll /reference:v20=libs/Common.Data.2_0_0_0.dll
而且效果很好。我能够同时使用两个 API:
但是当我尝试从 Visual Studio 构建它时,它会抱怨 /reference,即使我已经指定了引用别名:
The extern alias 'v10' was not specified in a /reference option
我试过将 <Reference />
修改为 include/exclude <SpecificVersion>True</SpecificVersion>
但没有效果。
我发现了这个 post,解决方案是删除并重新添加路径。使用该解决方案,Visual Studio 构建良好,但我回到了 System.MissingMethodException
。
感觉我快明白了,但又不完全。如何让 Visual Studio 正确构建?
[Edit4]
我试过 miguel 的方法,但还是不行。
在这次尝试中,我将 dll 重命名回它们的原始名称,并将它们存储在不同的文件夹中。
然后我更新了 app.config 以执行 bindingRedirect 和代码库。
当 运行 宁它时,我得到 MissingMethodException
。
我不确定 4- Find the and put in False
是什么意思,所以我尝试了 <Private>
、<SpecificVersion>
的所有组合以及将 <Reference Include>
设置为 FQN,但是 none 这些组合有效。
查看我的第 3 次编辑,当通过 CLI 完成时,我已经成功编译并 运行 示例(使用我重命名的 dll + app.config 代码库 href)。
我现在的问题是,如何配置我的 csproj,使其构建相同。
尝试将您的配置详细信息如下所示。
<dependentAssembly>
<assemblyIdentity name="myAssembly"
publicKeyToken="32ab4ba45e0a69a1"
culture="neutral" />
<bindingRedirect oldVersion="1.0.0.0"
newVersion="2.0.0.0"/>
</dependentAssembly>
在这里你可以为你提供旧版本和新版本的dll。你可以跟着这个 article and also this.
希望对您有所帮助。
您需要将 dependentAssembly 与 bindingRedirect 一起使用,但您还需要将 dll 放在不同的文件夹中或以不同的名称保存。完成后,您需要将以下内容放入 app.config:
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="myAssembly"
publicKeyToken="here token dll"
culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.0.0.0" newVersion="1.0.0.0" />
<bindingRedirect oldVersion="1.0.0.1-2.0.0.0" newVersion="2.0.0.0" />
<codeBase version="1.0.0.0" href="folder\namedll.dll" />
<codeBase version="2.0.0.0" href="folder\namedll.dll" />
</dependentAssembly>
</assemblyBinding>
用这段代码应该可以编译运行,但有时VS在编译时会删除或覆盖app.config中的代码。您需要在编译文件夹的配置文件中检查它。如果成功,您可以编辑 .csproj。为此你必须做:
1- Unload the project affected
2- Right click on project
3- Click Edit project
4- Find the <AutoGenerateBindingRedirects>
property and set it to False
5- Save changes and reload project
这对我有用。在我的项目中,我使用了两个版本的 Automapper。
最后,另一种解决方案是使用AppDomain.CurrentDomain.AssemblyResolve
构建事件并加载特定的dll。
为此,您需要捕捉事件:
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
public static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
//debug and check the name
if (args.Name == "MyDllName")
return Assembly.LoadFrom("c:\pathdll\midllv1.dll")
else if(args.Name ="MyDllName2")
return Assembly.LoadFrom("c:\pathdll\midllv2.dll");
else
return Assembly.LoadFrom("");
}
缺少适合我需要的完整答案,我决定以 AssemblyResolve
的方式捐赠我的解决方案,以使用两个 dll 并从文件加载相关内容,使用反射来解析类型。对于这个演示,我创建了两个名为 MathFuncs
的 dll,尝试调用它们的 Add.add
函数将解析为两个不同的 implementations.To 看到不同的结果在值 1 和 2 之间切换版本整数:
public static int version = 1;
public static void Main(string[] args)
{
AppDomain.CurrentDomain.AssemblyResolve += Program.CurrentDomain_AssemblyResolve;
version = 1;
Type a = Type.GetType("MathFuncs.Add, MathFuncs, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
MethodInfo methodInfo = a?.GetMethod("add");
object result = null;
if (methodInfo != null)
{
object[] parametersArray = new object[] {1, 2};
result = methodInfo.Invoke(Activator.CreateInstance(a, null), parametersArray);
}
if (result != null)
{
Console.WriteLine((int)result);
}
else
{
Console.WriteLine("failed");
}
Console.Read();
}
public static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
FileInfo fi = null;
if (version == 1)
{
fi = new FileInfo("C:\Users\ohbitton\Desktop\MathFuncs\MathFuncs.dll");
}
else
{
fi = new FileInfo("C:\Users\ohbitton\Desktop\MathFuncs2\MathFuncs.dll");
}
return Assembly.LoadFrom(fi.FullName);
}
要获取 Type.GetType
命名空间的完整名称,您可以使用 powershell:
([system.reflection.assembly]::loadfile("C:\Users\ohbitton\Desktop\MathFuncs\MathFuncs.dll")).FullName
我得到了 2 个预编译的 dll:
Common.Data, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f3b12eb6de839f43, processorArchitecture=MSIL
Common.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=f3b12eb6de839f43, processorArchitecture=MSIL
API 中的差异示例:
我正在尝试将两者加载到一个项目中以执行如下操作:
extern alias v10;
extern alias v20;
private static void UpgradeUser()
{
// Load old user
var userOld = new v10::Common.Data.UserData();
userOld.loadData("user.dat");
// Create new user
var userNew = new v20::Common.Data.UserData();
// Copy properties
userNew.FirstName = userOld._firstName;
userNew.LastName = userOld._lastName;
userNew.Age = userOld._age;
// Invoke method from v10 and v20 API
userOld.version();
userNew.DisplayVersion();
if (userNew.GetUserInfo() != userOld.getInfo())
{
throw new Exception("Discrepencies in upgrade ");
}
Console.WriteLine("Upgrade done!");
}
我已经设置了我的项目引用和 app.config
以下内容。我还手动将 dll 复制到我的输出文件夹中,匹配 appconfig hrefs
<!-- [Edited: see history for previous version] -->
<Reference Include="Common.Data, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f3b12eb6de839f43, processorArchitecture=MSIL">
<HintPath>libs\Common.Data.1_0_0_0.dll</HintPath>
<Aliases>v10</Aliases>
</Reference>
<Reference Include="Common.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=f3b12eb6de839f43, processorArchitecture=MSIL">
<HintPath>libs\Common.Data.2_0_0_0.dll</HintPath>
<Aliases>v20</Aliases>
</Reference>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Common.Data" publicKeyToken="f3b12eb6de839f43" culture="neutral" />
<codeBase version="1.0.0.0" href="libs\Common.Data.1_0_0_0.dll" />
<codeBase version="2.0.0.0" href="libs\Common.Data.2_0_0_0.dll" />
</dependentAssembly>
</assemblyBinding>
</runtime>
我已经成功构建了。
但是当我尝试 运行 它时,我得到了 UserData.loadData
的 MissingMethodException
。
我浏览了多篇 Whosebug 帖子、msdn 和 codeproject 文章,但似乎无法让它发挥作用。
我想我错过了一个重要的步骤,但不知道是什么,我真的需要一些帮助。
[编辑1]
我试过单独使用 dll,它们可以工作。 (删除了混乱。查看屏幕截图的版本历史记录)
[编辑2]
我尝试了 Mukesh Kumar 的提议,并将我的 app.config 更改为:
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Common.Data"
publicKeyToken="f3b12eb6de839f43"
culture="neutral" />
<bindingRedirect oldVersion="1.0.0.0"
newVersion="2.0.0.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
但是还是不行。我现在 FileNotFoundException
.
[Edit3]
好的,我几乎可以肯定 <bindingRedirect>
不正确,应该是 <codeBase>
。
我尝试从 CLI 编译:
csc Program.cs /reference:v10=libs/Common.Data.1_0_0_0.dll /reference:v20=libs/Common.Data.2_0_0_0.dll
而且效果很好。我能够同时使用两个 API:
但是当我尝试从 Visual Studio 构建它时,它会抱怨 /reference,即使我已经指定了引用别名:
The extern alias 'v10' was not specified in a /reference option
我试过将 <Reference />
修改为 include/exclude <SpecificVersion>True</SpecificVersion>
但没有效果。
我发现了这个 post,解决方案是删除并重新添加路径。使用该解决方案,Visual Studio 构建良好,但我回到了 System.MissingMethodException
。
感觉我快明白了,但又不完全。如何让 Visual Studio 正确构建?
[Edit4]
我试过 miguel 的方法,但还是不行。
在这次尝试中,我将 dll 重命名回它们的原始名称,并将它们存储在不同的文件夹中。
然后我更新了 app.config 以执行 bindingRedirect 和代码库。
当 运行 宁它时,我得到 MissingMethodException
。
我不确定 4- Find the and put in False
是什么意思,所以我尝试了 <Private>
、<SpecificVersion>
的所有组合以及将 <Reference Include>
设置为 FQN,但是 none 这些组合有效。
查看我的第 3 次编辑,当通过 CLI 完成时,我已经成功编译并 运行 示例(使用我重命名的 dll + app.config 代码库 href)。
我现在的问题是,如何配置我的 csproj,使其构建相同。
尝试将您的配置详细信息如下所示。
<dependentAssembly>
<assemblyIdentity name="myAssembly"
publicKeyToken="32ab4ba45e0a69a1"
culture="neutral" />
<bindingRedirect oldVersion="1.0.0.0"
newVersion="2.0.0.0"/>
</dependentAssembly>
在这里你可以为你提供旧版本和新版本的dll。你可以跟着这个 article and also this.
希望对您有所帮助。
您需要将 dependentAssembly 与 bindingRedirect 一起使用,但您还需要将 dll 放在不同的文件夹中或以不同的名称保存。完成后,您需要将以下内容放入 app.config:
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="myAssembly"
publicKeyToken="here token dll"
culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.0.0.0" newVersion="1.0.0.0" />
<bindingRedirect oldVersion="1.0.0.1-2.0.0.0" newVersion="2.0.0.0" />
<codeBase version="1.0.0.0" href="folder\namedll.dll" />
<codeBase version="2.0.0.0" href="folder\namedll.dll" />
</dependentAssembly>
</assemblyBinding>
用这段代码应该可以编译运行,但有时VS在编译时会删除或覆盖app.config中的代码。您需要在编译文件夹的配置文件中检查它。如果成功,您可以编辑 .csproj。为此你必须做:
1- Unload the project affected
2- Right click on project
3- Click Edit project
4- Find the<AutoGenerateBindingRedirects>
property and set it to False
5- Save changes and reload project
这对我有用。在我的项目中,我使用了两个版本的 Automapper。
最后,另一种解决方案是使用AppDomain.CurrentDomain.AssemblyResolve
构建事件并加载特定的dll。
为此,您需要捕捉事件:
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
public static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
//debug and check the name
if (args.Name == "MyDllName")
return Assembly.LoadFrom("c:\pathdll\midllv1.dll")
else if(args.Name ="MyDllName2")
return Assembly.LoadFrom("c:\pathdll\midllv2.dll");
else
return Assembly.LoadFrom("");
}
缺少适合我需要的完整答案,我决定以 AssemblyResolve
的方式捐赠我的解决方案,以使用两个 dll 并从文件加载相关内容,使用反射来解析类型。对于这个演示,我创建了两个名为 MathFuncs
的 dll,尝试调用它们的 Add.add
函数将解析为两个不同的 implementations.To 看到不同的结果在值 1 和 2 之间切换版本整数:
public static int version = 1;
public static void Main(string[] args)
{
AppDomain.CurrentDomain.AssemblyResolve += Program.CurrentDomain_AssemblyResolve;
version = 1;
Type a = Type.GetType("MathFuncs.Add, MathFuncs, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
MethodInfo methodInfo = a?.GetMethod("add");
object result = null;
if (methodInfo != null)
{
object[] parametersArray = new object[] {1, 2};
result = methodInfo.Invoke(Activator.CreateInstance(a, null), parametersArray);
}
if (result != null)
{
Console.WriteLine((int)result);
}
else
{
Console.WriteLine("failed");
}
Console.Read();
}
public static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
FileInfo fi = null;
if (version == 1)
{
fi = new FileInfo("C:\Users\ohbitton\Desktop\MathFuncs\MathFuncs.dll");
}
else
{
fi = new FileInfo("C:\Users\ohbitton\Desktop\MathFuncs2\MathFuncs.dll");
}
return Assembly.LoadFrom(fi.FullName);
}
要获取 Type.GetType
命名空间的完整名称,您可以使用 powershell:
([system.reflection.assembly]::loadfile("C:\Users\ohbitton\Desktop\MathFuncs\MathFuncs.dll")).FullName