MVVMLight Messenger.Unregister - should I unregister and how to do it? - mvvm-light

I've looked at 'Messenger and references' discussion, but I'm writing a new topic, because my issue is not technical, and I don't want to offtop there.
I've encountered a doubt - Have I to code cleanup()/RequestCleanup() method implementation to unregister previously registered Messenger in my viewmodel class? I'm afraid of memory leaks in the future.
I think I've found the documentation not to be enough bright for me.
Description of Messenger.Register is: '... Registering a recipient does not create a hard reference to it, so if this recipient is deleted, no memory leak is caused.'
1) Is this mean that I don't have to take care of it and implement-develop following solutions?
On the other hand, we can find in the code of GalaSoft.MvvmLight.ViewModelBase abstract class the short info about the Cleanup() method:
//
// Summary:
// Unregisters this instance from the Messenger class.
// To cleanup additional resources, override this method, clean up and then
// call base.Cleanup().
public virtual void Cleanup();
so 2) Is only invoking a Cleanup enough to unregister class-instance out of the Messenger?
3) Or maybe I have to invoke Messenger.Default.Unregister(this); in the body of a Cleanup method?
4) In the Unregister(Object) doc we read 'Unregisters a messager recipient completely' - what does the 'completely' mean?
I'm very sorry if my post seems to have out of the context quotes, I wanted to point out what I'm more interested in.
EDIT 1:
Hello Joel, thanks for reply. I've got several questions:
1) I have used your code. There's defined override void Cleanup() in CustomerMasterViewModel. Where to call it? Should I declare destructor in this case or maybe the ViewModelBase has an automatic mechanism for invoking the Cleanup()?
2) I have in my project another base class from a different toolkit, so my VMs cannot derive from both at the same time. How to organise your code to get the same effect by implementing only ICleanup interface?
public class CustomerMasterViewModel : SomeBaseClass, ICleanup
{
public CustomerMasterViewModel()
{
Messenger.Default.Register<Message>(this, this.MessageReceived);
}
#region messages
private void MessageReceived(Message obj)
{
//do something
}
#endregion
#region helper methods
public override void Cleanup()
{
//base.Cleanup();//there's no implementaction in an interface
ViewModelLocator.Cleanup();
}
#endregion
}

You have to invoke the Cleanup() method in GalaSoft.MvvmLight.ViewModelBase on each of you view models you wan't to dispose don't need any longer.
Example:
Let say your application has a tab control with different tabs. Each of your tabs displays a UserControl which has a dedicated ViewModel. The user has the ability to close a tabs which causes the underlining ViewModel to become obsolete.
Want you want to do now is to clean up the ViewModel calling the Cleanup() method in GalaSoft.MvvmLight.ViewModelBase. This will unregister ALL registered messages. The GarbageCollector will take care of you viewmodel if there are no other references.
Assuming you use the ViewModelLocator which also comes with the MVVM Light Framework you're not done yet because at least the ViewModelLocator itself has a reference to your viewmodel! Therefore the Garbage Collector can't finalize your viewmodel.
But it also has another side effect. When the user reopens the tab (Lets say the user is able to do so) the UserControl is loaded again and the ViewModelLocator will give you the same ViewModel instance. The only difference is that there are not registered messages because you cleaned them by calling the CleanUp() method.
What you need is a new instance of your ViewModel. To achieve this you have to clean up your ViewModelLocator as well!
You have to unregister them (Unregister<CustomerMasterViewModel>()) one by one or simply call Reset() which will unregister all viewmodels.
Then there should be no other reference to you viewmodel and the GarbageCollector can finally take care about it.
Here is an example to do so:
ViewModelLocator:
public class ViewModelLocator
{
public ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
SimpleIoc.Default.Register<CustomerMasterViewModel>();
}
public CustomerMasterViewModel CustomerMasterViewModel
{
get
{
return ServiceLocator.Current.GetInstance<CustomerMasterViewModel>();
}
}
public static void Cleanup()
{
SimpleIoc.Default.Reset();
//Don't forget to register them if the user attempts to open the new.
//The viewmodel initialization is lazy by default so this comes at no costs.
SimpleIoc.Default.Register<CustomerMasterViewModel>();
}
}
ViewModel
public class CustomerMasterViewModel : ViewModelBase
{
public CustomerMasterViewModel()
{
Messenger.Default.Register<Message>(this, this.MessageReceived);
}
#region messages
private void MessageReceived(Message obj)
{
//do something
}
#endregion
#region helper methods
public override void Cleanup()
{
base.Cleanup();
ViewModelLocator.Cleanup();
}
#endregion
}
In Short:
1) As far as i understood clean up is necessary after you're done.
2) Yes, calling the Cleanup() method in GalaSoft.MvvmLight.ViewModelBase will unregister all messages for this viewmodel.
3) No, see above.
4) Completely means it will unregister ALL registered messages.

