View is not locating the Viewmodel with default VML in MVVMCross - c#

I'm trying to create a Portable Class such that I can use that across the platforms. It is working fine in Windows Phone 8.1 App. But when it comes to Android, then it is showing the Viewmodel as null and DataContext as Null in debugger which breaks the application debugger. When I create another viewmodel and view to test the app, its working fine on android too. What can be the possible reasons.
EDIT : It is crashing due to the constructor , in which I am passing the business Logic instance. So , Constructor is necessary i think but in that case it is crashing.I am not trying to resolve the ViewModel , i am trying to resolve the Service instance in ViewModel and for the purpose of MVVM, I am keeping the Service out from Droid Project so base.OnCreate(bundle) does not come into scene anyways.
public BookViewModel(ILogic _logic)
{
logic = _logic;
//var ss= Mvx.Resolve<ILogic>();
//var x = Mvx.CanResolve<ILogic>();
_details = logic.Read();
}
Below is the Logic Code :
public class Logic : ILogic
{
#region Attributes
List<Detail.Detail> _details = new List<Detail.Detail>();
DataLayer.DataLayer dl = new DataLayer.DataLayer();
#endregion
#region .ctor
public Logic()
{
populateList();
}
#endregion
#region Methods
private void populateList()
{
_details = dl.Access();
}
Below is the App.cs in ViewModel in which CanResolve is giving False
public class App : Cirrious.MvvmCross.ViewModels.MvxApplication
{
#region Methods
public override void Initialize()
{
Mvx.RegisterType<ILogic, Logic>();
var ss = Mvx.CanResolve<ILogic>();
RegisterAppStart<ViewModels.BookViewModel>();
}
#endregion
}

There are a few questions and answers around similar to this - e.g. similar to MVVMCross ViewModel construction failure notifications
The basic answer is that MvvmCross cannot resolve the ViewModel during the constructor - you have to wait until after the base.OnCreate(bundle) call - at this point the ViewModel will be resolved.
There's also a bit more about when ViewModel's are located in Who should create view model instances in MvvmCross and CoreDispatcher.HasThreadAccess "breaking change" (and probably a few other places too)

Related

How to create view-model of prism when needed?

I really made a search for this topic and did not find anything, and because of that, I am asking the question here.
I have a WPF application with Prism installed.
I have wired the view-model with the view automatically by name convention
<UserControl x:Class="Views.ViewA"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True">
and the model in the 'Model' like this
public class ViewAViewModel {
public ViewAViewModel () {
// time-resource consuming operations
}
}
the automatic binding work perfectly without a problem and the view and its corresponding view-model is matching, but the problem here.
I have a lot of those views say (50) and for every one of them, the view-model will be created with constructor exhausting the processes. This will make the startup of the application longer and also it will create a lot of view-models objects and put them in the RAM without being sure that they will be used at all.
What I need is to create the view-model class when the view is activated (I mean when the view is navigated to). Is this possible and if yes how?
Update
here is how I register the view with the Module, this is causing all the views to be created when the startup of the module.
public class Module1 : IModule
{
public void OnInitialized(IContainerProvider containerProvider)
{
var regionManager = containerProvider.Resolve<IRegionManager>();
regionManager.RegisterViewWithRegion("region1", typeof(View1));
regionManager.RegisterViewWithRegion("region1", typeof(View2));
is there any way to delay the creating of the views, until the navigation request come?
You could use navigation, for each view.
Or you must create an interfaces for your view and view model.
An example:
public interface IMyView
{
IMyViewModel ViewModel { get; set; }
}
public interface IMyViewModel
{
}
In the module or app.cs, in the method RegisterTypes you should register these.
containerRegistry.Register<IMyView, MyView>();
containerRegistry.Register<IMyViewModel, MyViewModel>();
You must implement IMyView interface in your MyView.cs class.
public partial class MyView : UserControl, IMyView
{
public MyView(IMyViewModel viewModel)
{
InitializeComponent();
ViewModel = viewModel;
}
public IMyViewModel ViewModel
{
get => DataContext as IMyViewModel;
set => DataContext = value;
}
}
After you could use it:
public void OnInitialized(IContainerProvider containerProvider)
{
var regionManager = containerProvider.Resolve<IRegionManager>();
var firstView = containerProvider.Resolve<IMyView>();
regionManager.AddToRegion(RegionNames.MainRegion, firstView);
}
In such case you shouldn't use ViewModelLocator.AutoWireViewModel in your view.

How to clear data of ViewModel in MVVM Light xamrin?

I am working on Xamrin Form right now. I have problem with clear data of ViewModel.
When I logout and login with different user, it shows me data of previous user because the value of UserProfileViewModel doesn't get clear.
When user logout, I want to clear user data from UserProfileViewModel class file. Currently I do this manually when user click on logout. I want any default method like dispose to clear all class member.
I have tried to inherit IDisposable interface with this.Dispose(); but that also didn't work.
I have also tried with default constructor as following but it throws error of
`System.TypeInitializationException`
on this line in app.xaml.cs: public static ViewModelLocator Locator => _locator ?? (_locator = new ViewModelLocator());
public UserProfileViewModel()
{
//initialize all class member
}
In given code, you can see that on Logout call, I call method
`ClearProfileData` of `UserProfileViewModel`
which set default(clear)
data. It is manually. I want to clear data when user logout.
View Model Logout Page
[ImplementPropertyChanged]
public class LogoutViewModel : ViewModelBase
{
public LogoutViewModel(INavigationService nService, CurrentUserContext uContext, INotificationService inService)
{
//initialize all class member
private void Logout()
{
//call method of UserProfileViewModel
App.Locator.UserProfile.ClearProfileData();
//code for logout
}
}
}
User Profile View Model
[ImplementPropertyChanged]
public class UserProfileViewModel : ViewModelBase
{
public UserProfileViewModel(INavigationService nService, CurrentUserContext uContext, INotificationService inService)
{
//initialize all class member
}
//Is there any other way to clear the data rather manually?
public void ClearProfileData()
{
FirstName = LastName = UserName = string.Empty;
}
}
ViewModel Locator
public class ViewModelLocator
{
static ViewModelLocator()
{
MySol.Default.Register<UserProfileViewModel>();
}
public UserProfileViewModel UserProfile => ServiceLocator.Current.GetInstance<UserProfileViewModel>();
}
Firstly there is no need to cleanup these kinds of primitive data types, the gc will do that for you.
However if you use Messages or any other Strong Reference for that matter you WILL have to Unsubscribe from them otherwise your viewmodal will hang around in memory and will never go out of scope
The garbage collector cannot collect an object in use by an
application while the application's code can reach that object. The
application is said to have a strong reference to the object.
With Xamarin it really depends how you are coupling your View to Viewmodals to determine which approach you might take to cleanup your viewmodals.
As it turns out MVVM Light ViewModelBase implements an ICleanup interface which has an overridable Cleanup method for you.
ViewModelBase.Cleanup Method
To cleanup additional resources, override this method, clean up and
then call base.Cleanup().
public virtual void Cleanup()
{
// clean up your subs and stuff here
MessengerInstance.Unregister(this);
}
Now your just left with where to call ViewModelBase.Cleanup
You can just call it when your View Closes, if you get a reference to the DataContext (I.e ViewModalBase) on the DataContextChanged Event
Or you can wire up a BaseView that plumbs this for you, or you can implement your own NagigationService which calls Cleanup on Pop. It really does depend on who is creating your views and viewmodels and how you are coupling them

MVVM - How to use a given REST-API dll

I'm starting to learn MVVM in C# with mvvmlight. From an other project, I have a given (but self-made) REST-API as a dll. This dll I'm trying to use in this new project. It is completly based on this API. I'm unsure about the "model"-part and don't want to repeat myself while coding.
Problem is like this: The application has several "plugins/sections". Let's look at a sample like the "groups" section. Now the API defines a groups-Entity-Class with all properties like "groupname, permissions, members". Then, the Application must have a groups model and also a groupsViewModel and a GroupsView for the UI. There, I also must list/show "groupname, permissions, members".
Question is: Do I have to redeclare all the properties from the dll-entity-class in the mvvm-model-class? Or what is the best way to use a rest api? Is there any pattern. I read about the Extension Object Pattern. But I have no clue.
Hint: Since the API is written by myself (only classes, no mvvm or other frameworks), I could modify it as needed.
Thanks in advance!
Take a look at the Repository Pattern: https://msdn.microsoft.com/en-us/library/ff649690.aspx. Basically you create a repository which does the API calls, and transforms the entities to a format you can use in your view.
If the objects from the API are already sufficient you can simply return them, otherwise convert them in the Repository to something more useful.
In the ViewModel you then simply ask the repository to return the model, and need not to care how and in what format it got them.
Small Example:
interface IApiRepository
{
Task<ObservableCollection<ApiModel>> GetApiModelsAsync();
}
class ApiRepository : IApiRepository
{
private async Task<ObservableCollection> GetApiModelsAsync()
{
var myCollection = new ObservableCollection<ApiModel>();
var result = await DoMyApiCall();
foreach (result as item)
{
var newModel = new ApiModel();
newModel.fillFromApi(item);
myCollection.Add(newModel);
}
return myCollection;
}
}
class MyViewModel : ViewModelBase
{
private readonly IApiRepository _apiRepository;
public MyViewModel(IApiRepository apiRepository)
{
_apiRepository = apiRepository;
InitializeViewModel();
}
private ObservableCollection<ApiModel> _apiModels;
public ObservableCollection<ApiModel> ApiModels
{
get { return _apiModels; }
set { Set(ref _apiModels, value); }
}
private async void InitializeViewModel()
{
//as soon as the repo is finished ApiModels will raise the RaisePropertyChanged event
ApiModels = await _apiRepository.GetApiModelsAsync();
}
}
//in you ViewModelLocator
SimpleIoC.Default.Register<IApiRepository, ApiRepository>();
SimpleIoC.Default.Register<MyViewModel>();
//construct your viewmodel (with the injected repository)
var vm = SimpleIoc.Default.GetInstance<MyViewModel>();
with the help of this pattern you can do some other awesome things: 
if you add a new data source you need only to change the repository and not the ViewModel.
if you want to test your view (for example in Design Mode) you can inject a MockApiRepository which returns sample data instead of doing an API call

MVVM and DI with nested controls

I've been using MVVM for WPF quite a while now but I've always been doing it this way:
ExampleView.xaml.cs (namespace: Example.Views)
public partial class ExampleView
{
public ExampleView()
{
InitializeComponent();
var viewModel = new ExampleViewModel();
DataContext = viewModel;
}
}
The ExampleView.xaml has no code concerning the ExampleViewModel except for bindings to properties.
ExampleViewModel.cs (namespace: Example.ViewModels)
public ExampleViewModel()
{
// No important code in here concerning this topic. Code here is only used in this class.
}
Below is a simplified MainWindowView.xaml.
<Window ...
xmlns:views="clr-namespace:Example.Views">
<Grid>
<views:ExampleView />
</Grid>
</Window>
The MainWindowView.xaml.cs is similar to the ExampleView.xaml.cs. The MainWindowViewModel.cs has no important code concerning this topic.
Lastly, the App.xaml contains the StartupUri="Views/MainWindowView.xaml".
If this is a good pattern or not, I got my application to work. Since the application is not maintainable by me alone anymore, 2-3 people are now working on it creating some problems. One person is doing the majority of the coding (ViewModels basically), one person is doing the GUI (Views) and one person is doing the "framework" coding. (Using "" because this is not really a framework but I can't think of a better word for it.)
Now, I'm the guy that is doing the framework coding and I've been reading up on several subjects like dependency injection and the code below is what I came up with using UnityContainer from Windows.
ExampleView.xaml.cs (namespace: Example.Views)
public partial class ExampleView
{
public ExampleView()
{
InitializeComponent();
}
}
The ExampleView.xaml has no code concerning the ExampleViewModel except for bindings to properties.
ExampleViewModel.cs (namespace: Example.ViewModels)
public string MyText { get; set; }
public ExampleViewModel(ILocalizer localizer)
{
MyText = localizer.GetString("Title");
}
Below is a simplified MainWindowView.xaml.
<Window ...
xmlns:views="clr-namespace:Example.Views">
<Grid>
<views:ExampleView DataContext="{Binding ExampleViewModel}" />
</Grid>
</Window>
The MainWindowView.xaml.cs is similar to the ExampleView.xaml.cs.
MainWindowViewModel.cs
ExampleViewModel ExampleViewModel { get; set; }
private readonly ILocalizer _localizer;
private readonly IExceptionHandler _exHandler;
public MainWindowViewModel(ILocalizer localizer, IExceptionHandler exHandler)
{
_localizer = localizer;
_exHandler = exHandler;
ExampleViewModel = new ExampleViewModel(localizer);
}
Lastly, the App.xaml does not contains the StartupUri="..." anymore. It's now done in App.xaml.cs. It's also here where the `UnityContainer is initialized.
protected override void OnStartup(StartupEventArgs e)
{
// Base startup.
base.OnStartup(e);
// Initialize the container.
var container = new UnityContainer();
// Register types and instances with the container.
container.RegisterType<ILocalizer, Localizer>();
container.RegisterType<IExceptionHandler, ExceptionHandler>();
// For some reason I need to initialize this myself. See further in post what the constructor is of the Localizer and ExceptionHandler classes.
container.RegisterInstance<ILocalizer>(new Localizer());
container.RegisterInstance<IExceptionHandler>(new ExceptionHandler());
container.RegisterType<MainWindowViewModel>();
// Initialize the main window.
var mainWindowView = new MainWindowView { DataContext = container.Resolve<MainWindowViewModel>() };
// This is a self made alternative to the default MessageBox. This is a static class with a private constructor like the default MessageBox.
MyMessageBox.Initialize(mainWindowView, container.Resolve<ILocalizer>());
// Show the main window.
mainWindowView.Show();
}
For some reason I need to initialize the Localizer and ExceptionHandler classes myself. The Localizer and ExceptionHandler constructors are found below. Both have constructors with all arguments that have a default value. Adding constructors without arguments like
public ExceptionHandler() : this(Path.Combine(Directory.GetCurrentDirectory(), "ErrorLogs", DateTime.Now.ToString("dd-MM-yyyy") + ".log")) { }
doesn't change a thing.
public Localizer(ResourceDictionary appResDic = null, string projectName = null, string languagesDirectoryName = "Languages", string fileBaseName = "Language", string fallbackLanguage = "en")
{
_appResDic = appResDic ?? Application.Current.Resources;
_projectName = !string.IsNullOrEmpty(projectName) ? projectName : Application.Current.ToString().Split('.')[0];
_languagesDirectoryName = languagesDirectoryName.ThrowArgNullExIfNullOrEmpty("languagesFolder", "0X000000066::The languages directory name can't be null or an empty string.");
_fileBaseName = fileBaseName.ThrowArgNullExIfNullOrEmpty("fileBaseName", "0X000000067::The base name of the language files can't be null or an empty string.");
_fallbackLanguage = fallbackLanguage.ThrowArgNullExIfNullOrEmpty("fallbackLanguage", "0X000000068::The fallback language can't be null or an empty string.");
CurrentLanguage = _fallbackLanguage;
}
public ExceptionHandler(string logLocation = null, ILocalizer localizer = null)
{
// Check if the log location is not null or an empty string.
LogLocation = string.IsNullOrEmpty(logLocation) ? Path.Combine(Directory.GetCurrentDirectory(), "ErrorLogs", DateTime.Now.ToString("dd-MM-yyyy") + ".log") : logLocation;
_localizer = localizer;
}
My big question now is if I'm approaching dependency injection correctly and if having several static classes that I initialize once are bad. Several topics I've read state that static classes are a bad-practice because of bad testability and tightly coupled code, but right now the tradeoffs of dependency injection are bigger than having static classes.
Doing dependency injection correctly would be a first step in having less tightly coupled code though. I like the approach with the static MyMessageBox I can initialize once and that it's globally available in the application. This is mainly for "easy usage" I guess cause I can simply call MyMessageBox.Show(...) instead of injecting this all the way down to the smallest element. I have a similar opinion about the Localizer and ExceptionHandler because these will be used even more.
A last concern I have is the following. Lets say I have a class with several arguments and one of the arguments is the Localizer (because this will be used in nearly any class). Having to add ILocalizer localizer every time
var myClass = new MyClass(..., ILocalizer localizer);
feels very annoying. This would push me towards a static Localizer I initialize once and having never to care about it anymore. How would this problem be tackled?
If you have a bunch of "Services" which are used in many classes, you can create a facade class which encapsulates the required services and inject the facade into your classes.
Advantage of doing so is, you can easily add other services to that facade and they'd be available in all other injected classes, without modifying the constructor parameters.
public class CoreServicesFacade : ICoreServicesFacade
{
private readonly ILocalizer localizer;
private readonly IExceptionHandler excaptionHandler;
private readonly ILogger logger;
public ILocalizer Localizer { get { return localizer; } }
public IExceptionHandler ExcaptionHandler{ get { return exceptionHandler; } }
public ILogger Logger { get { return logger; } }
public CoreServices(ILocalizer localizer, IExceptionHandler exceptionHandler, ILogger logger)
{
if(localizer==null)
throw new ArgumentNullException("localizer");
if(exceptionHandler==null)
throw new ArgumentNullException("exceptionHandler");
if(logger==null)
throw new ArgumentNullException(logger);
this.localizer = localizer;
this.exceptionHandler = exceptionHandler;
this.logger = logger;
}
}
Then you can pass it to your classes:
var myClass = new MyClass(..., ICoreServicesFacade coreServices);
(which you shouldn't do anyway when using Dependency Injection, you shouldn't use new keyword, except for factories and models).
As for your ILocalizer and IExceptionHandler implementations... if your ExceptionHandler requires the Localizer and the localizer requires the string parameter, you have two options, depending on if the file name needs to be determined at a later point at run time or only once during the Application initialization.
Important
Don't use optional constructor parameters if you want to use dependency injection. For DI, constructor parameters should declare the dependencies in constructor and constructor dependencies are always considered as mandatory (don't use ILocalizer localizer = null within the constructor).
If you only create the logfile during the Applications initialization, it's quite easy
var logFilePath = Path.Combine(Directory.GetCurrentDirectory(), "ErrorLogs", DateTime.Now.ToString("dd-MM-yyyy") + ".log");
var localizer = new Localizer(...);
var exceptionHandler = new ExceptionHandler(logFilePath, localizer);
container.RegisterInstance<ILocalizer>(localizer);
container.RegisterInstance<IExceptionHandler>(exceptionHandler);
Basically in your bootstrapper you instantiate and configure your Localizer and ExceptionHandler, then register it as instance with the container.
If for some reason, you need to determine the name of log filename or language at a later point (after Bootstrapper configuration & initialization), you need to use a different approach: You need a factory class.
The factory will be injected into your classes rather than the instance of ILocalizer/IExceptionHandler and create the instance of it when the parameters are known.
public interface ILocalizerFactory
{
ILocalizer Create(ResourceDictionary appResDic, string projectName);
}
public class ILocalizerFactory
{
public ILocalizer Create(ResourceDictionary appResDic, string projectName)
{
var localizer = new Localizer(appResDic, projectName, "Languages", "Language", "en");
return localizer;
}
}
Using the facade Example from above:
public class CoreServicesFacade : ICoreServicesFacade
{
private readonly ILocalizer localizer;
public ILocalizer Localizer { get { return localizer; } }
public CoreServices(ILocalizerFactory localizerFactory, ...)
{
if(localizer==null)
throw new ArgumentNullException("localizerFactory");
this.localizer = localizerFactory.Create( Application.Current.Resources, Application.Current.ToString().Split('.')[0]);
}
}
Caveats & tips
Move Default configuration outside of the classes itself
Don't use such code inside your Localizer/ExceptionHandler classes.
_appResDic = appResDic ?? Application.Current.Resources;
_projectName = !string.IsNullOrEmpty(projectName) ? projectName : Application.Current.ToString().Split('.')[0];
_languagesDirectoryName = languagesDirectoryName.ThrowArgNullExIfNullOrEmpty("languagesFolder", "0X000000066::The languages directory name can't be null or an empty string.");
_fileBaseName = fileBaseName.ThrowArgNullExIfNullOrEmpty("fileBaseName", "0X000000067::The base name of the language files can't be null or an empty string.");
_fallbackLanguage = fallbackLanguage.ThrowArgNullExIfNullOrEmpty("fallbackLanguage", "0X000000068::The fallback language can't be null or an empty string.");
CurrentLanguage = _fallbackLanguage;
This pretty much makes it untestable and puts the configuration logic in the wrong place. You should only accept and validate the parameters passed into the constructor and determine the values and fall backs in either a) factory's create method or b) inside your bootstrapper (if runtime parameters aren't required).
Don't use View-related type inside your interfaces
Don't use ResourceDictionary in your public interfaces, this will leak View knowledge into your ViewModels and require you to have a reference to the assembly containing View/Application related code (I know I used it above, based on your Locator constructor).
If you need it, pass it as constructor Parameter and implement the class in Application/View assembly, while having your Interface in your ViewModel assembly). Constructors are implementation detail, and can be hidden (by implementing the class in a different assembly which allows reference to the class in question).
Static classes are evil
As you already realized, static classes are bad. Inject them is the way to go. Your Application will most likely need a navigation too. So you can put Navigation (Navigate to a certain View), MessageBoxes (display an information) and opening of new Windows (a kind of navigation too) into either one service or a navigation facade (similar to above one) and pass all services related to navigation as a single Dependency into your objects.
Passing parameters to ViewModel
Passing parameters can be a bit of a pain in "home-brew" frameworks and you shouldn't pass parameters via ViewModel constructors (prevents DI from resolving it or forcing you to use a factory). Instead consider writing a navigation service (or using exiting framework). Prims has it solved pretty nicely, you got a navigation service (which will do the navigation to a certain View and it's ViewModel and also offers INavigationAware interface with NavigateTo and NavigateFrom methods, which are called when one navigates to a new view (one of this methods parameters can be used to provide parameters to the ViewModel) and when navigating from a ViewModel (i.e. to determine if navigating from a view is viable or to cancel the navigation if necessary, for example: Asking the user to save or discard the data before navigating to the other ViewModel).
But that's bit off-topic.
Example:
public class ExampleViewModel : ViewModelBase
{
public ExampleViewModel(Example2ViewModel example2ViewModel)
{
}
}
public class Example2ViewModel : ViewModelBase
{
public Example2ViewModel(ICustomerRepository customerRepository)
{
}
}
public class MainWindowViewModel : ViewModelBase
{
public MainWindowViewModel(ExampleViewModel example2ViewModel)
{
}
}
// Unity Bootstrapper Configuration
container.RegisterType<ICustomerRepository, SqlCustomerRepository>();
// You don't need to register Example2ViewModel and ExampleViewModel unless
// you want change their container lifetime manager or use InjectionFactory
To get an resolve instance of your MainWindowViewModel simply do
MainWindowViewModel mainWindowViewModel = container.Resolve<MainWindowViewModel>();
and Unity will resolve all other dependencies (it will inject ICustomerRepository into Example2ViewModel, then inject Example2ViewModel into ExampleViewModel and finally inject ExampleViewModel into your MainWindowViewModel and return an instance of it.
The catch is: You can't use the container inside your ViewModels (though using it in View's code-behind is okay in your use case. However it's better to use navigation Service or a ViewModel Locator within your XAML (see Prism on how they did it)) .
So you need a navigation service of a kind, if you need to do it from ViewModels.

Ninject + ASP.NET Web Forms Not Working

I've successfully implemented Ninject in an MVC3 application, but am running into some trouble doing the same thing with ASP.NET Web Forms. I'm getting null references every time I try to access an injected property in my business layer. After setting breakpoints within the CreateKernel method, as well as several places within the ServiceLocator class, it looks like none of them are ever getting hit, so it's not even loading.
I'm sure I'm just approaching this wrong, but there is very little documentation or info out there for wiring up Ninject in a Web Forms application.
Basically here's what I have so far:
code behind
public class ReviewManager
{
[Inject] private IReviewRepository _reviewRepository { get; set; }
public ReviewManager() { }
public ReviewManager(IReviewRepository reviewRepository)
{
_reviewRepository = reviewRepository;
}
public Review GetById(int id)
{
if (id <= 0) throw new ArgumentException("ID must be greater than zero");
**I get a null reference exception on the next line. _reviewRepository is null**
return _reviewRepository.GetById(id);
}
}
global.asax.cs
public class Global : NinjectHttpApplication
{
protected override IKernel CreateKernel()
{
return ServiceLocator.Kernel;
}
// deleted for brevity
}
ServiceLocator.cs (edited for brevity, the relevant parts are here)
public static class ServiceLocator
{
public static IKernel Kernel { get; set; }
public static ILogger Logger { get; set; }
static ServiceLocator()
{
Kernel = new StandardKernel(new INinjectModule[] {
new LoggerBindings(),
new DataBindings()
});
if (Logger == null)
Logger = Kernel.Get<ILogger>();
}
}
public class LoggerBindings : NinjectModule
{
public override void Load()
{
Bind<ILogger>().To<NLogLogger>();
}
}
public class DataBindings : NinjectModule
{
public override void Load()
{
Bind<IReviewRepository>().To<ReviewRepository>();
}
}
ASP.Net via WebForms does not allow you to manage the lifecycle of all object instances (like MVC does). For example, the framework instantiates page objects. This means you probably can't implement DI in quite the same way as you would in MVC/WPF/Silverlight (the same problem is present in WinForms IIRC). You will likely have to initiate the dependency graph directly in each of your code behinds.
Translation: you will want to call ServiceLocator.Kernel.Get<IReviewRepository> when your page loads (or as lazy-init on the property).
The cool thing about MVC ist that it can run side a side of ASP.NET WebForm pages in the same application. In my opinion the best way to extend ASP.NET WebForms websites is to create new pages using MVC3 and to refactor every page that needs major changes to MVC3.
If this is no option go and use the Ninject.Web extension. It contains a IHttpModule that property injects all web pages and controlls after they are initialized. That way you can property inject the services als have them created by Ninject.
A potential workaround, by changing your DataBindings class as follows:
public class DataBindings : NinjectModule
{
public override void Load()
{
Bind<IReviewRepository>().To<ReviewRepository>();
Bind<ReviewManager>().ToSelf();
}
}
And within your caller, instead of
var rm = new ReviewManager();
Try using
var rm = ServiceLocator.Kernel.Get<ReviewManager>();
I havent tested this code, but i think it'll solve your null reference problem.
I use property injection for pages, masterpages and usercontrols. All my pages, for example, inherit from a base class that overrides RequestActivation method with the following code:
''' <summary>
''' Asks the kernel to inject this instance.
''' </summary>
Protected Overridable Sub RequestActivation()
ServiceLocator.Kernel.Inject(Me)
End Sub
And in each page I declare injectable properties:
<Inject()>
Property repo As IMyRepository

Categories