Prism с Xamarin для Android — исключение InvalidOperationException при запуске

#xamarin.forms #push-notification #xamarin.android #firebase-cloud-messaging #prism

#xamarin #xamarin.forms #xamarin.android #unity-контейнер #prism

Вопрос:

Я новичок в Xamarin и Prism, поэтому простите меня, если я упускаю что-то очевидное. Я следую примеру этого проекта в GitHub. Однако при запуске моего проекта Android я получаю следующую ошибку.

Система.Исключение InvalidOperationException: текущий тип, MyApp.Abstractions.IFacebookManager, является интерфейсом и не может быть сконструирован. Вам не хватает сопоставления типов?

Ошибка возникает в EntryPage.xaml.g.cs в LoadFromXaml() методе.

 [global::Xamarin.Forms.Xaml.XamlFilePathAttribute("Views\EntryPage.xaml")]
public partial class EntryPage : global::Xamarin.Forms.ContentPage {

    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Forms.Build.Tasks.XamlG", "2.0.0.0")]
    private void InitializeComponent() {
        // Exception thrown here!
        global::Xamarin.Forms.Xaml.Extensions.LoadFromXaml(this, typeof(EntryPage));
    }
}
  

Это наводит меня на мысль, что я неправильно использую контейнер IoC, но я не вижу, что я делаю иначе, чем в примере. EntryPageViewModel Конструктор принимает IFacebookManager в качестве параметра, и я понимаю, что Unity должен позаботиться об этом. У меня есть точка останова в конструкторе, но она не попадает. Это решение Xamarin .NET Standard. Я был бы признателен за любую помощь и указания. Спасибо!

Вот мой App.xaml.cs

 using MyApp.Abstractions;
using MyApp.Helpers;
using MyApp.Services;
using MyApp.ViewModels;
using MyApp.Views;
using Prism;
using Prism.Ioc;
using Prism.Unity;

using Xamarin.Forms;

namespace MyApp
{
    public partial class App : PrismApplication
    {
        public App(IPlatformInitializer initializer = null) : base(initializer) { }


        protected override void OnInitialized()
        {
            InitializeComponent();

            ServiceResolver.Instance.Add<ICloudService, AzureCloudService>();

            NavigationService.NavigateAsync("NavigationPage/EntryPage");            
        }



        protected override void RegisterTypes(IContainerRegistry containerRegistry)
        {
            containerRegistry.RegisterForNavigation<NavigationPage>();
            containerRegistry.RegisterForNavigation<EntryPage, EntryPageViewModel>();
        }
    }
}
  

Вот мой MainActivity.cs

 using Android.App;
using Android.Content.PM;
using Android.OS;
using MyApp.Droid.Services;
using MyApp.Abstractions;
using Android.Content;
using Xamarin.Facebook;
using Xamarin.Forms;
using Prism;
using Prism.Ioc;

namespace MyApp.Droid
{
    [Activity(Label = "MyApp", Icon = "@drawable/icon", Theme = "@style/MainTheme", Name = "com.mydomain.myapp.MainActivity", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
    {
        ICallbackManager fbCallbackManager;
        AndroidLoginProvider loginProvider;

        protected override void OnCreate(Bundle bundle)
        {
            TabLayoutResource = Resource.Layout.Tabbar;
            ToolbarResource = Resource.Layout.Toolbar;
            base.OnCreate(bundle);
            Microsoft.WindowsAzure.MobileServices.CurrentPlatform.Init();                        
            global::Xamarin.Forms.Forms.Init(this, bundle);
            DependencyService.Register<IFacebookManager, FacebookManager>();

            LoadApplication(new App(new AndroidInitializer()));
        }

        protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
        {
            base.OnActivityResult(requestCode, resultCode, data);

            var manager = DependencyService.Get<IFacebookManager>();
            if (manager != null)
            {
                (manager as FacebookManager)._callbackManager.OnActivityResult(requestCode, (int)resultCode, data);
            }
        }
    }

    public class AndroidInitializer : IPlatformInitializer
    {
        public void RegisterTypes(IContainerRegistry containerRegistry)
        {
        }
    }
}



public class AndroidInitializer : IPlatformInitializer
{
    public void RegisterTypes(IContainerRegistry containerRegistry)
    {
    }
}
  

Ответ №1:

Вы смешиваете службу зависимостей Xamarin Forms со службой, предоставляемой с Prism.Unity

Вместо вызова DependencyService.Register<IFacebookManager, FacebookManager>();

Вам нужно зарегистрировать его в AndroidInitializer .

 public class AndroidInitializer : IPlatformInitializer
{
    public void RegisterTypes(IContainerRegistry container)
    {
        container.RegisterSingleton<IFacebookManager, FacebookManager>();
    }
}
  

Затем вы всегда можете разрешить зависимость вручную, вызвав App.Container.Resolve inside OnActivityResult .

Комментарии:

1. Я думал об этом, но пытался следовать примеру. В любом случае, этот подход кажется хорошим, но я не могу вызвать App.Container.Resolve<IFacebookManager>() , потому что я получаю ошибку: An object reference is required for the non-static field, method, or property 'PrismApplicationBase.Container.' Я предполагаю, что App здесь должна быть ссылка на экземпляр App класса, а не ссылка на сам класс. Есть ли простой способ получить ссылку на текущий экземпляр приложения из моего проекта Android?

2. @NSouth Да, вы можете вызвать ((приложение) Xamarin. Формы. Приложение. Текущий). Container.Resolve()

3. Я должен указать здесь, что Prism 7.1, который имеет ContainerProvider<T> для разрешения типов в XAML, также переопределяет статическое текущее свойство, чтобы вы могли сделать App.Current.Container.Resolve<T>() . Также вы не должны использовать ServiceLocator или службу зависимостей XF, поскольку это создает анти-шаблон с несколькими контейнерами.

Ответ №2:

Чтобы завершить предыдущий ответ, который мне очень помог решить аналогичную проблему: с prism 7 вам необходимо разрешить зависимость с помощью IContainerProvider следующим образом:

 var container = (App.Current as Prism.Unity.PrismApplication).Container;
var manager = container.Resolve<IFacebookManager>();
  

В этом случае Unity используется в качестве «контейнера» IoC

Комментарии:

1. Есть ли что-то вроде «универсального» контейнера?

2. Извините, не могли бы вы объяснить свой вопрос, я не понимаю, что вы имеете в виду

3. Вместо указания, какое IoC пространство имен я буду использовать, есть что-то вроде: (App.Current как Prism. Универсальное приложение PRISM). Контейнер?

4. Я бы сказал, что нет, вы не можете. Но обратите внимание, что теперь, вероятно, из-за новой версии Prism (7.1), вы можете получить свой контейнер таким образом: container=PrismApplicationBase.Current.Container; Это позволяет вам получить его, не указывая, какой IoC вы будете использовать.

5. Отлично! Спасибо за эту информацию 🙂 Я бы хотел, чтобы они включили это в свои примечания к выпуску.