Related

Re-instantiate a singleton with Prism in Xamarin Forms

How can I dispose and re-instantiate a singleton with Prism/DryIoC in Xamarin Forms?
I'm working with Azure Mobile Apps for offline data. Occasionally, I need to delete the local sqlite database and re-initialize it. Unfortunately the MobileServiceClient occasionally holds the db connection open and there's no method exposed to close it. The suggested solution (https://github.com/Azure/azure-mobile-apps-net-client/issues/379) is to dispose of MobileServiceClient. Only problem is that is registered with DryIoC as a singleton.
I'm not overly familiar with DryIoC, or Prism and Forms for that matter... But for the life of me, I can't see a way to do this.
I did cook up a pretty elaborate scheme that almost worked.
In my ViewModel method, when I needed the db freed up, I fired off an event -
_eventAggregator.GetEvent<RegisterDatabaseEvent>().Publish(false);
Then in App.xaml.cs, I wired up a listener and a handler like so -
_eventAggregator.GetEvent<RegisterDatabaseEvent>().Subscribe(OnRegisterDatabaseEventPublished);
private void OnRegisterDatabaseEventPublished()
{
Container.GetContainer().Unregister<IAppMobileClient>();
Container.GetContainer().Unregister<IMobileServiceClient>();
Container.GetContainer().Register<IMobileServiceClient, AppMobileClient>(new SingletonReuse());
Container.GetContainer().Register<IAppMobileClient, AppMobileClient>(new SingletonReuse());
_eventAggregator.GetEvent<RegisterDatabaseCompletedEvent>().Publish(register);
}
Lastly, back in the ViewModel constructor, I had a final listener that handled the event coming back from App.xaml and finished processing.
_eventAggregator.GetEvent<RegisterDatabaseCompletedEvent>().Subscribe(OnRegisterDatabaseCompletedEventPublished);
So the amazing thing is that this worked. The database was able to be deleted and all was good. But then I navigated to a different page and BOOM. DryIoC said it couldn't wire up the ViewModel for that page. I assume the unregister/register jacked up DryIoC for all injection... So how can I accomplish what needs to be done?
FINAL SOLUTION
Thanks so much to dadhi for taking the time to help. You are certainly a class act and I'm now considering using DryIoC elsewhere.
For anyone who stumbles on this, I'm posting the final solution below. I'll be as verbose as I can to avoid any confusion.
First, in my App.xaml.cs, I added a method for registering my database.
public void RegisterDatabase(IContainer container)
{
container.RegisterMany<AppMobileClient>(Reuse.Singleton,
setup: Setup.With(asResolutionCall: true),
ifAlreadyRegistered: IfAlreadyRegistered.Replace,
serviceTypeCondition: type =>
type == typeof(IMobileServiceClient) || type == typeof(IAppMobileClient));
}
I simply add a call to that method in RegisterTypes in place of registering the types in there directly.
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.GetContainer().Rules.WithoutEagerCachingSingletonForFasterAccess();
...
RegisterDatabase(containerRegistry.GetContainer());
...
}
Note also the added rule for eager caching, per dadhi.
Later on when I need to release the database in the ViewModel... I kick things off by resetting my local db variable and sending an event to App.xaml.cs
_client = null;
_eventAggregator.GetEvent<RegisterDatabaseEvent>().Publish(true);
In App.xaml.cs, I have subscribed to that event and tied it to the following method.
private void OnRegisterDatabaseEventPublished()
{
RegisterDatabase(Container.GetContainer());
_eventAggregator.GetEvent<RegisterDatabaseCompletedEvent>().Publish(register);
}
Here I just call RegisterMany again, exactly the same as I do when the app starts up. No need to unregister anything. With the setup and ifAlreadyRegistered arguments (thanks, dadhi!), DryIoC allows the object to be replaced. Then I raise an event back to the VM letting it know the database has been released.
Finally, back in the ViewModel, I'm listening for the completed event. The handler for that event updates the local copy of the object like so.
_client = ((PrismApplication)App.Current).Container.Resolve<IAppMobileClient>();
And then I can work with the new object, as needed. This is key. Without setting _client to null above and resolving it again here, I actually ended up with 2 copies of the object and calls to methods were being hit 2x.
Hope that helps someone else looking to release their Azure Mobile Apps database!
I am not sure how exactly XF handles these things.
But in DryIoc in order for service to be fully deleted or replaced it need to be registered with setup: Setup.With(asResolutionCall: true). Read here for more details: https://bitbucket.org/dadhi/dryioc/wiki/UnregisterAndResolutionCache#markdown-header-unregister-and-resolution-cache
Update
Here are two options and considerations that work in pure DryIoc and may not work XF. But it probably may help with solution.
public class Foo
{
public IBar Bar { get; private set; }
public Foo(IBar bar) { Bar = bar; }
}
public interface IBar {}
public class Bar : IBar {}
public class Bar2 : IBar { }
[Test]
public void Replace_singleton_dependency_with_asResolutionCall()
{
var c = new Container(rules => rules.WithoutEagerCachingSingletonForFasterAccess());
c.Register<Foo>();
//c.Register<Foo>(Reuse.Singleton); // !!! If the consumer of replaced dependency is singleton, it won't work
// cause the consumer singleton should be replaced too
c.Register<IBar, Bar>(Reuse.Singleton,
setup: Setup.With(asResolutionCall: true)); // required
var foo = c.Resolve<Foo>();
Assert.IsInstanceOf<Bar>(foo.Bar);
c.Register<IBar, Bar2>(Reuse.Singleton,
setup: Setup.With(asResolutionCall: true), // required
ifAlreadyRegistered: IfAlreadyRegistered.Replace); // required
var foo2 = c.Resolve<Foo>();
Assert.IsInstanceOf<Bar2>(foo2.Bar);
}
[Test]
public void Replace_singleton_dependency_with_UseInstance()
{
var c = new Container();
c.Register<Foo>();
//c.Register<Foo>(Reuse.Singleton); // !!! If the consumer of replaced dependency is singleton, it won't work
// cause the consumer singleton should be replaced too
c.UseInstance<IBar>(new Bar());
var foo = c.Resolve<Foo>();
Assert.IsInstanceOf<Bar>(foo.Bar);
c.UseInstance<IBar>(new Bar2());
var foo2 = c.Resolve<Foo>();
Assert.IsInstanceOf<Bar2>(foo2.Bar);
}

