Autofac SingleInstance() и формы Xamarin
Для начала позвольте мне сказать, что я прочитал здесь несколько вопросов об SingleInstance, но все еще не могу найти прямой ответ, который мне помогает. Тем не менее, я прошу прощения, если я что-то пропустил.
Вот мой вопрос:
Я создаю приложение Xamarin Forms для iOS и Android. У меня есть один класс AppInitializer в PCL, где я регистрирую все свои зависимости интерфейса, используя Autofac. Затем я назначаю Контейнер из компоновщика как статическое свойство класса приложения. Проблема, с которой я сталкиваюсь, заключается в том, что, хотя я регистрирую все с помощью.SingleInstance(), я не получаю ни одного экземпляра.
Пример логики инициализации:
var builder = new ContainerBuilder();
builder.RegisterType<ErrorHandler>().SingleInstance().As<IErrorHandler>();
…
builder.RegisterType<MemberViewModel>().SingleInstance().As<IMemberViewModel>();
…
AppContainer.Current = builder.Build();
Я позволяю Autofac обрабатывать разрешающие интерфейсы в моих конструкторах. Например:
public MemberViewModel(ISettingsViewModel settings, IErrorHandler errorHandler, …) : base(settings, errorHandler){…}
Затем я использую указанную модель на странице, как показано ниже:
Пример использования страницы:
public ProfilePage()
{
InitializeComponent();
var displayModel = Model.CurrentMember;
…
}
…
**public IMemberViewModel Model =>
AppContainer.Current.Resolve<IMemberViewModel>();**
В этом примере я устанавливаю свойства Model.CurrentMember непосредственно перед тем, как попасть на эту страницу. Я установил контрольные точки и точно знаю, что это происходит. Однако, когда я разрешаю экземпляр модели, свойства в CurrentMember равны нулю.
Я что-то здесь не так делаю или я столкнулся с ошибкой?
-Edit- дал понять, что я использую Autofac.
-Редакт 2- Добавление более подробно.
Моя реализация класса IMemberViewModel имеет различные свойства, в том числе наблюдаемый объект, называемый текущим членом. Это заявлено как ниже:
public class MemberViewModel : ViewModelBase, IMemberViewModel
{
…
(see constructor above)
…
public MemberDisplay CurrentMember =>
m_CurrentMember ?? (m_CurrentMember = new MemberDisplay())
В реализации IMemberViewModel у меня есть метод, который устанавливает различные свойства в CurrentMember.
Порядок операций такой:
Конечный пользователь нажимает на изображение для члена. Это запускает команду в (теоретически) одноэлементном экземпляре реализации IMemberViewModel. Эта команда выполняет асинхронную задачу, которая ожидает асинхронного вызова API для загрузки данных для этого члена. После загрузки этих данных и установки свойств в CurrentMember приложение переходит к экрану профиля. Экран профиля разрешает IMemberViewModel (согласно выше).
Ожидаемое поведение: свойства CurrentMember из разрешенного экземпляра IMemberViewModel устанавливаются в значения, которые были только что установлены из метода загрузки данных. Это ожидание возникает из предположения, что существует единственный экземпляр IMemberViewModel.
Фактическое поведение: свойства CurrentMember имеют свои значения по умолчанию, то есть string.Empty, 0, null и т. Д.
Странно то, что это происходит не с каждой моделью. У меня есть модель сообщения, которую я решаю таким же образом на том же экране, и, кажется, все в порядке.
1 ответ
Эта проблема оказалась вызвана тем, как мы собирались все инициализировать. Ради потомков я кратко расскажу о том, что происходило и что я сделал, чтобы предотвратить это.
Предыдущий поток приложений:
- Приложение открывается и конструктор называется. Это вызывает процедуру инициализации выше.
- Пользователь входит в систему.
- Первый экземпляр IMemberViewModel разрешен с использованием статического контейнера.
- Появляется сообщение, запрашивающее у пользователя разрешения на push-уведомления.
- Когда это происходит, вызывается приложение OnSleep (iOS)
- После того, как пользователь выбирает ответ, вызывается OnResume.
- OnResume вызывает процедуру инициализации
- Новый контейнер создан.
- Вызов для загрузки данных происходит в старом контейнере, новые страницы ссылаются на новый контейнер.
- Проблема возникает как описано выше.
Исправление к потоку:
Во-первых, из того, что я могу сказать, не нужно делать вызовы init при возобновлении и / или запуске, если они сделаны в конструкторе приложения. Если приложение "убито" из-за того, что другим приложениям требуется пространство памяти, при следующем запуске будет создана новая версия приложения (см. Жизненный цикл активности Android и Жизненный цикл приложения iOS).
Во-вторых, из-за того, что я параноик и потому что это не может повредить, в процедуре инициализации приложения я сейчас проверяю, существует ли контейнер и зарегистрирован ли интерфейс.
public static void Init(ISetup setup)
{
if (Container != null && IsModelRegistered()) return;
RegisterDependencies(setup);
…
}
private static bool IsModelRegistered()
{
return Container.IsRegistered<IMemberViewModel>();
}