Description
Description
Hi everyone,
I'm not sure if this is the right repo or not, but I have an issue porting an application from .NET 6.0 to .NET 8.0.
I'm not even sure if the "root" problem has something to do with the XmlSerializer or not.
Our scenario:
Our solution contains two main applications:
- Client, which still runs on .NET Framework 4.8
- Server, which now runs on .NET 6 and we try to migrate it to .NET 8
Both applications share some common types and the actual solution is "large" (~100 Projects, multiple web apps (e.g. WebApi, Admin App etc.)).
We try to stay up to date and want to use new languages features, that's why we have a project that contains "BCL-Types" like this:
#if !NET7_0_OR_GREATER
namespace System.Runtime.CompilerServices
{
/// <summary>Specifies that a type has required members or that a member is required.</summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
// #if SYSTEM_PRIVATE_CORELIB
public
// #else
// internal
// #endif
sealed class RequiredMemberAttribute : Attribute
{ }
}
#endif
This way we can use something like this in the Common area and don't bother if it is used from .NET 4.8 or not:
public required string Name { get; set; }
We use XmlSerialization
for configuration stuff in our application and during the .NET 8 migration we found this RunTime Exception:
Could not load type 'System.Runtime.CompilerServices.RequiredMemberAttribute' from assembly 'BclTest.BclTypes, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
Details
System.TypeLoadException
HResult=0x80131522
Message=Could not load type 'System.Runtime.CompilerServices.RequiredMemberAttribute' from assembly 'BclTest.BclTypes, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
Source=System.Private.CoreLib
StackTrace:
at System.ModuleHandle.ResolveType(QCallModule module, Int32 typeToken, IntPtr* typeInstArgs, Int32 typeInstCount, IntPtr* methodInstArgs, Int32 methodInstCount, ObjectHandleOnStack type)
at System.ModuleHandle.ResolveTypeHandle(Int32 typeToken, RuntimeTypeHandle[] typeInstantiationContext, RuntimeTypeHandle[] methodInstantiationContext)
at System.Reflection.RuntimeModule.ResolveType(Int32 metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments)
at System.Reflection.CustomAttribute.FilterCustomAttributeRecord(MetadataToken caCtorToken, MetadataImport& scope, RuntimeModule decoratedModule, MetadataToken decoratedToken, RuntimeType attributeFilterType, Boolean mustBeInheritable, ListBuilder`1& derivedAttributes, RuntimeType& attributeType, IRuntimeMethodInfo& ctorWithParameters, Boolean& isVarArg)
at System.Reflection.CustomAttribute.AddCustomAttributes(ListBuilder`1& attributes, RuntimeModule decoratedModule, Int32 decoratedMetadataToken, RuntimeType attributeFilterType, Boolean mustBeInheritable, ListBuilder`1 derivedAttributes)
at System.Reflection.CustomAttribute.GetCustomAttributes(RuntimeModule decoratedModule, Int32 decoratedMetadataToken, Int32 pcaCount, RuntimeType attributeFilterType)
at System.Reflection.CustomAttribute.GetCustomAttributes(RuntimeType type, RuntimeType caType, Boolean inherit)
at System.Xml.Serialization.TempAssembly.LoadGeneratedAssembly(Type type, String defaultNamespace, XmlSerializerImplementation& contract)
at System.Xml.Serialization.XmlSerializer..ctor(Type type, String defaultNamespace)
at CommonTypes.Person.DeserializeFromXml(String xml) in C:\Users\muehsig\source\repos\BclTest.BclTypes\CommonTypes\Person.cs:line 13
at TestProject1.UnitTest1.TestMethod1() in C:\Users\muehsig\source\repos\BclTest.BclTypes\TestProject1\UnitTest1.cs:line 33
at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)
It seems, that the XmlSerializer uses the "wrong" type during runtime.
Reproduction Steps
I made a small test repo here.
The smallest repo would be something like this:
- Common Lib -> TargetFramework "netstandard2.0"
- .NET 4.8 Console App -> TargetFramework "48"
- .NET 8 Console App -> TargetFramework "net8.0"
- BCL-Type Lib -> TargetFramework "netstandard2.0,net8.0"
- Unit Test Project -> TargetFramework "net8.0"
In the Common Lib is a class like this:
public class Person
{
public required string Name { get; set; }
public static Person DeserializeFromXml(string xml)
{
XmlSerializer serializer = new XmlSerializer(typeof(Person));
using (StringReader reader = new StringReader(xml))
{
return (Person)serializer.Deserialize(reader);
}
}
}
In the .NET 8 Console App is a class like this:
internal class FoobarJob
{
public void Run([NotNullWhen(returnValue: false)] out string? errorMessage)
{
errorMessage = null;
Console.WriteLine("Running FoobarJob");
}
}
In the Unit Test we have this:
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
// works
Person person = new Person() { Name = "Test" };
string xml = @"<Person><Name>John Doe</Name></Person>";
Person personX = Person.DeserializeFromXml(xml);
Console.WriteLine(personX.Name);
}
}
Expected behavior
The Common type can be consumed from the .NET 4.8 client and the .NET 8 application and we can use modern language features in the .NET 8 application.
Actual behavior
With the TargetFrameworks in the BCL class like this "netstandard2.0,net8.0" the runtime error occures:
Could not load type 'System.Runtime.CompilerServices.RequiredMemberAttribute' from assembly 'BclTest.BclTypes, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
I thought, that the TargetFramework might be "wrong" and I just use "netstandard2.0" in the BCL-Type lib, but then I get this compiler error:
FoobarJob.cs(12,26,12,37): error CS0433: The type 'NotNullWhenAttribute' exists in both 'BclTest.BclTypes, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' and 'System.Runtime, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
3>Done building project "NetEightApp.csproj" -- FAILED.
Regression?
If I use "net6.0" instead of "net8.0" the issue is resolved, but the reason for this is simply this condition: #if !NET7_0_OR_GREATER
Known Workarounds
No response
Configuration
.NET Framework 4.8 & .NET 8
Other information
No response