I'm working on a little test app to learn how to implement singletons in WP7 using Caliburn.Micro. The app is very straightforward:
- In the bootstrapper, I register a singleton object called "DataModel," which has a single property--a string with a getter and setter.
- This singleton needs to be accessible from two viewmodels, MainPageViewModel and DataPageViewModel.
- I want to update the contents of the string from my second page (DataPage) and display the value on MainPage when I navigate back to it.
Once I get this figured out I will be using it to implement a master/details view in the for-production app I'm working on.
The test project builds and runs if I remove all references to the DataModel singleton. If I add the references back in and step through the code in debug mode, I find that DataModel is registered by the bootstrapper. Once the bootstrapper calls AddCustomConventions(),
I am getting a null reference exception on line 113 of SimpleContainer.cs:
System.NullReferenceException was unhandled
at System.Activator.InternalCreateInstance(Type type, BindingFlags invokeAttr, Binder binder, Object args, CultureInfo culture, StackCrawlMark& stackMark)
at System.Activator.CreateInstance(Type type, Object args)
at IoCTest.Framework.SimpleContainer.ActivateInstance(Type type, Object args)
at IoCTest.Framework.PhoneContainer.ActivateInstance(Type type, Object args)
at IoCTest.Framework.SimpleContainer.BuildInstance(Type type)
at IoCTest.Framework.SimpleContainer.GetInstance(Type service, String key)
at IoCTest.AppBootstrapper.GetInstance(Type service, String key)
at IoCTest.Framework.ViewModelLocator.<.cctor>b__0(Type viewType)
at IoCTest.Framework.ViewModelLocator.<.cctor>b__1(Object view)
at IoCTest.Framework.FrameAdapter.OnNavigated(Object sender, NavigationEventArgs e)
at System.Windows.Navigation.NavigationService.RaiseNavigated(Object content, Uri uri, PhoneApplicationPage existingContentPage, PhoneApplicationPage newContentPage)
at System.Windows.Navigation.NavigationService.CompleteNavigation(DependencyObject content)
at System.Windows.Navigation.NavigationService.ContentLoader_BeginLoad_Callback(IAsyncResult result)
at System.Windows.Navigation.PageResourceContentLoader.BeginLoad_OnUIThread(AsyncCallback userCallback, PageResourceContentLoaderAsyncResult result)
at System.Windows.Navigation.PageResourceContentLoader.<>c__DisplayClass4.b__0(Object args)
at System.Reflection.RuntimeMethodInfo.InternalInvoke(RuntimeMethodInfo rtmi, Object obj, BindingFlags invokeAttr, Binder binder, Object parameters, CultureInfo culture, Boolean isBinderDefault, Assembly caller, Boolean verifyAccess, StackCrawlMark& stackMark)
at System.Reflection.RuntimeMethodInfo.InternalInvoke(Object obj, BindingFlags invokeAttr, Binder binder, Object parameters, CultureInfo culture, StackCrawlMark& stackMark)
at System.Reflection.MethodBase.Invoke(Object obj, Object parameters)
at System.Delegate.DynamicInvokeOne(Object args)
at System.MulticastDelegate.DynamicInvokeImpl(Object args)
at System.Delegate.DynamicInvoke(Object args)
at System.Windows.Threading.Dispatcher.Dispatch(DispatcherPriority priority)
at System.Windows.Threading.Dispatcher.OnInvoke(Object context)
at System.Windows.Hosting.CallbackCookie.Invoke(Object args)
at System.Windows.Hosting.DelegateWrapper.InternalInvoke(Object args)
at System.Windows.RuntimeHost.ManagedHost.InvokeDelegate(IntPtr pHandle, Int32 nParamCount, ScriptParam pParams, ScriptParam& pResult)
It seems like I need to actually instantiate the object at some point, but I'm not sure where/how I would do that, and I can't find any examples. (The WP7 blog post shows singleton viewmodels in use, but the sample app doesn't use singletons.)
Here's my bootstrapper code:
protected override void Configure()
LogManager.GetLog = type => new DebugLogger(type);
container = new PhoneContainer(this);
container.RegisterSingleton(typeof(MainPageViewModel), "MainPageViewModel", typeof(MainPageViewModel));
container.RegisterPerRequest(typeof(DataPageViewModel), "DataPageViewModel", typeof(DataPageViewModel));
container.RegisterSingleton(typeof(DataModel), "DataModel", typeof(DataModel));
container.RegisterInstance(typeof(INavigationService), null, new FrameAdapter(RootFrame));
container.RegisterInstance(typeof(IPhoneService), null, new PhoneApplicationServiceAdapter(PhoneService));
And an example of one of the viewmodels:
public class MainPageViewModel : PropertyChangedBase
private readonly INavigationService navigationService;
private readonly DataModel dataModel;
public MainPageViewModel(INavigationService navigation, DataModel dataModel)
navigationService = navigation;
this.dataModel = dataModel;
public void GetData()
navigationService.Navigate(new Uri("/Views/DataPage.xaml", UriKind.RelativeOrAbsolute));
I'm not currently doing anything with dataModel; I was just trying to get the project to build before I worried about actually manipulating it.
Am I supposed to create an instance of DataModel and somehow assign it to the container, or does the bootstrapper take care of that for me? What else might I be missing?
Thanks for the help!
Mar 18, 2011 at 1:14 PM
For the simple container, you should only provide the "key" value if you intend to resolve by it. In the case of DataModel, you do not want to resolve by key, but by type. So, your registration should be:
container.RegisterSingleton(typeof(DataModel), null, typeof(DataModel));
I'm not sure if that will fix everything, but it's definitely a problem.
Yep, that appears to be the problem. No more null reference exceptions, and I seem to be able to access the object from my viewmodels. Thanks!
One other related question that I had: What are the pitfalls I should look out for in using singletons in my projects? They seem to come under a lot of criticism, but for an environment like Silverlight where it's sometimes clunky to maintain state, the
judicious use of a few singletons seems like a good way to ease the pain. But I also don't want to get too comfortable in a pattern that has its own drawbacks... Any words of wisdom would be much appreciated.