VS.NET 2010/MSBUILD能否为.NET 3.5 SP1生成XmlSerializers?

12
我刚刚将一个包含 WinForms、通用库和 Web 应用程序的 VS 2008 解决方案升级到了 VS 2010,但所有项目仍然针对 .NET 3.5 SP 1。我使用这种技术为我的通用库生成 XmlSerializers。WinForms 应用程序可以正常运行。当我的 Web 应用程序尝试使用引用相同 XmlSerializers 的这些库运行时,它会抛出以下异常:
``` Server Error in '/WebSubscribers' Application. Could not load file or assembly 'Ceoimage.Basecamp.XmlSerializers' or one of its dependencies. This assembly is built by a runtime newer than the currently loaded runtime and cannot be loaded. ```
我使用.NET Reflector查看了 XmlSerializer 的引用,发现它引用了 2.0 和 4.0 版本的 mscorlib,以及 3.5 和 4.0 版本的 System.Data.Linq。奇怪的是,它只使用了 4.0 版本的 System.Xml。这可能就是我的问题所在。
我该如何使 Web 应用程序能够使用这些 XmlSerializers 呢?当我简单地删除这些 XmlSerializers 时,Web 应用程序可以正常运行。这是一种选择,但我该如何强制 MSBUILD 为特定版本的 CLR 创建序列化程序呢?
以下是我添加到项目文件中的 MSBuild 任务,用于强制创建 XmlSerializers:
<Target Name="AfterBuild" DependsOnTargets="AssignTargetPaths;Compile;ResolveKeySource" Inputs="$(MSBuildAllProjects);@(IntermediateAssembly)" Outputs="$(OutputPath)$(_SGenDllName)">
 <Delete Files="$(TargetDir)$(TargetName).XmlSerializers.dll" ContinueOnError="true" />
 <SGen BuildAssemblyName="$(TargetFileName)" BuildAssemblyPath="$(OutputPath)" References="@(ReferencePath)" ShouldGenerateSerializer="true" UseProxyTypes="false" KeyContainer="$(KeyContainerName)" KeyFile="$(KeyOriginatorFile)" DelaySign="$(DelaySign)" ToolPath="$(SGenToolPath)">
  <Output TaskParameter="SerializationAssembly" ItemName="SerializationAssembly" />
 </SGen>
</Target>

1
你应该将那个编辑作为答案添加,这样我们就可以投票支持它,因为它似乎是最好的解决方案 :) - Lucas
3个回答

8

MSBuild 4会(应该会...)使用3.5工具来构建3.5项目。但是,它似乎无法确定3.5工具的位置,因此正在使用4.0工具。结果是,它正确地构建了您的3.5项目(使用CLR 2.0.50727程序集),但是4.0 sgen.exe工具正在生成一个CLR 4.0.30319程序集Ceoimage.Basecamp.XmlSerializers.dll。

MSBuild使用注册表获取v3.5工具的路径。如果无法确定3.5工具的路径,则需要依赖v4.0路径的MSBuild任务将回退到v4.0路径 - 如果您真正感兴趣,请查看C:\Windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.NETFramework.props中用于设置TargetFrameworkSDKToolsDirectory属性的逻辑。

您可以按以下方式诊断和修复可能的注册表问题:

安装Process Monitor并设置过滤器以监视msbuild的注册表访问(事件类别:Registry,进程名称:msbuild.exe,所有类型的结果)

运行您的构建

在Process Monitor中搜索与“MSBuild\ToolsVersions\4.0\SDK35ToolsPath”匹配的RegQueryValue访问。请注意,这可能位于“HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft”或“HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft”下

如果您查看注册表中的此键,您将看到它别名另一个注册表值,例如“$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft \Microsoft SDKs\Windows\v7.1\WinSDK-NetFx35Tools-x86@InstallationFolder)”。不久之后,当msbuild尝试从指定的键加载该值时,您可能会看到“NAME NOT FOUND”的结果。

从这里开始,应该清楚您需要添加/修改哪些键。

注册表值错误的原因有几个可能性。在我的情况下,Microsoft SDK v7.1安装存在问题,导致注册表键命名错误,已将其识别为错误,如下所示:

http://connect.microsoft.com/VisualStudio/feedback/details/594338/tfs-2010-build-agent-and-windows-7-1-sdk-targeting-net-3-5-generates-wrong-embedded-resources


感谢您的回答。我将结合https://dev59.com/DHE85IYBdhLWcg3whUBr#2739132来解决我的问题。 - Grimace of Despair

2
你是否依赖于任何4.0特定的东西?
如果你调用MSBuild 4.0,你将得到4.0工具。如果你调用MSBuild 3.5,你将得到3.5工具(这正是你想要的,因为你显然是在2.0 CLR中托管)。
另一个选项是在你的Web服务器上放置4.0 CLR。如果那不开放,你的流中不应该有任何4.0目标化的东西。

我刚刚检查了我的一个项目,发现它只引用了2.0和3.5框架程序集。然而,我注意到每个框架的“Specific Version”属性都指向“false”。当我卸载一个项目并查看.CSPROJ文件时,property标签确实提到了4.0工具,如下所示: <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">当我将“ToolsVersion”更改为“3.5”时,VS 2010会转换项目并将值返回到“4.0”。 - flipdoubt
@flipdoubt:VS2010强制使用4.0工具版本是一个已知的问题(请搜索)。这里的问题是导入的SGen任务是4.0 SGen任务,它将默认使用4.0 SGen.exe。也许将ToolPath覆盖为3.5 SGen可以解决问题。 - Ruben Bartelink
然后我看了你的编辑。我认为这是最好的修复方法,因为通过MSBuild 3.5运行ToolsVersion 4.0项目不会是长期解决方案。 - Ruben Bartelink

2

我发现可以明确指定SGEN任务的工具路径来使用3.5版本,如下所示:

<SGen BuildAssemblyName="$(TargetFileName)" BuildAssemblyPath="$(OutputPath)" References="@(ReferencePath)" ShouldGenerateSerializer="true" UseProxyTypes="false" KeyContainer="$(KeyContainerName)" KeyFile="$(KeyOriginatorFile)" DelaySign="$(DelaySign)" ToolPath="C:\Program Files\Microsoft SDKs\Windows\v7.0A\bin">

你是在更改目标文件还是以某种方式修改项目文件?我不知道如何将其应用于我的环境... - John Leidegren
这是很久以前的事情了,但我相当确定我直接编辑了CSPROJ文件。 - flipdoubt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接