I created a background service in android maui like in this question: How to create a background service in .NET Maui. It's working fine. But I don't understand how to add DI services in this background? I need to add IDbContextFactory for my ef core context and IServiceScopeFactory.
If I add them in the constructor, I got an error:
Error XA4213 The type 'MyBackgroundService' must provide a public default constructor
.
My backgroubdService:
[Service]
public class AndroidBackgroundService : Service, IService
{
UpdateBackgroundService _updateBackgroundService; //I need this DI service
public AndroidBackgroundService(UpdateBackgroundService updateBackgroundService) //This compile error
{
_updateBackgroundService = updateBackgroundService;
}
public AndroidBackgroundService()
{
}
public override IBinder OnBind(Intent intent)
{
throw new NotImplementedException();
}
[return: GeneratedEnum]//we catch the actions intents to know the state of the foreground service
public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
{
if (intent.Action == "START_SERVICE")
{
RegisterNotification();//Proceed to notify
Run();
}
else if (intent.Action == "STOP_SERVICE")
{
StopForeground(true);//Stop the service
StopSelfResult(startId);
}
return StartCommandResult.NotSticky;
}
public void Run()
{
_updateBackgroundService.Run();
}
//Start and Stop Intents, set the actions for the MainActivity to get the state of the foreground service
//Setting one action to start and one action to stop the foreground service
public void Start()
{
Intent startService = new Intent(Microsoft.Maui.ApplicationModel.Platform.CurrentActivity, typeof(AndroidBackgroundService));
startService.SetAction("START_SERVICE");
Microsoft.Maui.ApplicationModel.Platform.CurrentActivity.StartService(startService);
}
public void Stop()
{
Intent stopIntent = new Intent(Microsoft.Maui.ApplicationModel.Platform.CurrentActivity, this.Class);
stopIntent.SetAction("STOP_SERVICE");
Microsoft.Maui.ApplicationModel.Platform.CurrentActivity.StartService(stopIntent);
}
private void RegisterNotification()
{
NotificationChannel channel = new NotificationChannel("ServiceChannel", "ServiceDemo", NotificationImportance.Max);
NotificationManager manager = (NotificationManager)Microsoft.Maui.ApplicationModel.Platform.CurrentActivity.GetSystemService(Context.NotificationService);
manager.CreateNotificationChannel(channel);
Notification notification = new Notification.Builder(this, "ServiceChannel")
.SetContentTitle("Агент 2 фоновый процесс запущен")
.SetSmallIcon(Resource.Drawable.abc_ab_share_pack_mtrl_alpha)
.SetOngoing(true)
.Build();
StartForeground(100, notification);
}
}
My UpdateBackgroundService
public class UpdateBackgroundService : BaseBackgroundService
{
private readonly IServiceScopeFactory scopeFactory;
private readonly IDbContextFactory<AsterixDBContext> _DbContextFactoryAsterix;
private readonly IDbContextFactory<Agent2DBContext> _DbContextFactory;
public UpdateBackgroundService(IServiceScopeFactory scopeFactory, IDbContextFactory<Agent2DBContext> dbContextFactory, IDbContextFactory<AsterixDBContext> dbContextFactoryAsterix)
: base(dbContextFactory)
{
this.scopeFactory = scopeFactory;
_DbContextFactoryAsterix = dbContextFactoryAsterix;
_DbContextFactory = dbContextFactory;
}
public Run()
{
...
}
}
MauiProgram
builder.Services.AddTransient<UpdateBackgroundService>();
#if ANDROID
builder.Services.AddTransient<AndroidBackgroundService>();
#endif
Seems like a [Service] class can't have any parameters in the constructor, here is an alternative way to use dependency injection without passing parameters.
Create a ServiceProvider
class :
public static class ServiceProvider
{
public static TService GetService<TService>()
=> Current.GetService<TService>();
public static IServiceProvider Current
=>
#if WINDOWS10_0_17763_0_OR_GREATER
MauiWinUIApplication.Current.Services;
#elif ANDROID
MauiApplication.Current.Services;
#elif IOS || MACCATALYST
MauiUIApplicationDelegate.Current.Services;
#else
null;
#endif
}
Then you can simply use it in any component constructor :
_Contexte = ServiceHelper.GetService<Contexte>();
As noted by @sellotape you can also use IPlatformApplication.Current.Services
, I created a static class to access it to avoid null warnings :
// Usage example: CurrentServiceProvider.Services.GetRequiredService<Parametrage>()
internal static class CurrentServiceProvider
{
public static IServiceProvider Services
{
get
{
IPlatformApplication? app = IPlatformApplication.Current;
if (app == null)
throw new InvalidOperationException("Cannot resolve current application. Services should be accessed after MauiProgram initialization.");
return app.Services;
}
}
}