Popup messagebox in ViewModel

Very interesting topic, Jeremy’s IMessageBoxService idea is very easy to handle, unit-test. The pain is have to inject this IMessageBox to every class needed.

Glenn Block’s suggestion is using event handler, code can be a little bit cleanner. Test is a little bit harder.


[Test]
 public void should_validate_new_mail_role_and_raise_show_message_event()
 {
    var subscriber = MockRepository.GenerateMock<IEventSubscriber>();
     _emailRoleMaintViewModel.ShowMessageEvent
     += new System.EventHandler<EventArgs<string>>(subscriber.Handler);

     subscriber.Expect(x=>x.Handler( _emailRoleMaintViewModel, new EventArgs<string>("") ))
     .IgnoreArguments();

     _emailRoleMaintViewModel.AddEmailRole("");

     subscriber.VerifyAllExpectations();

 }

More difficult problem is to get the confirmation from view, Glenn’s solution is ‘to raise a separate event to indicate selection which the VM subscribes to‘. Too much overkill for a simple messagebox.

Dong Scott’ s post about CancellableCommand is another option, ViewModel only focus on success logic, but give options to view to ‘cancel’ the action. The comfirmation rending and analyzing code won’t be testable then, but who cares? It’s just a messagebox.


public class CancellableCommand : RelayCommand
{

   public void Execute(object parameter)
   {
   var eventHandler = Executing;
   CancelEventArgs cancelEventArgs = new CancelEventArgs(true);
   if (eventHandler != null)
   {
     eventHandler(parameter, cancelEventArgs);
   }

   if (cancelEventArgs.Cancel) return;

    _execute(parameter);
   }

    public event CancelEventHandler Executing;

}

[Test]
 public void should_stop_if_view_cancel()
 {
 var subscriber = MockRepository.GenerateMock<IEventSubscriber<CancelEventArgs>>();
 _emailRoleMaintViewModel.DeleteCommandObject.Executing +=
 new CancelEventHandler(subscriber.Handler);

 subscriber.Expect(x => x.Handler(_emailRoleMaintViewModel, new CancelEventArgs(true)))
 .IgnoreArguments();

 _emailRoleMaintViewModel.DeleteCommandObject.Execute("");

 subscriber.VerifyAllExpectations();
 _mockLookupProxy.AssertWasNotCalled(x => x.DeleteEmailRole(null), opt => opt.IgnoreArguments());

 }

 [Test]
 public void should_delete_if_view_did_not_cancel()
 {
 var subscriber = MockRepository.GenerateMock<IEventSubscriber<CancelEventArgs>>();
 _emailRoleMaintViewModel.DeleteCommandObject.Executing +=
 new CancelEventHandler(subscriber.Handler);

 subscriber.Expect(x => x.Handler(_emailRoleMaintViewModel, new CancelEventArgs(true)))
 .IgnoreArguments()
 .Do(new EventHandler<CancelEventArgs>(
 (o, e) => { e.Cancel = false; }
 ));

 _emailRoleMaintViewModel.DeleteCommandObject.Execute("");

 subscriber.VerifyAllExpectations();
 _mockLookupProxy.AssertWasCalled(x => x.DeleteEmailRole(null), opt => opt.IgnoreArguments());
 }

    public interface IEventSubscriber<TEventArgs> where TEventArgs : EventArgs
    {
        void Handler(object sender, TEventArgs e);
    }

Advertisements

Should always ignoreArgument when AssertWasCalled

I should have added this option in my test earlier, then I won’t waste 2+ hours today. The reason causing problem was very simple, AssertWasCalled() method in current verion RhinoMocks (3.5.0.1337 RC) doesn’t action consistently. I think it’s because comparing the object reference equalty. I had to add options=>options.IgnoreArguments() to get around from this.

Here is a piece of my MVP + CLSA test. Struggled for a while with Property setter, thanks God, it isn’t that hard.


    [TestFixture]
    public class When_initializing_view : EstablishContext
    {
        [Test]
        public void should_call_view_setter_if_id_exists()
        {
            // Arrange
            _mockOrganizationRepository.Expect(x => x.FindById(1)).Return(_stubOrganizationDTO);

            _mockView = MockRepository.GenerateMock<IEditDetailsView<OrganizationBO>>();

            _presenter = new EditDetailsPresenter<OrganizationBO>(_mockView);

            // Action
            _presenter.InitViewFor(1);

            // Assert
            _mockView.AssertWasCalled(x => x.CurrentBO = null,
                options =>options.IgnoreArguments() );
        }
    }

