An index of the HomeLibrary application posts can be found here.
As I mentioned in my last Home Library post, I was eager to do a slice through the layers to test out any possible flaws or problems in my general architecture so far. To facilitate this, I fired up Balsamiq and created a View
of roughly how I want one of the screens to look. The screen which manages people:
Note that all I want to implement here is the grid, not the whole screen. Also, I probably don’t need to explain the low fidelity nature of Balsamiq, which ensures that we don’t get too weighed down by such things as colour-selection.
The main things I want to verify in this exercise is:
- whether I have configured
Automapper
correctly; - that my dependency injection is in order; and
- that the data is making it from the database to the
View
as expected.
I created a separate branch to do this exercise, and as always with GIT, it’s very easy to make that available – LayersSlice Branch
Firstly, I created a service which will feed data to a presenter:
public class HomeLibraryService : IHomeLibraryService, IDisposable { private readonly IUnitOfWork _unitOfWork; private readonly IUniversalMapper _mapper; public HomeLibraryService(IUnitOfWork unitOfWork, IUniversalMapper mapper) { _unitOfWork = unitOfWork; _mapper = mapper; } public BindingList<UiModel.Models.Person> GetAllPeople() { var peopleRepository = _unitOfWork.People; PaginatedList<Person> peoplePaginated = null; peoplePaginated = peopleRepository.Paginate(0, 3); var uiList = new BindingList<UiModel.Models.Person>(); _mapper.Map(peoplePaginated.AsQueryable(), uiList); return uiList; } public void Dispose() { // This will be properly done in final code. } }
2 of the main things I want to verify can be ascertained right here. You can see that the parameters to the constructor are facilitating constructor injection by my dependency injection container. That is configured in my composition root
, a.k.a. Program.cs
:
static class Program { private static IUnityContainer _container; /// <summary> /// The main entry point for the application. /// </summary> [STAThread] static void Main() { Database.SetInitializer(new HomeLibraryInitializer()); new HomeLibraryContext().Database.Initialize(true); var autoMapperBootstrapper = new MapperBootstrapper(); autoMapperBootstrapper.Initialize(); ConfigureIoc(); Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new PersonView()); } private static void ConfigureIoc() { _container = new UnityContainer(); _container.RegisterInstance<IMappingEngine>(AutoMapper.Mapper.Engine) .RegisterType<IUniversalMapper, UniversalMapper>(new TransientLifetimeManager()); _container.RegisterType<IRepositoryProvider, RepositoryProvider>( new TransientLifetimeManager(), new InjectionMember[] {new InjectionConstructor(new RepositoryFactories())} ); _container.RegisterType<IUnitOfWork, UnitOfWork>(new TransientLifetimeManager()); _container.RegisterType<IHomeLibraryService, HomeLibraryService>(new TransientLifetimeManager()); PresenterBinder.Factory = new UnityPresenterFactory(_container); } }
The service also contains the code which I use to map a domain object to a Ui-model (objects which are more suited and shaped to the View
for use in Viewmodels
). With just two lines of code I’m able to map all of the properties from the domain’s PaginatedList
to the Ui’s BindingList
. Thank you Jimmy Bogard!
I’ve only created one View
for this demonstration as I only needed to verify the slice through the layers. What you will see in the PersonView
is that I have used the PresenterBinding
attribute to bind the View
to the PeoplePresenter
:
[PresenterBinding(typeof(PeoplePresenter))] public partial class PersonView : MvpForm, IPersonView { public PersonView() { InitializeComponent(); } private void PersonView_Load(object sender, EventArgs e) { dgvPersons.AllowUserToAddRows = false; dgvcDelete.Image = ImageResources.delete; dgvcEdit.Image = ImageResources.edit; dgvPersons.DataSource = ViewModel.People; } public event EventHandler ViewClosing; public event EventHandler EditPersonClicked; public PersonViewModel ViewModel { get; set; } private void dgvPersons_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e) { switch (dgvPersons.Columns[e.ColumnIndex].Name) { case "dgvcIsAuthor": bool isAuthor; if (bool.TryParse(dgvPersons.Rows[e.RowIndex].Cells["dgvcIsAuthor"].Value.ToString(), out isAuthor)) { e.Value = isAuthor ? ImageResources.tick : ImageResources.cross; } break; } } public void ReleasePresenter(IPresenter presenter) { PresenterBinder.Factory.Release(presenter); } protected override void OnClosing(CancelEventArgs e) { ViewClosing(this, EventArgs.Empty); base.OnClosing(e); } }
WinformsMVP has 2 mechanisms for binding, that attribute and binding by convention. The reason I can’t use convention in my project is because my presenters will be located in a separate project which does not conform to the convention used by WinformsMVP to search for presenters. And that’s okay. It was a design choice I made. But if people prefer to bind by convention and to organise their projects in such a way that it conforms to that convention, then that’s okay too.
When the app is fired up, the following window loads:
It’s not much to look at now, but this is just “proof of concept” time. The data is there and all is well for the plumbing, from top to bottom in the stack.
Note that a lot of the code in this post (in the branch referred above) will not look the same as I progress the project further. I have already moved forward and started putting in place the bootstrapping code in earnest.
Look out for a post in the near future which will talk about the query handling aspects of my architecture by virtue of the Mediator Pattern. Got to keep those presenters clean!