-
Notifications
You must be signed in to change notification settings - Fork 5.4k
Description
Description
When publishing an application with ReadyToRun in .NET 8, that includes the below C# type hierarchy, the following exception is thrown during the start of the published application:
System.TypeLoadException: Return type in method 'ReadyToRunRepro.DataElementBase`1[TIdentifier].get_Id()' on type 'ReadyToRunRepro.DataElementBase`1[TIdentifier]' from assembly 'ReadyToRunRepro, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' is not compatible with base type method 'ReadyToRunRepro.DataElementBase.get_Id()
The application with the same type hierarchy works without problems in .NET 7.
The type hierarchy:
public abstract class DataElementBase
{
private protected DataElementBase(IdentifierBase id)
{
this.Id = id;
}
public virtual IdentifierBase Id { get; }
}
public abstract class DataElementBase<TIdentifier> : DataElementBase
where TIdentifier : DataElementIdentifierBase
{
private protected DataElementBase(
TIdentifier id)
: base(id)
{
}
public override TIdentifier Id => (TIdentifier) base.Id;
}
public sealed class EventDefinition : DataElementBase<EventDefinitionIdentifier>
{
internal EventDefinition(EventDefinitionIdentifier id)
: base(id)
{
}
}
public abstract class EventDefinitionBase : DataElementBase<EventDefinitionIdentifier>
{
private protected EventDefinitionBase(
NodeIdentifier nodeId,
EventSettingsBase settings,
Type? argumentType = null)
: base(
new EventDefinitionIdentifier(nodeId))
{
}
protected internal abstract EventSettingsBase Settings { get; }
}
public abstract class EventDefinitionBase<TSettings> : EventDefinitionBase
where TSettings : EventSettingsBase
{
private protected EventDefinitionBase(
NodeIdentifier nodeId,
TSettings settings,
Type? argumentType = null)
: base(nodeId, settings, argumentType)
{
this.Settings = settings;
}
protected internal override TSettings Settings { get; }
}
public abstract class IdentifierBase
{
}
public sealed class NodeIdentifier : IdentifierBase, IEquatable<NodeIdentifier>
{
public bool Equals(NodeIdentifier? other)
{
return true;
}
}
public abstract class OwnedIdentifierBase<TOwner> : IdentifierBase, IEquatable<OwnedIdentifierBase<TOwner>>
where TOwner : IdentifierBase
{
public OwnedIdentifierBase(TOwner owner)
{
this.Owner = owner;
}
public TOwner Owner { get; }
public bool Equals(OwnedIdentifierBase<TOwner>? other)
{
return true;
}
}
public abstract class DataElementIdentifierBase : OwnedIdentifierBase<NodeIdentifier>
{
protected DataElementIdentifierBase(NodeIdentifier owner)
: base(owner)
{
}
}
public abstract record DataElementSettingsBase
{
private protected DataElementSettingsBase()
{
}
}
public sealed class EventDefinitionIdentifier : DataElementIdentifierBase
{
public EventDefinitionIdentifier(NodeIdentifier owner)
: base(owner)
{
}
}
public abstract record EventSettingsBase : DataElementSettingsBase;
public record EventSettings : EventSettingsBase;Reproduction Steps
-
Clone the repo https://github.com/bitbonk/ReadyToRunRepro and run
dotnet publish .\ReadyToRunRepro\ReadyToRunRepro.csproj --configuration=Release -p:PublishReadyToRun=true --runtime win-x64 --self-containedfollowed by
.\ReadyToRunRepro\bin\Release\net7.0\win-x64\publish\ReadyToRunRepro.exe -
See that the worker service starts correctly and runs without errors
-
Change the target framework in
ReadyToRunRepro.csprojto .NET 8:<TargetFramework>net8.0</TargetFramework> -
Publish again with the same command as in 1.:
dotnet publish .\ReadyToRunRepro\ReadyToRunRepro.csproj --configuration=Release -p:PublishReadyToRun=true --runtime win-x64 --self-containedand then start the published .NET 8 exe:
.\ReadyToRunRepro\bin\Release\net8.0\win-x64\publish\ReadyToRunRepro.exe
Expected behavior
The worker service application published with a .NET 8 TFM starts and runs without problems.
Actual behavior
the following exception occurs:
Unhandled exception. System.TypeLoadException: Return type in method 'ReadyToRunRepro.DataElementBase`1[TIdentifier].get_Id()' on type 'ReadyToRunRepro.DataElementBase`1[TIdentifier]' from assembly 'ReadyToRunRepro, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' is not compatible with base type method 'ReadyToRunRepro.DataElementBase.get_Id()'.
at ReadyToRunRepro.Service..ctor()
at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitRootCache(ServiceCallSite callSite, RuntimeResolverContext context)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitRootCache(ServiceCallSite callSite, RuntimeResolverContext context)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitIEnumerable(IEnumerableCallSite enumerableCallSite, RuntimeResolverContext context)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitRootCache(ServiceCallSite callSite, RuntimeResolverContext context)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.CreateServiceAccessor(ServiceIdentifier serviceIdentifier)
at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(ServiceIdentifier serviceIdentifier, ServiceProviderEngineScope serviceProviderEngineScope)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
at Microsoft.Extensions.Hosting.Internal.Host.StartAsync(CancellationToken cancellationToken)
at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.RunAsync(IHost host, CancellationToken token)
at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.RunAsync(IHost host, CancellationToken token)
at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.Run(IHost host)
at Program.<Main>$(String[] args) in C:\source\ReadyToRunRepro\ReadyToRunRepro\Program.cs:line 8
Regression?
Yes this is a regression, since it works with a .NET 7 TFM.
Known Workarounds
none is known
Configuration
No response
Other information
Deleting either one of these three lines (commented with in code deleting this line will make the error go away) will make the problem go away:
-
in
Service.cs:private readonly NestedDictionary<NodeIdentifier, EventDefinitionIdentifier, EventDefinitionBase> dict = new();
-
in
Worker.csthis.logger.LogInformation("Service {service} acquired", this.service);
-
in
Types.cs:public record EventSettings : EventSettingsBase;
See also https://github.com/bitbonk/ReadyToRunRepro/pull/2/files which contains step 3 and 4 of the reproduction steps.
You can just switch to branch net8 and run buildandrun.ps1 to see the error.