I then created a generic EditDetailsPresenter, worked as I expected with one concern left: How to mock DataPortal directly. Currently I am still mocking repository. Ideally I should mock DataPortal instead, otherwise I have to make sure there is at least one BO is fully functional.

Mocking DataPortal is very hard, because those Fecth, Create, Update and Delete are all static methods, I had to create an adapter to wrap those static methods, then using StructreMap again inside adapter enable switching between DataPortal and mock DomainDAO, here is what I have tried:

  1. Create DomainDAO and IDomainDAO,
    public class DomainDAO<BO> : IDomainDAO<BO>
    {
    public BO Fetch(object criteria)
    {
    return DataPortal.Fetch<BO>(criteria);
    }
    
    public BO Create()
    {
    return DataPortal.Create<BO>();
    }
    
    public BO Update(BO bo)
    {
    return DataPortal.Update<BO>(bo);
    }
    
    public void Delete(object criteria)
    {
    DataPortal.Delete<BO>(criteria);
    }
    }
    
    public interface IDomainDAO<T>
    {
    T Fetch(object criteria);
    T Create();
    T Update(T bo);
    void Delete(object criteria);
    }
    
    
  2. Create DAOManager to Get DomainDAO:
    public static class DAOManager
    {
    public static IDomainDAO<BO> DomainDAO<BO>()
    {
    return ObjectFactory.GetInstance<IDomainDAO<BO>>();
    }
    }
    
    
  3. Change classic factory method in BO from DataPortal to this new DAOManager.
    public static OrganizationBO GetOrganizationBO(int stakeholderId)
    {
    return DAOManager.DomainDAO<OrganizationBO>().Fetch(new SingleCriteria<OrganizationBO, int>(stakeholderId));
    //            return DataPortal.Fetch<OrganizationBO>(new SingleCriteria<OrganizationBO, int>(stakeholderId));
    }
    
  4. The presenter testing code can be changed to:
    
    [SetUp]
    public void SetUp()
    {
    _mockService = MockRepository.GenerateMock<IDomainDAO<OrganizationBO>>();
    ObjectFactory.Inject<IDomainDAO<OrganizationBO>>(_mockService);
    }
    
    [TearDown]
    public void TearDown()
    {
    ObjectFactory.ResetDefaults();
    }
    
    [TestFixture]
    public class When_initializing_view : EstablishContext
    {
            [Test]
            public void should_call_view_setter_if_id_exists()
            {
                // Arrange
                // We don't need to set return value here.
                _mockService.Expect(x => x.Fetch(new SingleCriteria<OrganizationBO, int>(68190)));
    
                _mockView = MockRepository.GenerateMock<IEditDetailsView<OrganizationBO>>();
    
                _presenter = new EditDetailsPresenter<OrganizationBO>(_mockView);
    
                // Action
                _presenter.InitViewFor(68190);
    
                // Assert
                _mockView.AssertWasCalled(x => x.CurrentBO = null,
                    options => options.IgnoreArguments() );
            }
    
            [Test]
            public void should_set_error_msg_in_view_if_id_not_exists()
            {
                // Arrange
                Exception ex = new Exception("Data Not Found", new DataNotFoundException());
                _mockService.Expect(x => x.Fetch(new SingleCriteria<OrganizationBO, int>(1)))
                    .IgnoreArguments()
                    .Throw(new DataPortalException("DataPortal.Fetch failed.",
                        ex,
                        null)); // See, we don't have to mock BO here, unless you try to read BO through exception in UI.
    
                _mockView = MockRepository.GenerateMock<IEditDetailsView<OrganizationBO>>();
    
                _presenter = new EditDetailsPresenter<OrganizationBO>(_mockView);
    
                // Action
                _presenter.InitViewFor(1);
    
                // Assert
                _mockView.AssertWasCalled(x => x.ShowError(null),
                    options => options.IgnoreArguments() );
            }
    

Fetch can be done this way, but I can’t mock Save() because it’s deep in Csla, unless I can override BusinessBase.Save() which is another mission impossible (too many internal classes involved). To make Csla TDD friendly, let ask for TypeMock to save us out.

 [TestFixture]
    public class When_view_firing_save_event : EstablishContext
    {
        [Test]
        public void should_set_success_msg_and_set_new_BO_in_view_if_save_new_bo_succeed()
        {
            // Arrange
            OrganizationBO orgBo = DataPortal.Create<OrganizationBO>();
            orgBo.Name = "test Org";
            Assert.That(orgBo.IsValid, orgBo.GetValidationMessage());

            _mockView = MockRepository.GenerateMock<IEditDetailsView<OrganizationBO>>();
            _mockView.Stub(x => x.CurrentBO).Return(orgBo).Repeat.Any();

            _presenter = new EditDetailsPresenter<OrganizationBO>(_mockView);

            // This won't work, because BusinessBase.Save() is tightly coupled to DataPortal.
            //_mockService.Expect(x => x.Update(orgBo)).IgnoreArguments();

            // Use TypeMock to mock DataPortal.Update() inside BusinessBase.Save()
            using (RecordExpectations recorder = RecorderManager.StartRecording())
            {
                  // CAUTION: ALL calls here are mocked!!!
                  recorder.DefaultBehavior.IgnoreArguments();
                  recorder.ExpectAndReturn(DataPortal.Update(orgBo), orgBo);
            }

            // Action
            _mockView.Raise(x => x.RequestSave += null, this, EventArgs.Empty);

            // Assert
            _mockView.AssertWasCalled(x => x.CurrentBO = null,
                                      options => options.IgnoreArguments());

            _mockView.AssertWasCalled(x => x.ShowMessage(null),
                                      options => options.IgnoreArguments());

    }

We can remove DomainDAO then if using TypeMock to mock DataPortal.

The new ObjectFactory in CSLA 3.6 seems can make this mock easier, looks like your own ObjectFactory is not static class anymore, different than DataPortal. By creating your own IDataPortal or IObjectFactory, as Ryan did,  mocking those service layer objects should become possible. Someday I will look into this CSLA.ObjectFactory thing if we aren’t going to TypeMock. Rocky’s statement indicates this will be a lot of work:

It is very important to realize that when using this factory model, the data portal does virtually nothing for you. It doesn’t automatically call MarkOld(), MarkNew() or MarkAsChild(). You assume total responsibility for creating and initializing the business object graph, and for setting the state of all objects in the graph.

But it seems Ryan’s ObjectFactroy sample code already finished most of them. One thing doesn’t fit our situation is, we need IRepository<> changed to IDomainObjectRepository in Factory.


        private readonly IRepository<T> _repository;
        public BusinessBaseServerFactory(IRepository<T> repository)
        {
            _repository = repository;
        }

So far, I think unless CSLA.ObjectFactory can provide an optional attribute something like “RepositoryType”, I have to create One ObjectFacotry per BO. Our libaray will become more messier.

Event wire up in MVP

I was struggling in MVP eventhandler for a long time. What is the correct way to link view events with presenter method. Before I used to simply put the presenter method code directly into my UI control’s event, as the example shown in Billy McCafferty,

protected void btnAddDays_OnClick(object sender, EventArgs e)
{
presenter.AddDays(txtNumberOfDays.Text, Page.IsValid);
}

This is OK but I think this needs UI developer know more about presenter’s behaviors, what if  presenter has many methods? Alex Mueller gave out a nice solution by introducing EventHandler in view, which I like it better. In fact, Billy is doing the similar thing but it’s in another way, eventHandler in presenter to enable view hooking up to, this causes the view coding a little bit heavier. Don’t we always want to keep view thinner?

protected void Page_Load(object sender, EventArgs e)
{
EditProjectPresenter presenter =
new EditProjectPresenter(editProjectView);
// I think this code should move into presenter’s constructor. Like Alex Mueller did.
presenter.ProjectUpdated += new EventHandler(HandleProjectUpdated);
presenter.InitView();
}

private void HandleProjectUpdated(object sender, EventArgs e)
{
Response.Redirect(
MyPageMethods.ShowProjectSummary.Show(projectId, userId));
}

I also like Alex’s name covention for eventhandler in MVP.

View         Presenter

RequestAction     ActionListener

When I played his code in VS2008, those null check and extra declaration seem not necessary any more. Instead of doing this:


protected void btnEdit_Click(object sender, EventArgs e)
{
// Raise our event
OnRequestCustomerEdit();
}
public event EventHandler RequestCustomerDetails;
public virtual void OnRequestCustomerEdit()
{
EventHandler<SingleValueEventArgs<string>> eventHandler = RequestCustomerEdit;
if (eventHandler != null)
{
eventHandler(this, null);
}
}

I can now make it much simplier:

btnEdit_Click +=    RequestCustomerDetails;

Not sure is it correct yet. I will update when I found new problem for this.

NHibernate and Stored Procedure, Part2, dealing with orginal values

In part1, I can easily add those original values in the hbm mapping xml files. This works OK for web forms, and also no problem for one time update in win forms. But when I tried to make a repeatable update in win forms, the problem appears.

The original values in Entity objects doesn’t sync! When I called a DAO.commit(), all the changes have been made into database, and the changed columns/properties also still kept in entity objects, you think those original properties will be refreshed? No, not until you clean up the NHiberate cache manually.

Object: ID,
property: originalvalue (1)
property: realvalue (1)

Set realvalue = 2, then call Dao.CommitChanges()

Object: ID,
property: originalvalue (1) {This didn’t update! Still keep the previous original value, should be the current/original value which is 2.}
property: realvalue (2)

The solution I got is, after Dao.CommitChanges(), do a cache clean up manually.

myEntityDao.StartTransaction();
myEntityDao.SaveOrUpdate(view.MyEntityToUpdate);
myEntityDao.CommitChanges();

myEntityDao.Evict( view.MyEntityToUpdate );

This made my winForm repeatable update working. An extra work is, I have to recall the initview() in UI layer, because UI needs to re-load this updated object from Dao after evict().

Again, this is a win form only issue, web form doesn’t have this problem because each page is reloaded after every commit.

How loose is loose enough?

In MVP pattern, the challenge part is how to update data back to model/db from view. If view can call the model’s update function directly, it’s so called ‘supervision controller’, as Martin Fowler’s newly suggested. Obviously this might cause tight coupling problem, and also we don’t want both view and presenter can call model’s update function.

If all the update operation have to go through presentor, this pattern becomes to ‘Passive View’. (So nice to know Chinese, Rader’s post explained it very well to me. Though I did read Billy MacCarfferty’s similar one ealier but could not understand completely. )

Now I got it why both JP and Billy adopt DTO in their demo projects. By transfer DTO-only-data between presenter and view, all the update operation will be centralized to presenter then.

Billy’s idea of ‘Update method in view’ might solve my problem: all the update operations have to go through stored procedure which needs both original and new data as calling parameters. Presentor should have no problem to get all of them, old data from model and new data from view.

As other reader pointed out, in Billy sample code, the presenter’s constructor really confused people.

Presenter(commentListing, DaoFactory.GetCommentDao());

Which means presenter needs to instantiate Dao layer stuff? Billy added a SessionFactoryConfigPath parm in DaoFactory’s constructor, this forced me created a dbConfig.cs in data layer and overrode constructor to make web and presenter both db-config-free.

This also reminds me Steven Yang and JP’s code in which they added a service layer or task layer to entity object back to presenter. Like Jeremy’s sample,

Presenter(IWorkItemView view, IWorkItemService service)

JP did the same thing in his MVP sample, without NHibernate, he put connection string in DataAccess layer.

namespace EnterprisePatterns.DataAccess
{
public class DbConnectionFactory : IDbConnectionFactory
{
public IDbConnection Create()
{
IDbConnection connection = DbProviderFactories.GetFactory(“System.Data.SqlClient”).CreateConnection();
//connection.ConnectionString =
// “data source=(local);Integrated Security=SSPI;Initial Catalog=EnterprisePatternsEdmonton”;
connection.ConnectionString =
“data source=’.\\SQLEXPRESS’;Integrated Security=SSPI;Initial Catalog=Northwind”;
return connection;
}
}

I think this is the right place to configure db, should not be in web, or somewhere else.

In fact, in his task layer, the FindById and FindAll / GetList could be done by NHinberat.Generic. We might cut down one extra layer then.

Way To MVP: MVC vs. MVP

As Darron Schall said,

Instead of a Controller, we now have a Presenter, but the basic idea remains the same – the model stores the data, the view is a representation of that data (not necessarily graphical), and the presenter coordinates the application.

Additionally, the View in MVP is responsible for handling the UI events (like mouseDown, keyDown, etc), which used to be the Controllers job, and the Model becomes strictly a Domain Model.

My thoughts: Presenter acts as a controller in MVC, kind of, but it’s loosely coupled to View. View can switch Presenter by coding. The UI can have multiple views to implement multiple presenters. In fact, it’s common each presenter is created to have its unique purpose which can make test easier, e.g. AddCustomerPresenter.cs, IEditCustomerView.cs and IListCustomersView.cs.

Both Presenter and Model can have methods, so does View. (In ROR, view is very static.) Apparently presenter is a connection between View and Model. RoR doesn’t have this middle layer.

Billy McCafferty ‘s article is good for dotnet dummies, I was very easy to follow. JP’s level is too high for me, currently.