Application_EndRequest triggered before request end?

I have some simple classes that need to be disposed a the end of the request.
For that end I call the Dispose method on those objects from the Application_EndRequest event in Global.asax.
This "works fine on my machine" but causes some problems on my production server where I get Cannot access a disposed object. This happens in some MVC helpers.
It seemed to me like Application_EndRequest is triggered at the end of the request. Is this not the case? Is there another event I should be using to dispose my objects?
Application pool issues - likely
I suspect that your disposable object isn't bound to request but rather app wide (it may be instantiated per request but it may be using some shared resources). As long as you're testing your application in development environment it seems to behave as expected but as soon as you put it in production you get issues. This indicates you may have problems with application pool.
IIS web application pool capabilities actually instantiate several HttpApplication instances for your application and they may all share common disposable resources. If that's the case with your disposable object and you're sharing it it may be that it isn't thread safe. The same would be true when you wouldn't wrap your shared resource usage inside thread safe operations.
That's why it may happen that while one request is in progress another one begins and the first one disposed the object while the second process still uses it.
More information is always helpful
If you'd explain the nature of your disposable object/resource and how you're using it in your application we could help you much better. But in the meantime, you could read my blog post that talks about application pools and handling them. It's not about disposable objects per se, but you may still find all the information very useful and helpful.
If you need some object disposable per-request to use inside your controllers, I would recommend you using controller's lifecycle handlers instead of using Application_BeginRequest and Application_EndRequest. See the following example.
The Controller:
public class BaseController : Controller
{
protected override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
this.HttpContext.Items["MyDisposableObject"] = new MyDisposableObject();
}
protected override void OnResultExecuted(ResultExecutedContext filterContext)
{
base.OnResultExecuted(filterContext);
if (this.HttpContext.Items.Contains("MyDisposableObject"))
{
var myDisposableObject =
this.HttpContext.Items["MyDisposableObject"] as IDisposable;
if (myDisposableObject != null)
{
myDisposableObject.Dispose();
}
}
}
}
The IDisposable object:
public sealed class MyDisposableObject : IDisposable
{
private bool disposed;
public void Dispose()
{
if (!this.disposed)
{
// Dispose all managed
// and unmanaged resources.
// Note disposing has been done.
this.disposed = true;
}
}
}
If the objects are scoped to controller level you can override the Dispose method of Controller to dispose those objects.
protected override void Dispose(bool disposing)
{
if(disposing)
{
// dispose the objects here
}
base.Dispose(disposing);
}
If you are using some DI framework (like Ninject) in your application you can delegate that job to them.
Instead of disposing the objects at the end of the request you can also try wrapping them in an using statement wherever you access by this way you make sure the object is disposed.

