C# 依赖注入有重复的单例

C# dependency injection has duplicate singletons

我正在开发一个 Xamarin.Forms 应用程序,由于某些原因,存在多次创建的单例。然而,这并不是每次都会发生,而且似乎是随机的。我的依赖注入设置发生在 App.xaml.cs

using System;
using Microsoft.AppCenter;
using Microsoft.AppCenter.Analytics;
using Microsoft.AppCenter.Crashes;
using Microsoft.Extensions.DependencyInjection;
using Peripass.Mobile.Framework;
using Peripass.Mobile.Framework.DependencyInterfaces;
using Peripass.YardAssetManagementApp.Data;
using Peripass.YardAssetManagementApp.Data.FileSystem;
using Peripass.YardAssetManagementApp.Data.LocalDataServices;
using Peripass.YardAssetManagementApp.Data.Queues;
using Peripass.YardAssetManagementApp.Data.RemoteDataServices;
using Peripass.YardAssetManagementApp.Data.Syncing;
using Peripass.YardAssetManagementApp.Data.Syncing.QueueItemSyncActions;
using Peripass.YardAssetManagementApp.Device;
using Peripass.YardAssetManagementApp.MVVM;
using Peripass.YardAssetManagementApp.MVVM.Pages;
using Peripass.YardAssetManagementApp.PushNotifications;
using Peripass.YardAssetManagementApp.Services;
using Peripass.YardAssetManagementApp.StepsWizard;
using Peripass.YardAssetManagementApp.UI.Views;
using Peripass.YardAssetManagementApp.UserAuthentication;
using Peripass.YardAssetManagementApp.ViewModels;
using Xamarin.Forms;
using Application = Xamarin.Forms.Application;

namespace Peripass.YardAssetManagementApp {
  public partial class App : Application, IApp {
    private string _startedByAndroidNotificationMessage;
    
    public App() {
      ConfigureServices();
      InitializeComponent();
      AppCenter.Start($"android={Configuration.Configuration.ApplicationInsightsKeyAndroid};", typeof(Analytics), typeof(Crashes));
      MainPage = ServiceProvider.GetRequiredService<MainPage>();
    }

    public static IServiceCollection Services { get; } = new ServiceCollection();
    public IServiceProvider ServiceProvider { get; set; }

    private void ConfigureServices() {
      Services.AddSingleton<ILifecycleHooksService, LifecycleHooksService>();
      Services.AddTransient<INetworkInformationProvider, NetworkInformationProvider>();

      RegisterLocalDataServices();
      RegisterRemoteDataServices();
      RegisterSyncLogic();
      RegisterServices();

      RegisterViews();
      Services.AddSingleton<IUserManager, UserManager>();
      Services.AddSingleton<HttpClientProvider>();
      Services.AddSingleton<ILogger, Logger>();
      Services.AddSingleton<INavigationService, NavigationService>();
      Services.AddSingleton<StepsWizardManager>();
      Services.AddSingleton<MainPage>();
      Services.AddSingleton(DependencyService.Get<ILocalFileSystem>());
      Services.AddSingleton<IRestServiceHelper, RestServiceHelper>();
      Services.AddSingleton<IPushNotificationsService, PushNotificationsService>();
      ServiceProvider = Services.BuildServiceProvider();
    }

    public void RegisterServices() {
      Services.AddTransient<ITaskService, TaskService>();
      Services.AddTransient<ILocationService, LocationService>();
      Services.AddTransient<ILocalizationService, LocalizationService>();
      Services.AddTransient<IDiagnosticsService, DiagnosticsService>();
    }

    public void RegisterLocalDataServices() {
      Services.AddSingleton<ILocalTaskDataService, LocalTaskDataService>();
      Services.AddSingleton<ILocalLocationDataService, LocalLocationDataService>();
      Services.AddSingleton<ILocalUserDataService, LocalUserDataService>();
      Services.AddSingleton<ILocalPushNotificationDataService, LocalPushNotificationDataService>();
      Services.AddSingleton<ILocalPictureDataService, LocalPictureDataService>();
      Services.AddSingleton<ISafeFileSystem, SafeFileSystem>();
      Services.AddSingleton<ILocalLocalizationDataService, LocalLocalizationDataService>();
    }

    public void RegisterRemoteDataServices() {
      Services.AddSingleton<IRemoteTaskDataService, RemoteTaskDataService>();
      Services.AddSingleton<IRemoteLocationDataService, RemoteLocationDataService>();
      Services.AddSingleton<IRemoteUserDataService, RemoteUserDataService>();
      Services.AddSingleton<IRemoteFileDataService, RemoteFileDataService>();
      Services.AddSingleton<IRemoteLocalizationDataService, RemoteLocalizationDataService>();
    }

