how can i inject a presenter into a view without the view having a reference to the presenter - asp.net

in a classic Passive-MVP pattern, how can i avoid a reference of the presenter in my view completely & still inject the presenter instance which needs the view instance as a parameter.
with asp.net as an example:
my implemented views (web project) should not have a reference to the Presenters. (Neither IPresenter nor the concrete ones)
when the view instantiates, (basically my web page), the presenter should be instantiated with the current view's reference.
i am using unity as my ioc container.
right now what i do in the web page's code behind is this:
public partial class SomePage : MyBasePage, ISomeView
{
private readonly ISomePresenter presenter;
public SomePage()
{
this.presenter = ResolveSomeWay(this);
}
}
for this i have a reference of the 'Presenter Contracts DLL' in my view implementation. is there a way to avoid this reference completely & still hook up the presenter with the view instance, when the view instantiates?
i just care about the presenter instantiation, since the presenter's constructor can set the passed parameter-view-instance to its View Property & it subscribes to the view's events, for any future communication.
thanks folks for your time.

You could "publish" a new View was instantiated to a Message Bus, to which a Presenter factory could "bind" the instantiated View to a Presenter. Although the View would be agnostic of the presenter, it would not be of the Message Bus.
public partial class SomePage : MyBasePage, ISomeView
{
// Alternative #1
public SomePage(IMessageBus messageBus)
{
// You publish a message saying that a handler for ISomeView is to handle the
// message.
messageBus.Publish<ISomeView>(this);
}
// Alternative #2
public SomePage()
{
Resolver.Resolve<IMessageBus>().Publish<ISomeView>(this);
}
}
// This could be somewhere else in your application (this could be a separate DLL), but
// I will use the Global.asax.cs here, for simplicity
public void Application_Start()
{
Container.Resolve<IMessageBus>()
.Subscribe<ISomeView>(someView =>
{
var presenter = new SomePresenter(someView);
});
}
public interface ISomeView {
event Action<ISomeView> SomeEvent;
}
public class SomePresenter
{
public SomePresenter(ISomeView view) {
// if you attach the presenter to the View event,
// the Garbage Collector won't finalize the Presenter until
// the View is finalized too
view.SomeEvent += SomeEventHandler;
}
private void SomeEventHandler(ISomeView view) {
// handle the event
}
}

Related

open new window and pass collection to viewmodel using mvvm

I have a view which consists of a button when i click i want to open window in which i want to pass observable collection to viewmodel of the new window open.I am using below code it is working but i am not sure it is mvvm pattern or not.
ViewCode:
NewWindow newWindowDialog;
newWindowDialog = new NewWindow()
{
newWindowDialogCollection = suppliersList,
Owner = Application.Current.MainWindow
};
newWindowDialog.ShowDialog();
NewWindow Dialog Code:
public partial class NewWindow : Window
{
public NewWindow()
{
InitializeComponent();
newWindowDialogCollection = new ObservableCollection<SModel>();
DataContext = this;
}
public ObservableCollection<ISupplierModel> newWindowDialogCollection { get; set; }
}
In xaml "newWindowDialogCollection" act as my data source for binding
But i am not sure above way is right way to impliment to open new window and pass collection. and i want to do it by pure mvvm & viewmodel.
Please let me know your thoughts
Creating a new window in a viewmodel couples the View and the ViewModel layers tightly, which defeats the purpose of MVVM.
Also, you shouldn't have business data in the View, but in a ViewModel instead, even with dialogs.
What you'll see some MVVM frameworks do is implementing service classes for everything that is not (easily) manageable by either the Model, the View or the ViewModel. Displaying dialogs is one of those things.
I'll use the example of Catel to demonstrate.
Catel offers a IUIVisualizerService interface, that you can inject in the constructor of your viewmodel:
public MyViewModel(IUIVisualizerService visualizerService)
{
this._visualizerService = visualizerService;
}
And to open a new dialog, since Catel internally matches views and viewmodels, you simple create the dialog's viewmodel and resolve the appropriate view. This way, you can pass you data to the viewmodel's constructor as you please:
var viewModel = new MyViewModel(suppliersList);
_visualizerService.Show(viewModel);
You could create a window service that is responsible for open the window and then inject your view model with such a service. You then call the service's ShowWindow method to open the window from your view model, e.g.:
Service:
public interface IWindowService
{
void ShowWindow(ObservableCollection<string> collection);
}
public class WindowService : IWindowService
{
public void ShowWindow(ObservableCollection<string> collection);
{
NewWindow newWindowDialog = new NewWindow()
{
newWindowDialogCollection = collection,
Owner = Application.Current.MainWindow
};
newWindowDialog.ShowDialog();
}
}
View Model:
public class ViewModel
{
private readonly IWindowService _service;
public ViewModel(IWindowService service)
{
_service = service;
}
//...
public void OpenCommandExecuted()
{
_service.ShowWindow(_theCollectionToPass);
}
}
Using this approach the view model only knows about an interface that you can easily unit test, without actually opening up a window, by providing a mock implementation of the interface.

