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

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
{
// ...
}

Related

How do you inject a view dependency into Presenter (MVP) in ASP.NET Web Forms using Simple Injector or Microsoft.Extensions.Dependency?

The following example shows a scenario where I'm trying to implement a DI container. In this case, I'm trying to use Simple Injector or Microsoft.Extensions.DependencyInjection DI Container. I've seen code examples that start hitting around the target, such as here, but no bullseye as of yet.
Below is a general code sample that I would like to modify to use one of the aforementioned DI containers (Used Simple Injector for example). I could move the view out of the presenter constructor and set it as a property. However, I was hoping for a more eloquent solution also it is a dependency that needs to be injected.
I know .NET 4.7.2 has increased DI support functionality but the biggest benefit seems to be allowing dependencies to be easily injected into pages/user controls. For MVP architecture I need the concrete class of the page tied to its view interface so the DI container can resolve and pass into the presenter, as the presenter depends on the view. I've not seen an example of this implemented well other than Unity using its DependencyOverride, which can pass the concrete class at runtime.
public partial class UserLoginView : IUserLoginView
{
private UserLoginPresenter _userLoginPresenter;
protected override void OnLoad(EventArgs e)
{
//This is my problem:
//An error will be thrown "...contains the parameter with name
//'view' and type IUserLoginView, but IUserLoginView is not
//registered..."
_userLoginPresenter = SimpleInjectorDependencyInjector
.GetInstance<IDeveloperTestStatusPresenter>();
}
}
public class UserLoginPresenter : IUserLoginPresenter
{
private readonly IUserLoginView view;
private readonly IUserService _userService;
public UserLoginPresenter(IUserLoginView userLoginView,
IUserService userService)
{
this.view = userLoginView;
this._userService = userService;
}
public static class SimpleInjectorDependencyInjector
{
private static readonly Container container = new Container();
public static T GetInstance<T>() where T : class
{
return container.GetInstance<T>();
}
//Assume this is called from App on start
public static void RegisterClasses()
{
container
.Register<IUserLoginPresenter, UserLoginPresenter>();
container
.Register<IUserService, UserService>();
}
}
I was able to accomplish what I was looking for using Microsoft.Extensions.DependencyInjection Container.
In my MSDependencyInjector wrapper class, I used the ActivatorUtilities extension.
public static T GetService<T, I>(I interfaceInstance)
{
return ActivatorUtilities.CreateInstance<T>(container, interfaceInstance);
}
Implemented in my page's partial class I wrote:
_userLoginPresenter = MSDependencyInjector.GetService<UserLoginPresenter,
IUserLoginView>(this);
A Caveat: The 'T' parameter of createInstance wants the concrete class type not the interface. This caused hours of frustration, prompting the creation of this question in the first place. MS documentation isn't the greatest but I definitely misread.
I'm not sure how to implement something as straightforward in Simple Injector and would be interested in knowing. Based on my reading I not sure if my solution isn't something like a service locator, which depending on which camp you are from should be avoided. However, if the implementation of this can be contained for just solving the need for this MVP paradigm then it is my hope all will be well.

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

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.

How to test asp.net server controls

We have developed a number of ASP.Net server controls and we need to test them. I want to instantiate a control, set some properties, call CreateChildControls and test the control-hierarchy.
I run into a number of problems:
The controls rely on HttpContext
CreateChildControls is private
Even adding a single child control to the controls collection calls the ResolveAdapter() method which relies on HttpContext.
How can I get around this?
p.s. I do not wish to test the controls on a page (!).
It sounds a lot like you don't care about the actual rendering of the control at all, but rather the logic contained within the control. For that I would suggest that you have another problem besides the inability to test the control outside the HttpContext.
If the logic only pertains to the control, then you should trust the framework to do it's job, and drop the control on a page to see if it works properly. If the logic you are attempting to test is business logic, then you need to refactor.
Pull out the business logic into a seperate Project/Dll somewhere, and think about implementing a MVP pattern with your server control. You don't have to go with a big heavy framework like WCSF either. Conceptually you can implement this with little effort.
Create an interface that represents the values on your view:
public interface IOrderView
{
Int32 ID{get; set;}
String Name{get; set;}
List<Item> Items {set;}
}
Once this is defined, you need a presenter that exercises this view:
public class OrderPresenter
{
public IOrderView View {get; set;}
public void InitializeView()
{
//Stuff that only happens when the page loads the first time
//This is only for an example:
var order = Orders.GetOrder(custId);
View.ID = order.ID;
View.Name = order.Name;
View.Items = order.Items;
}
public void LoadView()
{
//Stuff that happens every page load
}
}
Now your server control can implement this interface, and initialize itself with the OrderPresenter
public class OrderControl: Panel, IOrderView
{
private OrderPresenter Presenter{get; set;}
public OrderControl()
{
//Create new presenter and initialize View with reference
// to ourselves
Presenter = new OrderPresenter{View = this;}
}
protected override void OnLoad(EventArgs e)
{
if(Page.IsPostback)
{
_presenter.InitializeView();
}
_presenter.LoadView();
//Other normal onload stuff here...
}
//Now for the interface stuff
public Int32 ID
{
get{ return Int32.Parse(lblOrderId.Text); }
set{ lblOrderId.Text = value.ToString(); }
}
public String Name
{
get{ return lblOrderName.Text; }
set{ lblOrderName.Text = value; }
}
public List<Item> Items
{
set
{
gvItems.DataSource = value;
gvItems.DataBind();
}
}
}
And there you have it! You should be able to write unit tests against the OrderPresenter now using a stubbed out View. No HttpContext required, and you have cleaner seperation of concerns.
If you already have all your business logic seperated out then I appologize, but I can't think of any other reason to test a server control outside the ASP.Net runtime besides needing to verify actual business logic. If this is the case, then I would highly encourage you to refactor now before you realize the maintenance nightmare this will eventually cause via Leaky Abstractions.

How to use Dependency Injection with ASP.NET Web Forms

I am trying to work out a way to use dependency injection with ASP.NET Web Forms controls.
I have got lots of controls that create repositories directly, and use those to access and bind to data etc.
I am looking for a pattern where I can pass repositories to the controls externally (IoC), so my controls remain unaware of how repositories are constructed and where they come from etc.
I would prefer not to have a dependency on the IoC container from my controls, therefore I just want to be able to construct the controls with constructor or property injection.
(And just to complicate things, these controls are being constructed and placed on the page by a CMS at runtime!)
Any thoughts?
UPDATE 2019:
With the introduction of Web Forms 4.7.2, there is now better support for DI. This invalidates the below. See: Wiring up Simple Injector in WebForms in .NET 4.7.2
You can use automatic constructor injection by replacing the default PageHandlerFactory with a custom one. This way you can use an overloaded constructor to load the dependencies. Your page might look like this:
public partial class HomePage : System.Web.UI.Page
{
private readonly IDependency dependency;
public HomePage(IDependency dependency)
{
this.dependency = dependency;
}
// Do note this protected ctor. You need it for this to work.
protected HomePage () { }
}
Configuring that custom PageHandlerFactory can be done in the web.config as follows:
<?xml version="1.0"?>
<configuration>
<system.web>
<httpHandlers>
<add verb="*" path="*.aspx"
type="YourApp.CustomPageHandlerFactory, YourApp"/>
</httpHandlers>
</system.web>
</configuration>
Your CustomPageHandlerFactory can look like this:
public class CustomPageHandlerFactory : PageHandlerFactory
{
private static object GetInstance(Type type)
{
// TODO: Get instance using your favorite DI library.
// for instance using the Common Service Locator:
return Microsoft.Practices.ServiceLocation
.ServiceLocator.Current.GetInstance(type);
}
public override IHttpHandler GetHandler(HttpContext cxt,
string type, string vPath, string path)
{
var page = base.GetHandler(cxt, type, vPath, path);
if (page != null)
{
// Magic happens here ;-)
InjectDependencies(page);
}
return page;
}
private static void InjectDependencies(object page)
{
Type pageType = page.GetType().BaseType;
var ctor = GetInjectableCtor(pageType);
if (ctor != null)
{
object[] arguments = (
from parameter in ctor.GetParameters()
select GetInstance(parameter.ParameterType)
.ToArray();
ctor.Invoke(page, arguments);
}
}
private static ConstructorInfo GetInjectableCtor(
Type type)
{
var overloadedPublicConstructors = (
from constructor in type.GetConstructors()
where constructor.GetParameters().Length > 0
select constructor).ToArray();
if (overloadedPublicConstructors.Length == 0)
{
return null;
}
if (overloadedPublicConstructors.Length == 1)
{
return overloadedPublicConstructors[0];
}
throw new Exception(string.Format(
"The type {0} has multiple public " +
"ctors and can't be initialized.", type));
}
}
Downside is that this only works when running your side in Full Trust. You can read more about it here. But do note that developing ASP.NET applications in partial trust seems a lost cause.
Starting from .NET 4.7.2 (what's new), it is now easy for developers to use Dependency Injection in WebForms applications. With the UnityAdapter, you can add it to your existing WebForms application in 4 simple steps. See this blog.
Autofac supports fairly unobtrusive dependency injection in ASP.NET WebForms. My understanding is it just hooks into the ASP.NET page lifecycle using an http module and does property injection. The only catch is that for controls I don't think this happens until after the Init event.
The best way is to have a base class for the controls like:
public class PartialView : UserControl
{
protected override void OnInit(System.EventArgs e)
{
ObjectFactory.BuildUp(this);
base.OnInit(e);
}
}
That will inject any control that inherits from that base class (uses structuremap). Combining that with a property based config, you will be able to have controls like:
public partial class AdminHeader : PartialView
{
IMyRepository Repository{get;set;}
}
Update 1: If you can't have the controls inherit, perhaps the CMS has a hook right after creating the controls, in there you can call the BuildUp. Also if the CMS allows you to hook something to fetch the instance you could use constructor based injection, but I prefer BuildUp on this specific scenario as asp.net doesn't have a hook for this.
You could also create some singleton instances in the Application_Start global.asax event and have them available as public static readonly properties.
This is a solution I recently used to avoid hooking into the pipeline (I find that confuses everyone that looks at my code in the future, but yes, I see its benefits as well):
public static class TemplateControlExtensions
{
static readonly PerRequestObjectManager perRequestObjectManager = new PerRequestObjectManager();
private static WIIIPDataContext GetDataContext(this TemplateControl templateControl)
{
var dataContext = (WIIIPDataContext) perRequestObjectManager.GetValue("DataContext");
if (dataContext == null)
{
dataContext = new WIIIPDataContext();
perRequestObjectManager.SetValue("DataContext", dataContext);
}
return dataContext;
}
public static IMailer GetMailer(this TemplateControl templateControl)
{
return (IMailer)IoC.Container.Resolve(typeof(IMailer));
}
public static T Query<T>(this TemplateControl templateControl, Query<T> query)
{
query.DataContext = GetDataContext(templateControl);
return query.GetQuery();
}
public static void ExecuteCommand(this TemplateControl templateControl, Command command)
{
command.DataContext = GetDataContext(templateControl);
command.Execute();
}
private class PerRequestObjectManager
{
public object GetValue(string key)
{
if (HttpContext.Current != null && HttpContext.Current.Items.Contains(key))
return HttpContext.Current.Items[key];
else
return null;
}
public void SetValue(string key, object newValue)
{
if (HttpContext.Current != null)
HttpContext.Current.Items[key] = newValue;
}
}
}
This shows how you can create your own life time manager pretty easily as well as hook into an IoC container if you so desire. Oh, and I am also using a query/command structure which is sort of unrelated, but more on the reasoning behind that can be found here:
Limit your abstractions: Refactoring toward reduced abstractions

Using Autofac with ASP.NET and the MVP pattern

I'm trying to integrate Autofac into an exsisting ASP.NET web application.
The pages follow the MVP pattern. Each page implements a View and delegate functionality to a Presenter. The View is injected into the Presenter thru the constructor.
I was able to register the Presenter and View and the page loads fine but when a postback happens the user controls on the view are null. It seems that Autofac creates a new instance of the Page to give to the presenter instead of giving it the instance real Page instance. Is there a way to have Page instances registered with Autofac?
Has anyone use Autofac with ASP.NET and MVP?
Thanks!
There is a better way. First, enable the Web integration module. This will enable automatic property injection into the Page instance.
Since your presenter needs the view in its constructor, your page should take a dependency on a presenter factory instead of the presenter itself.
So, first you need the presenter factory, which is a delegate with the necessary parameters:
public delegate IOCTestPresenter IOCTestPresenterFactory(IIOCTestView view);
This delegate must match the parameters (type and name) of the presenter constructor:
public class IOCTestPresenter
{
public IOCTestPresenter(IIOCTestView view)
{
}
}
In your view, add a property receiving the factory delegate, and use the delegate to create the presenter:
public partial class IOCTest
{
public IOCTestPresenterFactory PresenterFactory {get;set;}
protected void Page_Load(object sender, EventArgs e)
{
var presenter = PresenterFactory(this);
}
}
In your container setup you will have to make the following registrations:
builder.Register<IOCTestPresenter>().FactoryScoped();
builder.RegisterGeneratedFactory<IOCTestPresenterFactory>();
I figured out a solution. Basically, you would register the page instance during the Page_PreInit event and then call the container to inject the dependencies. Ex.
public partial class IOCTest : System.Web.UI.Page, IIOCTestView
{
protected void Page_PreInit(object sender, EventArgs e)
{
var containerProviderAccessor = (IContainerProviderAccessor)HttpContext.Current.ApplicationInstance;
var containerProvider = containerProviderAccessor.ContainerProvider;
var builder = new ContainerBuilder();
builder.Register(this).ExternallyOwned().As<IIOCTestView>();
builder.Build(containerProvider.RequestContainer);
containerProvider.RequestContainer.InjectProperties(this);
}
public IOCTestPresenter Presenter { get; set; }
I'm not sure if there is a better way, but this seems to work.

Resources