shinyorg / shiny

.NET Framework for Backgrounding & Device Hardware Services (iOS, Android, & Catalyst)
https://shinylib.net
MIT License
1.43k stars 227 forks source link

[Bug]: ServiceCollectionExtensions.AddBluetoothLE() throws "System.InvalidOperationException: This service descriptor is keyed. Your service provider may not support keyed services." #1413

Closed swlasse closed 6 months ago

swlasse commented 6 months ago

Component/Nuget

BluetoothLE Client (Shiny.BluetoothLE)

What operating system(s) are effected?

Version(s) of Operation Systems

Android 14 (API level 34) is the OS I have available for testing, however, this bug is likely to affect all OS'es.

Hosting Model

Steps To Reproduce

  1. Create new MAUI project targeting .NET 8 and use Android as platform.
  2. Install latest version of Shiny (I use: <PackageReference Include="Shiny.BluetoothLE" Version="3.2.4" />):
  3. Run the following code:
public static MauiApp CreateMauiApp()
{
    var builder = MauiApp.CreateBuilder();
    builder.Services.AddKeyedSingleton("myKey", new object());
    builder.Services.AddBluetoothLE();
    ...
}

Throws:

System.InvalidOperationException: 'This service descriptor is keyed. Your service provider may not support keyed services.'

Expected Behavior

Expected to support registration of the BLE services even when other keyed registrations exists in the service collection.

From what I can tell, the behavior of ServiceDescriptor has changed in .NET 8 - see this related issue / discussion: https://github.com/dotnet/runtime/issues/95789#issuecomment-1856096964

Actual Behavior

In Shiny, the call to ServiceCollectionExtensions.AddBluetoothLE() is calling services.HasImplementation<BleManager>() which is doing this:

public static bool HasImplementation(this IServiceCollection services, Type implementationType) =>
services.Any(x => x.ImplementationType == implementationType);

... which, according to: https://github.com/dotnet/runtime/issues/95789#issuecomment-1856096964 is no longer allowed on a keyed descriptor.

Other libraries (from other projects) are now also failing because of this change, but a proposed fix from: https://github.com/AzureAD/microsoft-identity-web/pull/2676/files#diff-1cc266edfdfa6f7443d29d8bb2c6492da93bd6a8d410e1004d8429f9e2ec1c32R143-R151 ... looks like this:

private static bool HasImplementationType(IServiceCollection services, Type implementationType)
{
    return services.Any(s =>
#if NET8_0_OR_GREATER
    s.ServiceKey is null &&
#endif
    s.ImplementationType == implementationType);
}

A simple workaround (hack) for me that "works", was to flip the order of registrations, so the keyed registration comes after, but one might not be in a position where that is easily done (nor desired), hence raising this issue for awareness.

Please let me know if something is unclear.

Thanks again for your hard work on this project!

Exception or Log output

No response

Code Sample

No response

Code of Conduct

swlasse commented 5 months ago

FWIW: I just updated our project to use the newly released 3.3.0 version of Shiny.BluetoothLE and I can confirm that the above issue has now been resolved - thanks again for your awesome work on this project.

aritchie commented 5 months ago

Thank you for confirming. I wish more people did this!