Windsor composite lifestyle for asp.net process

I have an asp.net process which also consumes messages from a servicebus (MassTransit). For webrequests my database session is resolved with a PerWebRequest lifestyle.
But when the process consumes a message from MassTransit I need the database session to have another lifestyle, as no HttpContext is available.
I have made this:
public class PerRequestLifeStyleManager : ILifestyleManager
{
readonly PerWebRequestLifestyleManager perWebRequestLifestyleManager;
readonly PerThreadLifestyleManager perThreadLifestyleManager;
public PerRequestLifeStyleManager()
{
perWebRequestLifestyleManager = new PerWebRequestLifestyleManager();
perThreadLifestyleManager = new PerThreadLifestyleManager();
}
public void Init(IComponentActivator componentActivator, IKernel kernel, ComponentModel model)
{
perWebRequestLifestyleManager.Init(componentActivator, kernel, model);
perThreadLifestyleManager.Init(componentActivator, kernel, model);
}
public object Resolve(CreationContext context)
{
return GetManager().Resolve(context);
}
public bool Release(object instance)
{
return GetManager().Release(instance);
}
public void Dispose()
{
GetManager().Dispose();
}
ILifestyleManager GetManager()
{
if (HttpContext.Current != null)
{
return perWebRequestLifestyleManager;
}
return perThreadLifestyleManager;
}
}
Can anyone tell me, if this is the right way to go? And if it isn't, what is?
Thanks.
EDIT: I have just updated the question with some code that seems to work (before it was untested). I still am eager to know if this - seen from a Windsor perspective - is safe and sound.
Try using one of the hybrid lifestyles.
By using the Castle Windsor extension, you should just be able to have your ISession as a dependency on the constructor of the consumer class. That way, the container will manage the lifecycle of the ISession, and dispose of it once the consumer is disposed by MT.
If you need even more control, you can look at how the WindsorConsumerFactory is implemented to wrap the resolution and release of the consumer class instance around the delivery of the message to the consumer.
If you need to inject something beyond that, you can also use an interceptor:
Unit of work when using MassTransit