    public void RegisterSyncLogic() {
      Services.AddSingleton<IToMobileTasksSyncer, ToMobileTasksSyncer>();

      Services.AddSingleton<IQueue, IncomingHighPriorityQueue>();
      Services.AddSingleton<IQueue, IncomingLowPriorityQueue>();
      Services.AddSingleton<IQueue, OutgoingHighPriorityQueue>();
      Services.AddSingleton<IQueue, OutgoingLowPriorityQueue>();
      Services.AddSingleton<IQueue, PictureSyncLowPriorityQueue>();
      Services.AddSingleton<IQueue, PictureSyncHighPriorityQueue>();

      Services.AddSingleton<IQueueOrchestrator, IncomingQueueOrchestrator>();
      Services.AddSingleton<IQueueOrchestrator, OutgoingQueueOrchestrator>();
      Services.AddSingleton<IQueueOrchestrator, PictureQueueOrchestrator>();

      Services.AddSingleton<IQueueProcessor, IncomingQueueProcessor>();
      Services.AddSingleton<IQueueProcessor, OutgoingQueueProcessor>();
      Services.AddSingleton<IQueueProcessor, PictureQueueProcessor>();

      Services.AddSingleton<IQueueItemSyncAction, FetchTaskDetailSyncAction>();
      Services.AddSingleton<IQueueItemSyncAction, OutgoingStepValueUpdateSyncAction>();
      Services.AddSingleton<IQueueItemSyncAction, OutgoingTaskStatusChangedSyncAction>();
      Services.AddSingleton<IQueueItemSyncAction, SyncPictureAction>();

      Services.AddSingleton<IFileIntegrityHelper, FileIntegrityHelper>();
    }

    public void RegisterViews() {
      Services.AddTransient<LoginViewContent>();
      Services.AddSingleton<LoginViewModel>();

      Services.AddTransient<UserInfoViewContent>();
      Services.AddSingleton<UserInfoViewModel>();

      Services.AddTransient<MainTaskListViewContent>();
      Services.AddSingleton<MainTaskListViewModel>();

      Services.AddTransient<TaskDetailViewContent>();
      Services.AddSingleton<TaskDetailViewModel>();

      Services.AddTransient<IntegerFieldTaskStepViewContent>();
      Services.AddSingleton<IntegerFieldTaskStepViewModel>();

      Services.AddTransient<TextAreaTaskStepViewContent>();
      Services.AddSingleton<TextAreaTaskStepViewModel>();

      Services.AddTransient<DropDownTaskStepViewContent>();
      Services.AddSingleton<DropDownTaskStepViewModel>();

      Services.AddTransient<MoveToLocationSummaryViewContent>();
      Services.AddSingleton<MoveToLocationSummaryViewModel>();

      Services.AddTransient<TakePictureStepViewContent>();
      Services.AddSingleton<TakePictureStepViewModel>();

      Services.AddTransient<MoveToLocationStepViewContent>();
      Services.AddSingleton<MoveToLocationStepViewModel>();

      Services.AddTransient<TaskCompletedViewContent>();
      Services.AddSingleton<TaskCompletedViewModel>();

      Services.AddTransient<LoggedOutViewContent>();
      Services.AddSingleton<LoggedOutViewModel>();

      Services.AddTransient<TextFieldTaskStepViewContent>();
      Services.AddSingleton<TextFieldTaskStepViewModel>();

      Services.AddTransient<DecimalFieldTaskStepViewContent>();
      Services.AddSingleton<DecimalFieldTaskStepViewModel>();

      Services.AddTransient<GenericExceptionPageViewContent>();
      Services.AddSingleton<GenericExceptionPageViewModel>();

      Services.AddTransient<CleanupBeforeLogoutPageViewContent>();
      Services.AddSingleton<CleanupBeforeLogoutPageViewModel>();

      Services.AddTransient<SelectLanguageViewContent>();
      Services.AddSingleton<SelectLanguageViewModel>();
    }

    protected override async void OnStart() {
      await ServiceProvider.GetRequiredService<ILifecycleHooksService>().OnStart();
    }

    protected override void OnSleep() { }

    protected override async void OnResume() {
      await ServiceProvider.GetRequiredService<ILifecycleHooksService>().OnResume();
    }

    public void StartedByTapOnAndroidNotification(string message) {
      _startedByAndroidNotificationMessage = message;
    }
  }
}

多次创建的单例就是这些。可能是其他单例也被创建了多次,但我确信这些。

      Services.AddSingleton<IQueueOrchestrator, IncomingQueueOrchestrator>();
      Services.AddSingleton<IQueueOrchestrator, OutgoingQueueOrchestrator>();
      Services.AddSingleton<IQueueOrchestrator, PictureQueueOrchestrator>();

      Services.AddSingleton<IQueueProcessor, IncomingQueueProcessor>();
      Services.AddSingleton<IQueueProcessor, OutgoingQueueProcessor>();
      Services.AddSingleton<IQueueProcessor, PictureQueueProcessor>();