How to share a common object in each page request of a ASP.net MVC 4 webapp?

I come from "regular" asp.net so i'm a bit (totally) lost with MVC.
What I was doing with my own asp.net programmation pattern :
I have one custom class objet which represent the "page" and its properties (like mypage.loadJquery, mypage.isLogged, mypage.Title, custom cache logic, etc.)
This class is instanciate once on top of each ASHX page, I then manipulate a stringbuilder to produce HTML and spit it right at the browser at the end.
Having only one request on the ASHX page, i can use my page object instanciated at the top till the end when calling final response.write
Now i'm trying to go for MVC. I "kind of" understood the M/V/C model and the routing concept. I would like to keep my custom "page" object but I lost my page life cycle and I definitely don't know how to instanciate my page object ONCE in at the top of every call.
I need this instanciated ONCE shared object across every models, controllers, views, partial views, htmlhelper...
I realize MVC pattern might be confusing for me at this moment, bu how could I try to reproduce my need ?
(Very concrete exemple : On every request i need to check if the user is logged via his cookies. If it is I round trip the database to get user infos. Then I DO NEED THESE INFOS ON PRATICALLY EVERY model / controller / view of the app, but of course don't want to round back each time to security check and database querying, how can i have these info on the whole mvc cyle ?)
In my project I create interface IViewModel that contains all fields that I need in my layout/masterpage and set is as model of it so I can easily use them:
IViewModel.cs
public interface IViewModel
{
string Title { get; set; }
User User { get; set; }
}
Layout.cshtml
#model IViewModel
<html>
<head>
<title>#Model.Title</title>
</head>
<body>
#if (Model.User.IsAuthenticated) {
You are logged as #Model.User.Name
}
</body>
</html>
All my models implement that interface (or inherit from ViewModelBase that is default implementation of that class). Additionally I have custom action filter that check if returned ActionResult is (Partial)ViewResult and if Model of it implement my IViewModel interface and fill data in that interface.
public FillViewModelAttribute : ActionFilterAttribute
{
public override OnActionExecuted(ActionExecutedContext context)
{
var viewResult = context.Result as ViewResult;
if (viewResult != null && viewResult.Model is IViewModel)
{
var viewModel = (IViewModel)viewResult.Model;
// fill data
}
}
}
I created many projects like this. Basically, you can create a base controller class where all the other controllers inherit from it.
[Authorize]
public class BaseController : Controller
{
private Instructor _G_USER = null;
protected Instructor G_USER
{
get
{
if (_G_USER == null)
{
_G_USER = Your DB query here
ViewData["G_USER"] = _G_USER;
}
return _G_USER;
}
}
}
Then in your every child class, you can do
[Authorize]
public class YourController : BaseController
{
public ActionResult Index()
{
if(!G_USER.CAN_DO_THIS) throw new NoPermissionException();
return View();
}
}
To use the User in the view, create an extension method.
public static class ExtentionMethods
{
public static USER G_USER(this ViewPage page)
{
return (USER)page.ViewData["G_USER"];
}
}
Then use in the page like this
<%=this.G_USER().....%>

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

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.

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