How to pass unit of work container into constructor of repository using dependency injection

I'm trying to work out how to complete my implementation of the Repository pattern in an ASP.NET web application.
At the moment, I have a repository interface per domain class defining methods for e.g. loading and saving instances of that class.
Each repository interface is implemented by a class which does the NHibernate stuff. Castle Windsor sorts out the DI of the class into the interface according to web.config. An example of an implemented class is provided below:
public class StoredWillRepository : IStoredWillRepository
{
public StoredWill Load(int id)
{
StoredWill storedWill;
using (ISession session = NHibernateSessionFactory.OpenSession())
{
storedWill = session.Load<StoredWill>(id);
NHibernateUtil.Initialize(storedWill);
}
return storedWill;
}
public void Save(StoredWill storedWill)
{
using (ISession session = NHibernateSessionFactory.OpenSession())
{
using (ITransaction transaction = session.BeginTransaction())
{
session.SaveOrUpdate(storedWill);
transaction.Commit();
}
}
}
}
As pointed out in a previous thread, the repository class needs to accept an unit of work container (i.e. ISession) rather than instantiating it in every method.
I anticipate that the unit of work container will be created by each aspx page when needed (for example, in a property).
How do I then specify that this unit of work container instance is to be passed into the constructor of StoredWillRepository when Windsor is creating it for me?
Or is this pattern completely wrong?
Thanks again for your advice.
David
I have a persistence framework built on top of NHibernate that is used in a few Web apps. It hides the NH implementation behind an IRepository and IRepository<T> interface, with the concrete instances provided by Unity (thus I could in theory swap out NHibernate for, say, Entity Framework fairly easily).
Since Unity doesn't (or at least the version I'm using doesn't) support the passing in of constructor parameters other than those that are dependency injections themselves, passing in an extant NH ISession isn't possible; but I do want all objects in the UOW to share the same ISession.
I solve this by having a controlling repository class that manages access to the ISession on a per-thread basis:
public static ISession Session
{
get
{
lock (_lockObject)
{
// if a cached session exists, we'll use it
if (PersistenceFrameworkContext.Current.Items.ContainsKey(SESSION_KEY))
{
return (ISession)PersistenceFrameworkContext.Current.Items[NHibernateRepository.SESSION_KEY];
}
else
{
// must create a new session - note we're not caching the new session here... that's the job of
// BeginUnitOfWork().
return _factory.OpenSession(new NHibernateInterceptor());
}
}
}
}
In this example, PersistenceFrameworkContext.Current.Items accesses an IList<object> that is stored either ThreadStatic if not in a Web context, or within HttpContext.Current.Items if it is in a Web context (to avoid thread-pool problems). The first call to the property instantiates the ISession from the stored factory instance, subsequent calls just retrieve it from storage. The locking will slow things down slightly but not as much as just locking an appdomain-scoped static ISession instance.
I then have BeginUnitOfWork and EndUnitOfWork methods to take care of the UOW - I have specifically disallowed nested UOWs because frankly they were a pain to manage.
public void BeginUnitOfWork()
{
lock (_lockObject)
{
if (PersistenceFrameworkContext.Current.Items.ContainsKey(SESSION_KEY))
EndUnitOfWork();
ISession session = Session;
PersistenceFrameworkContext.Current.Items.Add(SESSION_KEY, session);
}
}
public void EndUnitOfWork()
{
lock (_lockObject)
{
if (PersistenceFrameworkContext.Current.Items.ContainsKey(SESSION_KEY))
{
ISession session = (ISession)PersistenceFrameworkContext.Current.Items[SESSION_KEY];
PersistenceFrameworkContext.Current.Items.Remove(SESSION_KEY);
session.Flush();
session.Dispose();
}
}
}
Finally, a pair of methods provide access to the domain-type-specific repositories:
public IRepository<T> For<T>()
where T : PersistentObject<T>
{
return Container.Resolve<IRepository<T>>();
}
public TRepository For<T, TRepository>()
where T : PersistentObject<T>
where TRepository : IRepository<T>
{
return Container.Resolve<TRepository>();
}
(Here, PersistentObject<T> is a base class providing ID and Equals support.)
Access to a given repository is thus in the pattern
NHibernateRepository.For<MyDomainType>().Save();
This is then facaded over such that you can use
MyDomainType.Repository.Save();
Where a given type has a specialised repository (ie needs more than it can get from IRepository<T>) then I create an interface deriving from IRepository<T>, an extending implementation inheriting from my IRepository<T> implementation, and in the domain type itself I override the static Repository property using new
new public static IUserRepository Repository
{
get
{
return MyApplication.Repository.For<User, IUserRepository>();
}
}
(MyApplication [which is called something less noddy in the real product] is a facade class which takes care of supplying the Repository instance via Unity so you have no dependency on the specific NHibernate repository implementation within your domain classes.)
This gives me full pluggability via Unity for the repository implementation, easy access to the repository in code without jumping through hoops, and transparent, per-thread ISession management.
There's lots more code than just what's above (and I've simplified the example code a great deal), but you get the general idea.
MyApplication.Repository.BeginUnitOfWork();
User user = User.Repository.FindByEmail("wibble#wobble.com");
user.FirstName = "Joe"; // change something
user.LastName = "Bloggs";
// you *can* call User.Repository.Save(user), but you don't need to, because...
MyApplication.Repository.EndUnitOfWork();
// ...causes session flush which saves the changes automatically
In my Web app, I have session-per-request, so BeginUnitOfWork and EndUnitOfWork get called in BeginRequest and EndRequest respectively.
I have a pretty similar structure to yours, and here's how I solve your question:
1) To specify my container on each method, I have a separate class ("SessionManager") which I then invoke via a static property. By doing so, here's an example using my Save implementation:
private static ISession NHibernateSession
{
get { return SessionManager.Instance.GetSession(); }
}
public T Save(T entity)
{
using (var transaction = NHibernateSession.BeginTransaction())
{
ValidateEntityValues(entity);
NHibernateSession.Save(entity);
transaction.Commit();
}
return entity;
}
2) My container is not created on each ASPX page. I instantiate all of my NHibernate goodness on the global.asax page.
** A few more things spring up **
3) You don't need to have a helper to instantiate the Load. You might as well use Get instead of Load. More information # Difference between Load and Get.
4) Using your current code, you would have to repeat pretty much the same code for each domain object you need (StoredWillRepository, PersonRepository, CategoryRepository, etc..?), which seems like a drag. You could very well use a generic class to operate over NHibernate, like:
public class Dao<T> : IDao<T>
{
public T SaveOrUpdate(T entity)
{
using (var transaction = NHibernateSession.BeginTransaction())
{
NHibernateSession.SaveOrUpdate(entity);
transaction.Commit();
}
return entity;
}
}
In my implementation, I could then use something like:
Service<StoredWill>.Instance.SaveOrUpdate(will);
Technically, the answer to my question is to use the overload of container.Resolve which allows you to specify the constructor argument as an anonymous type:
IUnitOfWork unitOfWork = [Code to get unit of work];
_storedWillRepository = container.Resolve<IStoredWillRepository>(new { unitOfWork = unitOfWork });
But let's face it, the answers provided by everyone else have been much more informative.