我发现在解析 ILifecycleHooksService 的依赖项时,有时会多次调用 OnStart 方法来创建这些单例的多个实例。这是 LifeCycleHooksService 中的代码:

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Peripass.YardAssetManagementApp.Data;
using Peripass.YardAssetManagementApp.Data.LocalDataServices;
using Peripass.YardAssetManagementApp.Data.Syncing;
using Peripass.YardAssetManagementApp.MVVM;
using Peripass.YardAssetManagementApp.PushNotifications;
using Peripass.YardAssetManagementApp.ViewModels;

namespace Peripass.YardAssetManagementApp {
  public interface ILifecycleHooksService {
    Task OnStart();
    Task OnResume();
  }
  public class LifecycleHooksService : ILifecycleHooksService {
    private readonly INavigationService _navigationService;
    private readonly ILocalUserDataService _localUserDataService;
    private readonly ILocalTaskDataService _localTaskDataService;
    private readonly ILocalLocationDataService _localLocationDataService;
    private readonly ILocalPushNotificationDataService _localPushNotificationDataService;
    private readonly ILocalPictureDataService _localPictureDataService;
    private readonly IToMobileTasksSyncer _toMobileTasksSyncer;
    private readonly IEnumerable<IQueueProcessor> _queueProcessors;
    private readonly IPushNotificationsService _pushNotificationsService;
    private readonly ILocalLocalizationDataService _localLocalizationDataService;

    public LifecycleHooksService(INavigationService navigationService,
      ILocalUserDataService localUserDataService, 
      IToMobileTasksSyncer toMobileTasksSyncer, 
      IEnumerable<IQueueProcessor> queueProcessors,
      ILocalTaskDataService localTaskDataService, 
      ILocalLocationDataService localLocationDataService, 
      ILocalPushNotificationDataService localPushNotificationDataService, 
      IPushNotificationsService pushNotificationsService,
      ILocalPictureDataService localPictureDataService, ILocalLocalizationDataService localLocalizationDataService) {

      _navigationService = navigationService;
      _localUserDataService = localUserDataService;
      _toMobileTasksSyncer = toMobileTasksSyncer;
      _queueProcessors = queueProcessors;
      _localTaskDataService = localTaskDataService;
      _localLocationDataService = localLocationDataService;
      _localPushNotificationDataService = localPushNotificationDataService;
      _pushNotificationsService = pushNotificationsService;
      _localPictureDataService = localPictureDataService;
      _localLocalizationDataService = localLocalizationDataService;
    }
    public async Task OnStart() {
      await ReloadData();
      await CreateDeviceIdOnFirstStartAsync();
      var currentUser = _localUserDataService.GetCurrentUser();
      if (currentUser.IsLoggedIn) {
        Configuration.Configuration.SetEnvironment(currentUser.Environment);
        await _navigationService.NavigateToViewModelAsync<MainTaskListViewModel>(true);
        try {
          await _toMobileTasksSyncer.SyncAllMetaData();
        }
        catch {
          // ignored
        }
      }
      else {
        await _navigationService.NavigateToViewModelAsync<LoginViewModel>();
      }
      foreach (var queueProcessor in _queueProcessors) {
        _ = Task.Run(async () => await queueProcessor.StartPeriodicProcessing());
      }
    }

    public async Task OnResume() {
      try {
        await _toMobileTasksSyncer.SyncAllTasks(true);
      }
      catch {
        // ignored
      }
    }

    public async Task CreateDeviceIdOnFirstStartAsync() {
      var currentPushNotificationInfo = await _localPushNotificationDataService.GetCurrentPushNotificationInfo();
      if (string.IsNullOrEmpty(currentPushNotificationInfo.DeviceIdTag)) {
        await _localPushNotificationDataService.SetDeviceId(Guid.NewGuid().ToString());
      }
    }

    private async Task ReloadData() {
      await _localLocalizationDataService.ReloadData();
      await _localUserDataService.ReloadData();
      await _localTaskDataService.ReloadData();
      await _localLocationDataService.ReloadData();
      await _localPictureDataService.ReloadData();
      await _pushNotificationsService.ProcessReceivedMessages();
    }
  }
}

在此 class 中,我注入了 IQueueProcessors 的 I Enumerable。这些 queueProcessors 将自己注入 IQueueOrchestrators。如果您需要更多信息,请问我。

我发现是什么原因造成的。原因与 android 相关,并在此堆栈溢出 post 中进行了描述:description of what causes this

对我有用的解决方案来自 post 关于同一主题:solution for me

基本上对我有用的解决方案是将其添加到 OnCreate:

// Possible work around for market launches. See https://issuetracker.google.com/issues/36907463
// for more details. Essentially, the market launches the main activity on top of other activities.
// we never want this to happen. Instead, we check if we are the root and if not, we finish.

if (!isTaskRoot()) {
    final Intent intent = getIntent();
    if (intent.hasCategory(Intent.CATEGORY_LAUNCHER) && Intent.ACTION_MAIN.equals(intent.getAction())) {
        Log.w(LOG_TAG, "Main Activity is not the root.  Finishing Main Activity instead of launching.");
        finish();
        return;       
    }
}