Injecting Lower Layer Dependency in Presenter in an ASP.NET MVP Application

I recently read Phil Haack's post where he gives an example of implementing Model View Presenter for ASP.NET. One of the code snippets shows how the code for the view class.
public partial class _Default : System.Web.UI.Page, IPostEditView
{
PostEditController controller;
public _Default()
{
this.controller = new PostEditController(this, new BlogDataService());
}
}
However, here the view constructs the instance of the BlogDataService and passes it along to the presenter. Ideally the view should not know about BlogDataService or any of the presenter's lower layer dependencies. But i also prefer to keep the BlogDataService as a constructor injected dependency of the presenter as it makes the dependencies of the presenter explicit.
This same question has been asked on stackoverflow here.
One of the answers suggests using a service locator to get the instance of the BlogDataService and passing it along to the presenter's constructor.This solution however does not solve the problem of the view knowing about the BlogDataService and needing to explicitly get a reference to it.
Is there a way to automatically construct the presenter object using an IoC or DI container tool such that the view does not have to deal with explicitly creating the BlogDataService object and also injecting the view and service instances into the presenter's constructor. I prefer to use the constructor injection pattern as far as possible.
Or is there a better design available to solve the problem?. Can there be a better way to implement this If i am building a WinForms application instead of a ASP.NET WebForms application?
Thanks for any feedback.
Yes there is. For example using StructureMap in a webform constructor:
public partial class AttributeDetails : EntityDetailView<AttributeDetailPresenter>, IAttributeDetailView
{
public AttributeDetails()
{
_presenter = ObjectFactory.With<IAttributeDetailView>(this).GetInstance<AttributeDetailPresenter>();
}
....
}
and as you can see here presenter needs view and service injected
public AttributeDetailPresenter(IAttributeDetailView view, IAttributeService attributeService)
{
MyForm = view;
AppService = attributeService;
}
You can also use StructureMap BuildUp feature for webforms so that you can avoid using ObjectFactory directly in your view.
I did exactly this. The solution is based on Autofac, but can be implemented on top of any container.
First, define an interface representing the authority for presenting views in a request to the MVP system:
public interface IMvpRequest
{
void Present(object view);
}
Next, create a base page which has a property of that type:
public abstract class PageView : Page
{
public IMvpRequest MvpRequest { get; set; }
}
At this point, set up dependency injection for pages. Most containers have ASP.NET integration, usually in the form of HTTP modules. Because we don't create the page instance, we can't use constructor injection, and have to use property injection here only.
After that is set up, create event arguments representing a view which is ready to be presented:
public class PresentableEventArgs : EventArgs
{}
Now, catch the events in PageView and pass them to the request (present the page as well):
protected override bool OnBubbleEvent(object source, EventArgs args)
{
var cancel = false;
if(args is PresentableEventArgs)
{
cancel = true;
Present(source);
}
else
{
cancel = base.OnBubbleEvent(source, args);
}
return cancel;
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
Present(this);
}
private void Present(object view)
{
if(MvpRequest != null && view != null)
{
MvpRequest.Present(view);
}
}
Finally, create base classes for each type of control you'd like to serve as a view (master pages, composite controls, etc.):
public abstract class UserControlView : UserControl
{
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
EnsureChildControls();
RaiseBubbleEvent(this, new PresentableEventArgs());
}
}
This connects the control tree to the MVP system via IMvpRequest, which you'll now have to implement and register in the application-level container. The ASP.NET integration should take care of injecting the implementation into the page. This decouples the page entirely from presenter creation, relying on IMvpRequest to do the mapping.
The implementation of IMvpRequest will be container-specific. Presenters will be registered in the container like other types, meaning their constructors will automatically be resolved.
You will have some sort of map from view types to presenter types:
public interface IPresenterMap
{
Type GetPresenterType(Type viewType);
}
These are the types you will resolve from the container.
(The one gotcha here is that the view already exists, meaning the container doesn't create the instance or ever know about it. You will have to pass it in as a resolution parameter, another concept supported by most containers.)
A decent default mapping might look like this:
[Presenter(typeof(LogOnPresenter))]
public class LogOnPage : PageView, ILogOnView
{
// ...
}

Resources