SlideShare una empresa de Scribd logo
1 de 53
MVC
Purée
Codegarde
n
June 2014
Just to introduce myself…
• I’m Andy Butland
• I work for Zone, a digital agency where I head up the
.NET development team in London
• We develop web sites and applications primarily using
ASP.Net MVC and Umbraco
• Blog (sporadically) at http://web-matters.blogspot.it/
• Find here a copy of slides and links to various resources
• Contact: abutland73@gmail.com / @andybutland
And what are we talking about… “MVC
Purée”?
• We’ll be discussing best practices with using MVC in
Umbraco:
• “For the session title, how about ‘MVC purist’?”
• “Hmm, don’t like the sound of that… makes me sound too
much of a pedant, and we all have to be pragmatic too.”
• “OK… we’ll go with ‘MVC purée’”
Contents
1
2
3
6
4
MVC best practices 5
7
Using MVC with Umbraco
Strongly typed views and
mapping from Umbraco
data
Cleaning up views
Dependency injection
Unit testing
Wrap up and Q & A
1. MVC best practices
Journey to Umbraco MVC
“Classic”
ASP
ASP.Net
Webforms
ASP.Net
MVC
Umbraco
(XSLT)
Umbraco
(Razor)
Umbraco
(MVC)
Learnings and best practices…
MVC patterns and practices
MODEL
CONTROLLER
VIEW
VIEW
MODELS
Simple, strongly typed views,
with no domain logic Custom view models
for each view
Mapping from domain
models to view models
Application components
supported with unit tests
and composed via
dependency injection
COMPONEN
T
Separation of concerns
2. Using MVC with Umbraco
MVC techniques with Umbraco
• Since version 4.10 we’ve been able to use MVC as well
as traditional Web Forms for rendering Umbraco
templates.
• MVC rendering has been implemented in a flexible way,
giving a lot of scope to the developer in building their
application.
• Logic, querying and data access in the views
• Using surface controllers actions with partial views
• Hijacking routes
Logic and querying in the views
• This technique is most similar to that used in traditional
Umbraco templating and can be used be all developers,
not just those using Visual Studio.
• It’s not ideal though from a more purist MVC
perspective.
UMBRACO
DEFAULT
CONTROLLERRequest
TEMPLATE/
VIEWPopulates and
passes a standard
RenderModel
Within the view we can use
the Umbraco helper to:
• Access page properties
• Query for other nodes
• Run Examine searches
Example: using the Umbraco helper in our
view@inherits Umbraco.Web.Mvc.UmbracoTemplatePage
<h1>@Model.Content.GetPropertyValue("heading")</h1>
@{
var rootNode = Umbraco.TypedContentAtRoot().First();
var categoryNodes = rootNode
.Descendants("CategoryFolder")
.First()
.Children;
foreach (var item in categoryNodes)
{
<div>@item.GetPropertyValue("title")</div>
}
}
Property access
not obvious for
front-end
developers less
familiar with
Umbraco APIs.
The Umbraco
helper provides
access to
functions that
arguably should
not be the
concern of the
view.
Surface controller actions and partials
• We can prepare and pass our own view model by
making an @Html.Action call from within our template,
to a surface controller that returns a partial.
• It’s downside is a more complex request flow, and the
necessity of creating two templates.
UMBRACO
DEFAULT
CONTROLLERRequest
TEMPLATE/
VIEW
SURFACE
CONTROLLER
ACTION
PARTIAL
VIEW
Example: using a surface controller and
partial@inherits Umbraco.Web.Mvc.UmbracoTemplatePage
@{
Layout = "_Layout.cshtml";
}
@Html.Action("Register", "AccountSurface")
[ChildActionOnly]
public PartialViewResult Register()
{
var vm = new RegisterViewModel();
PopulateRegisterViewModel(vm);
return PartialView("Register", vm);
}
@model RegisterViewModel
<h1>@Model.Heading</h1>
Our Umbraco template
view calls out to a
surface controller action
method…
… which populates a
custom view model and
passes this to a partial
view…
… that can benefit from
strong typing.
Route hijacking
• We can intercept the request with our own controller,
populate a custom view model and pass it to the
strongly typed view.
• We now have a very clean view with little logic, and
proper separation of concerns from an MVC
perspective.
• There is a little more work to do from a developer
perspective, but we can mitigate that…
CUSTOM
CONTROLLER
Request
TEMPLATE/
VIEWPopulates and
passes a custom
view model
Example: route hijacking
using System.Web.Mvc;
using Umbraco.Web.Mvc;
public class EventPageController : RenderMvcController
{
public ActionResult EventPage()
{
// Populate view model and
// render view
}
}
The controller name
must match the
document type alias,
e.g. EventPage
The action method
name must match the
template name
BETTER STILL… see Hybrid Framework method of
inheriting from SurfaceController and
implementing IRenderMvcController
3. Strongly typed views and
mapping from Umbraco data
Custom view model
• We can create a simple POCO class to represent our
view
• In most cases will represent a single document type
• However often we’ll want to pull in data from other nodes,
or even other data sources
• We’ll map the Umbraco content to our view model
• Our view can reference that as it’s @model
• It’s now simply displaying properties and collections from
the view model, with no business logic or data access
• Actually need have no reference to Umbraco at all…
• …unless you want it of course. You can still inherit from
the Umbraco base page if you need to.
Example: custom view model (definition)
public class NewsLandingPageViewModel
{
public string Heading { get; set; }
public IHtmlString BodyText { get; set; }
public IList<NewsItem> LatestNewsItems { get; set; }
}
Directly mapped
from fields on the
document type
Generated from a
node query
Example: custom view model (mapping)
public ActionResult NewsLandingPage()
{
var vm = new NewsLandingPageViewModel
{
Heading = CurrentPage.GetPropertyValue<string>("heading"),
BodyText = CurrentPage.GetPropertyValue<IHtmlString>("bodyText"),
LatestNewsItems = CurrentPage.Descendants("NewsItem")
.OrderByDescending(x => DateTime.Parse(
x.GetPropertyValue<string>(“publicationDate")))
.Take(10)
.Select(x => new NewsItem
{
Heading = CurrentPage.GetPropertyValue<string>("heading"),
PublicationDate = DateTime.Parse(
x.GetPropertyValue<string>("publicationDate"))),
})
.ToList(),
};
return View("NewsLandingPage", vm);
}
Using the “Umbraco Mapper” package
• The package has been created to streamline the
mapping of Umbraco content to view models
• Convention based mapping from single instances and
collections of IPublishedContent
• Ability to override those conventions for particular
properties as required
• Mapping from other sources such as XML and JSON
• Supply of custom mapping methods for handling custom
types
Example: mapping using conventions
public ActionResult NewsLandingPage()
{
var vm = new NewsLandingPageViewModel();
var latestNewsNodes = CurrentPage.Descendants("NewsItem")
.OrderByDescending(x => DateTime.Parse(
x.GetPropertyValue<string>(“publicationDate")))
.Take(10);
var mapper = new UmbracoMapper();
mapper.Map(CurrentPage, vm)
.MapCollection(latestNewsNodes, vm.LatestNewsItems);
return View("NewsLandingPage", vm);
}
Example: overriding conventions
mapper.Map(CurrentPage, vm, new Dictionary<string,PropertyMapping>
{
{
"Copy", new PropertyMapping
{
SourceProperty = "bodyText",
}
}
})
.MapCollection(latestNewsNodes, vm.LatestNewsItems,
new Dictionary<string,PropertyMapping>
{
{
"Category", new PropertyMapping
{
SourceProperty = "Name",
LevelsAbove = 1,
}
}
});
Maps from property
with different name
Maps from node at
a higher level in the
tree.
COMING SOON… attribute based property mappings
Example: a custom mapping (1)
public class GeoCoordinate
{
public decimal Longitude { get; set; }
public decimal Latitude { get; set; }
public int Zoom { get; set; }
}
...
var mapper = new UmbracoMapper();
mapper.AddCustomMapping(typeof(GeoCoordinate).FullName,
CustomMappings.MapGeoCoordinate);
Example: a custom mapping (2)
public static object MapGeoCoordinate(IUmbracoMapper mapper,
IPublishedContent contentToMapFrom, string propName, bool isRecursive)
{
var propertyValueAsCsv = contentToMapFrom
.GetPropertyValue<string>(propName, isRecursive, null);
if (!string.IsNullOrEmpty(propertyValueAsCsv))
{
var parts = propertyValueAsCsv.Split(',');
if (parts != null && parts.Length == 3)
{
return new GeoCoordinate
{
Latitude = decimal.Parse(parts[0]),
Longitude = decimal.Parse(parts[1]),
Zoom = int.Parse(parts[2]),
};
}
}
return null;
}
4. Dependency injection
Dependency injection: what and why?
• By injecting our dependencies to a class, rather than
“newing” them up within one, we:
• Program to interfaces – improving the testability of our
code
• Reduce coupling
• Develop components with single responsibilities
• An IoC container can then help us with the instantiation
of the concrete classes at runtime
Example: injecting services to a controller
public class HomePageController : BaseController
{
private readonly IDataService _dataService;
public HomePageController(IDataService dataService)
{
_dataService = dataService;
}
public ActionResult HomePage()
{
// Do something with the data service
var data = _dataService.GetData();
...
}
}
An instance of
IDataService - as well as
any dependencies it may
have - is injected into the
controller’s constructor at
run-time.
Example: integrating Ninject with Umbraco
PM> Install-Package Ninject.MVC3 Install Ninject from NuGet
Creates a file NinjectWebCommon.cs in App_Start
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<IDataService>().To<MyDataService>();
}
Any time a component “requests”
an IDataService, they’ll get a
concrete DataService
5. Unit testing
Isolating our unit under test from
dependencies• When unit testing, we aim to confirm the function of a
particular method or class, by replacing any
dependencies it has with versions that are under the
control of our tests
• We avoid brittle data or slow running processes
• We isolate our tests to just the small piece being
examined
UNIT UNDER
TEST
DEPENDENT
CLASS
DEPENDENT
CLASS
… which allows
us to we replace
them with mocks
or stubs we
control
Dependencies
are referenced
through
interfaces…
Example: using mocks (with Moq)
[TestMethod]
public void WebServiceTests_SuccessResponse_ReturnsStatusAndMessage()
{
// Arrange
var service = new WebServiceWrapper(MockHttpClient(),
"http://www.example.com/");
// Act
var result = service.Request("exchange-rates",
ResponseContentType.JSON).Result;
// Assert
Assert.IsTrue(result.Success);
Assert.IsNotNull(result.Response);
Assert.IsNull(result.ErrorMessage);
}
Within the test,
we are avoiding
calling the
external web
service directly
by mocking the
HTTP call
Example: using mocks (with Moq) (2)
private static IHttpClient MockHttpClient()
{
var mock = new Mock<IHttpClient>();
mock.Setup(x => x.GetAsync(It.IsAny<string>()))
.Returns(Task<HttpResponseMessage>.Factory.StartNew(() =>
{
var response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StringContent(@"{ 'rates': [{
'currencyCode': 'EUR',
'rate': 1.24
}]}";);
return response;
}));
mock.Setup(x => x.GetAsync(It.Is<string>(y => y.Contains("invalid"))))
.Returns(Task<HttpResponseMessage>.Factory.StartNew(() =>
{
return new HttpResponseMessage(HttpStatusCode.BadRequest);
}));
return mock.Object;
}
At runtime we
create our own
“HttpClient” by
mocking the
interface and
return fixed
responses.
Unit testing with Umbraco
• We can use various techniques to control our
dependencies, e.g. mocks and stubs
• Umbraco doesn’t make it particularly easy…
• Problems with unit testing surface controllers
• Issues with extension and static methods
• … but be no means impossible
• Avoid the problem - move logic into a separate class,
leaving a simple controller of little value to test
• Utilise the base test classes
• Look into MS Fakes
Example: unit testing a surface controller
[HttpPost]
public ActionResult CreateComment(CommentViewModel model)
{
if (!ModelState.IsValid)
{
return CurrentUmbracoPage();
}
TempData.Add("CustomMessage", "Thanks for your comment.");
return RedirectToCurrentUmbracoPage();
}
If validation fails,
should return to
view.
If successful,
should have
value in
TempData and
redirect
Example: unit testing a surface controller (2)
[TestMethod]
public void CreateComment_WithValidComment_RedirectsWithMessage()
{
// Arrange
var controller = new BlogPostSurfaceController();
var model = new CommentViewModel
{
Name = "Fred",
Email = "fred@freddie.com",
Comment = "Can I test this?",
};
// Act
var result = controller.CreateComment(model);
// Assert
Assert.IsNotNull(result);
}
Will fail, with null
reference for
UmbracoContext
Example: testing a “command handler” class
public class BlogPostSurfaceControllerCommandHandler
{
public ModelStateDictionary ModelState { get; set; }
public TempDataDictionary TempData { get; set; }
public bool HandleCreateComment(CommentViewModel model)
{
if (!ModelState.IsValid) {
return false;
}
TempData.Add("CustomMessage", "Thanks for your comment.");
return true;
}
}
Logic moved to class
with no Umbraco
dependency…
Example: testing a “command handler” class
(2)public class BlogPostSurfaceController : SurfaceController
{
BlogPostSurfaceControllerCommandHandler _commandHandler;
public BlogPostSurfaceController()
{
_commandHandler = new BlogPostSurfaceControllerCommandHandler();
_commandHandler.ModelState = ModelState;
_commandHandler.TempData = TempData;
}
[HttpPost]
public ActionResult CreateCommentWithHandler(CommentViewModel model)
{
if (!_commandHandler.HandleCreateComment(model)) {
return CurrentUmbracoPage();
}
return RedirectToCurrentUmbracoPage();
}
}
… which is referenced
in the surface
controller…
… leaving a thin
controller method, with
little value for testing.
Example: testing a “command handler” class
(3)[TestMethod]
public void CreateComment_WithValidComment_ReturnsTrueWithMessage()
{
// Arrange
var handler = new BlogPostSurfaceControllerCommandHandler();
handler.ModelState = new ModelStateDictionary();
handler.TempData = new TempDataDictionary();
var model = new CommentViewModel
{
Name = "Fred",
Email = "fred@freddie.com",
Comment = "Can I test this?",
};
// Act
var result = handler.HandleCreateComment(model);
// Assert
Assert.IsTrue(result);
Assert.IsNotNull(handler.TempData["CustomMessage"]);
}
The logic in the
handler though, can
now be tested.
Using the Umbraco core base test classes
• Allow us to test our surface controller without
modification:
• We need to clone the source code, build and reference
the Umbraco.Tests.dll in our project
• We have to use NUnit
• We then have access to some base classes we can
inherit from, to run tests with the appropriate contexts set
up
• But even then, there are some reflection hoops to jump
through
• Once done though, we can successfully run tests on
Umbraco surface controllers
Example: base test classes
[TestFixture]
[DatabaseTestBehavior(DatabaseBehavior.NoDatabasePerFixture)]
public class BlogPostSurfaceControllerTests : BaseRoutingTest
{
[Test]
public void ExampleTest()
{
var controller = GetController();
var model = new CommentViewModel {
Email = "fred@freddie.com",
Comment = "Can I test this?",
};
var result = controller.CreateComment(model);
var redirectResult = result as RedirectToUmbracoPageResult;
Assert.IsNotNull(redirectResult);
Assert.AreEqual(1000, redirectResult.PublishedContent.Id);
Assert.IsNotNull(controller.TempData["CustomMessage"]);
}
}
We can test for
specific Umbraco
results and other
controller actions.
Using base class
methods and reflection,
the controller can be
instantiated with
necessary contexts.
The test class inherits
from the Umbraco base
test class.
Working around issues with extension
methods
• Some Umbraco methods – particularly on
IPublishedContent – are implemented as extension
methods
• These can’t be mocked or stubbed
• Using MS Fakes
• We can add a fakes
assembly for any dll
• And at runtime
replace a method’s
implementation with one of our own
• VS.Net Premium or Ultimate only though 
Example: can’t mock IPublishedContent
methodprivate static IPublishedContent MockIPublishedContent()
{
var mock = new Mock<IPublishedContent>();
mock.Setup(x => x.Id).Returns(1000);
mock.Setup(x => x.Name).Returns("Test content");
mock.Setup(x => x.CreatorName).Returns("A.N. Editor");
// This won’t work...
mock.Setup(x => x.GetPropertyValue(It.IsAny<string>()))
.Returns((string alias) => MockIPublishedContentProperty(alias));
return mock.Object;
}
GetPropertyValue() is
an extension method,
which can’t be mocked.
Example: using Microsoft Fakes
[TestMethod]
public void MapFromIPublishedContent_MapsCustomProperties()
{
using (ShimsContext.Create())
{
var model = new SimpleViewModel();
var mapper = GetMapper();
var content = new StubPublishedContent();
Umbraco.Web.Fakes.ShimPublishedContentExtensions
.GetPropertyValueIPublishedContentStringBoolean =
(doc, alias, recursive) => {
switch (alias) {
case "bodyText": return "This is the body text";
default: return string.Empty;
}
};
mapper.Map(content, model);
Assert.AreEqual("This is the body text", model.BodyText);
}
}
We replace the
method
implementation with
our own at runtime.
6. Cleaning up views
Strongly typed partials
• Often our HTML will contain blocks of similar layout that
are repeated on multiple pages
• We can use @Html.Partial to “DRY” up our views
• By having our view models implement multiple small
interfaces, or by using inheritance, we can strongly
type our partial but avoid instantiating objects for each
one
Example: custom view model for partial
public class HeroSectionViewModel
{
public string Heading { get; set; }
public string Standfirst { get; set; }
}
@model CodegardenSamples.Models.HeroSectionViewModel
<h1>@Model.Heading</h1>
<p>@Model.Standfirst</p>
...
@model CodegardenSamples.Models.HomePageViewModel
@Html.Partial("_HeroSection“, new HeroSectionViewModel
{
Heading = Model.Heading,
Standfirst = Model.Heading
})
We can strongly type
our partials just as we
can full page views
BUT… we have to handle instantiating this
partial's model in our view
Example: view models with inheritance
public abstract class BaseViewModel
{
public string Heading { get; set; }
public string Standfirst { get; set; }
}
public class HomePageViewModel : BaseViewModel { }
public class ContentPageViewModel : BaseViewModel { }
...
@model CodegardenSamples.Models.HomePageViewModel
@Html.Partial("_HeroSection")
...
@model CodegardenSamples.Models.BaseViewModel
<h1>@Model.Heading</h1>
<p>@Model.Standfirst</p>
As our view models
inherit from a base
class…
… a strongly typed
partial can be created
that can reference the
model of the parent
page.
BUT… using inheritance in this way can be
restrictive – we can only inherit from one base.
Example: view models with interfaces (1)
public interface IHeroSection
{
string Heading { get; }
string Standfirst { get; }
}
public interface ISideBar
{
string SideBarTitle { get; }
string SideBarIntro { get; }
}
public class HomePageViewModel : IHeroSection, ISideBar
{
public string Heading { get; set; }
public string Standfirst { get; set; }
public string SideBarTitle { get; set; }
public string SideBarIntro { get; set; }
}
Having our view model implement
multiple small interfaces…
Example: view models with interfaces (2)
@model CodegardenSamples.Models.HomePageViewModel
@Html.Partial("_HeroSection")
@Html.Partial("_SideBar")
...
@model CodegardenSamples.Models.IHeroSection
<h1>@Model.Heading</h1>
<p>@Model.Standfirst</p>
...
@model CodegardenSamples.Models.ISideBar
<h2>@Model.SideBarTitle.</h2>
<p>@Model.SideBarIntro</p>
… again means we can reference
the model in a strongly typed
manner in the partials.
7. Wrap up and Q &A
In summary
• No one true way to build an Umbraco site… pick
what works for you and your team
• If you like the “purist” MVC approach, you can apply
these best practices without too much additional
effort, and still be “pragmatic” about delivery to your
clients
Lastly, some thanks…
• Neil, Ali, Rob, Raffaele, Nnamdi, Ollie and the rest of my
colleagues at Zone
• Numerous discussions, questions and advice as we’ve
evolved techniques and technologies over the years
• Anthony, Darren, Ismail, Jeavon, Jeroen, Shannon,
Warren and many others
• Blogs, forum threads and other community contributions
that have influenced the thinking behind our work and this
presentation
MVC Puree - Approaches to MVC with Umbraco

Más contenido relacionado

La actualidad más candente

How to Build Real-time Chat App with Express, ReactJS, and Socket.IO?
How to Build Real-time Chat App with Express, ReactJS, and Socket.IO?How to Build Real-time Chat App with Express, ReactJS, and Socket.IO?
How to Build Real-time Chat App with Express, ReactJS, and Socket.IO?Katy Slemon
 
Angular or Backbone: Go Mobile!
Angular or Backbone: Go Mobile!Angular or Backbone: Go Mobile!
Angular or Backbone: Go Mobile!Doris Chen
 
Backbone JS for mobile apps
Backbone JS for mobile appsBackbone JS for mobile apps
Backbone JS for mobile appsIvano Malavolta
 
JavaScript - Chapter 7 - Advanced Functions
 JavaScript - Chapter 7 - Advanced Functions JavaScript - Chapter 7 - Advanced Functions
JavaScript - Chapter 7 - Advanced FunctionsWebStackAcademy
 
JavaScript - Chapter 15 - Debugging Techniques
 JavaScript - Chapter 15 - Debugging Techniques JavaScript - Chapter 15 - Debugging Techniques
JavaScript - Chapter 15 - Debugging TechniquesWebStackAcademy
 
Node JS Express : Steps to Create Restful Web App
Node JS Express : Steps to Create Restful Web AppNode JS Express : Steps to Create Restful Web App
Node JS Express : Steps to Create Restful Web AppEdureka!
 
The Windows Runtime and the Web
The Windows Runtime and the WebThe Windows Runtime and the Web
The Windows Runtime and the WebJeremy Likness
 
My XML is Alive! An Intro to XAML
My XML is Alive! An Intro to XAMLMy XML is Alive! An Intro to XAML
My XML is Alive! An Intro to XAMLJeremy Likness
 
AngularJS 101 - Everything you need to know to get started
AngularJS 101 - Everything you need to know to get startedAngularJS 101 - Everything you need to know to get started
AngularJS 101 - Everything you need to know to get startedStéphane Bégaudeau
 
jQuery - Chapter 3 - Effects
jQuery - Chapter 3 - Effects  jQuery - Chapter 3 - Effects
jQuery - Chapter 3 - Effects WebStackAcademy
 
Introduction to VueJS & Vuex
Introduction to VueJS & VuexIntroduction to VueJS & Vuex
Introduction to VueJS & VuexBernd Alter
 
Architetture Serverless con SQL Server e Azure Functions
Architetture Serverless con SQL Server e Azure FunctionsArchitetture Serverless con SQL Server e Azure Functions
Architetture Serverless con SQL Server e Azure FunctionsMassimo Bonanni
 
Integrating Servlets and JSP (The MVC Architecture)
Integrating Servlets and JSP  (The MVC Architecture)Integrating Servlets and JSP  (The MVC Architecture)
Integrating Servlets and JSP (The MVC Architecture)Amit Ranjan
 
SenchaCon 2016: Learn the Top 10 Best ES2015 Features - Lee Boonstra
SenchaCon 2016: Learn the Top 10 Best ES2015 Features - Lee Boonstra  SenchaCon 2016: Learn the Top 10 Best ES2015 Features - Lee Boonstra
SenchaCon 2016: Learn the Top 10 Best ES2015 Features - Lee Boonstra Sencha
 

La actualidad más candente (20)

PHP & MVC
PHP & MVCPHP & MVC
PHP & MVC
 
Web Components
Web ComponentsWeb Components
Web Components
 
How to Build Real-time Chat App with Express, ReactJS, and Socket.IO?
How to Build Real-time Chat App with Express, ReactJS, and Socket.IO?How to Build Real-time Chat App with Express, ReactJS, and Socket.IO?
How to Build Real-time Chat App with Express, ReactJS, and Socket.IO?
 
Angular or Backbone: Go Mobile!
Angular or Backbone: Go Mobile!Angular or Backbone: Go Mobile!
Angular or Backbone: Go Mobile!
 
Backbone JS for mobile apps
Backbone JS for mobile appsBackbone JS for mobile apps
Backbone JS for mobile apps
 
JavaScript - Chapter 7 - Advanced Functions
 JavaScript - Chapter 7 - Advanced Functions JavaScript - Chapter 7 - Advanced Functions
JavaScript - Chapter 7 - Advanced Functions
 
JavaScript - Chapter 15 - Debugging Techniques
 JavaScript - Chapter 15 - Debugging Techniques JavaScript - Chapter 15 - Debugging Techniques
JavaScript - Chapter 15 - Debugging Techniques
 
Node JS Express : Steps to Create Restful Web App
Node JS Express : Steps to Create Restful Web AppNode JS Express : Steps to Create Restful Web App
Node JS Express : Steps to Create Restful Web App
 
The Windows Runtime and the Web
The Windows Runtime and the WebThe Windows Runtime and the Web
The Windows Runtime and the Web
 
My XML is Alive! An Intro to XAML
My XML is Alive! An Intro to XAMLMy XML is Alive! An Intro to XAML
My XML is Alive! An Intro to XAML
 
AngularJS 101 - Everything you need to know to get started
AngularJS 101 - Everything you need to know to get startedAngularJS 101 - Everything you need to know to get started
AngularJS 101 - Everything you need to know to get started
 
jQuery - Chapter 3 - Effects
jQuery - Chapter 3 - Effects  jQuery - Chapter 3 - Effects
jQuery - Chapter 3 - Effects
 
Aem best practices
Aem best practicesAem best practices
Aem best practices
 
Introduction to VueJS & Vuex
Introduction to VueJS & VuexIntroduction to VueJS & Vuex
Introduction to VueJS & Vuex
 
Architetture Serverless con SQL Server e Azure Functions
Architetture Serverless con SQL Server e Azure FunctionsArchitetture Serverless con SQL Server e Azure Functions
Architetture Serverless con SQL Server e Azure Functions
 
Integrating Servlets and JSP (The MVC Architecture)
Integrating Servlets and JSP  (The MVC Architecture)Integrating Servlets and JSP  (The MVC Architecture)
Integrating Servlets and JSP (The MVC Architecture)
 
SenchaCon 2016: Learn the Top 10 Best ES2015 Features - Lee Boonstra
SenchaCon 2016: Learn the Top 10 Best ES2015 Features - Lee Boonstra  SenchaCon 2016: Learn the Top 10 Best ES2015 Features - Lee Boonstra
SenchaCon 2016: Learn the Top 10 Best ES2015 Features - Lee Boonstra
 
Fundaments of Knockout js
Fundaments of Knockout jsFundaments of Knockout js
Fundaments of Knockout js
 
Enterprise TypeScript
Enterprise TypeScriptEnterprise TypeScript
Enterprise TypeScript
 
Fast mobile web apps
Fast mobile web appsFast mobile web apps
Fast mobile web apps
 

Similar a MVC Puree - Approaches to MVC with Umbraco

How AngularJS Embraced Traditional Design Patterns
How AngularJS Embraced Traditional Design PatternsHow AngularJS Embraced Traditional Design Patterns
How AngularJS Embraced Traditional Design PatternsRan Mizrahi
 
Umbraco OktoberFest 2014
Umbraco OktoberFest 2014Umbraco OktoberFest 2014
Umbraco OktoberFest 2014Jeavon Leopold
 
jquery summit presentation for large scale javascript applications
jquery summit  presentation for large scale javascript applicationsjquery summit  presentation for large scale javascript applications
jquery summit presentation for large scale javascript applicationsDivyanshGupta922023
 
DDD, CQRS and testing with ASP.Net MVC
DDD, CQRS and testing with ASP.Net MVCDDD, CQRS and testing with ASP.Net MVC
DDD, CQRS and testing with ASP.Net MVCAndy Butland
 
Ember.js - A JavaScript framework for creating ambitious web applications
Ember.js - A JavaScript framework for creating ambitious web applications  Ember.js - A JavaScript framework for creating ambitious web applications
Ember.js - A JavaScript framework for creating ambitious web applications Juliana Lucena
 
Uncommon Design Patterns
Uncommon Design PatternsUncommon Design Patterns
Uncommon Design PatternsStefano Fago
 
Design patterns in java script, jquery, angularjs
Design patterns in java script, jquery, angularjsDesign patterns in java script, jquery, angularjs
Design patterns in java script, jquery, angularjsRavi Bhadauria
 
Multilingualism makes better programmers
Multilingualism makes better programmersMultilingualism makes better programmers
Multilingualism makes better programmersAlexander Varwijk
 
Design patterns in javascript
Design patterns in javascriptDesign patterns in javascript
Design patterns in javascriptAyush Sharma
 
Patterns Are Good For Managers
Patterns Are Good For ManagersPatterns Are Good For Managers
Patterns Are Good For ManagersAgileThought
 
Google Cloud Endpoints: Building Third-Party APIs on Google AppEngine
Google Cloud Endpoints: Building Third-Party APIs on Google AppEngineGoogle Cloud Endpoints: Building Third-Party APIs on Google AppEngine
Google Cloud Endpoints: Building Third-Party APIs on Google AppEngineRoman Kirillov
 
MV* presentation frameworks in Javascript: en garde, pret, allez!
MV* presentation frameworks in Javascript: en garde, pret, allez!MV* presentation frameworks in Javascript: en garde, pret, allez!
MV* presentation frameworks in Javascript: en garde, pret, allez!Roberto Messora
 
mobile in the cloud with diamonds. improved.
mobile in the cloud with diamonds. improved.mobile in the cloud with diamonds. improved.
mobile in the cloud with diamonds. improved.Oleg Shanyuk
 
MvvmQuickCross for Windows Phone
MvvmQuickCross for Windows PhoneMvvmQuickCross for Windows Phone
MvvmQuickCross for Windows PhoneVincent Hoogendoorn
 
Voorhoede - Front-end architecture
Voorhoede - Front-end architectureVoorhoede - Front-end architecture
Voorhoede - Front-end architectureJasper Moelker
 

Similar a MVC Puree - Approaches to MVC with Umbraco (20)

How AngularJS Embraced Traditional Design Patterns
How AngularJS Embraced Traditional Design PatternsHow AngularJS Embraced Traditional Design Patterns
How AngularJS Embraced Traditional Design Patterns
 
Umbraco OktoberFest 2014
Umbraco OktoberFest 2014Umbraco OktoberFest 2014
Umbraco OktoberFest 2014
 
jquery summit presentation for large scale javascript applications
jquery summit  presentation for large scale javascript applicationsjquery summit  presentation for large scale javascript applications
jquery summit presentation for large scale javascript applications
 
Javascript Design Patterns
Javascript Design PatternsJavascript Design Patterns
Javascript Design Patterns
 
DDD, CQRS and testing with ASP.Net MVC
DDD, CQRS and testing with ASP.Net MVCDDD, CQRS and testing with ASP.Net MVC
DDD, CQRS and testing with ASP.Net MVC
 
Ember.js - A JavaScript framework for creating ambitious web applications
Ember.js - A JavaScript framework for creating ambitious web applications  Ember.js - A JavaScript framework for creating ambitious web applications
Ember.js - A JavaScript framework for creating ambitious web applications
 
Uncommon Design Patterns
Uncommon Design PatternsUncommon Design Patterns
Uncommon Design Patterns
 
Design patterns in java script, jquery, angularjs
Design patterns in java script, jquery, angularjsDesign patterns in java script, jquery, angularjs
Design patterns in java script, jquery, angularjs
 
Multilingualism makes better programmers
Multilingualism makes better programmersMultilingualism makes better programmers
Multilingualism makes better programmers
 
Design patterns in javascript
Design patterns in javascriptDesign patterns in javascript
Design patterns in javascript
 
Patterns Are Good For Managers
Patterns Are Good For ManagersPatterns Are Good For Managers
Patterns Are Good For Managers
 
Google Cloud Endpoints: Building Third-Party APIs on Google AppEngine
Google Cloud Endpoints: Building Third-Party APIs on Google AppEngineGoogle Cloud Endpoints: Building Third-Party APIs on Google AppEngine
Google Cloud Endpoints: Building Third-Party APIs on Google AppEngine
 
Backbone.js
Backbone.jsBackbone.js
Backbone.js
 
MV* presentation frameworks in Javascript: en garde, pret, allez!
MV* presentation frameworks in Javascript: en garde, pret, allez!MV* presentation frameworks in Javascript: en garde, pret, allez!
MV* presentation frameworks in Javascript: en garde, pret, allez!
 
Intro to AngularJs
Intro to AngularJsIntro to AngularJs
Intro to AngularJs
 
Javascript Design Patterns
Javascript Design PatternsJavascript Design Patterns
Javascript Design Patterns
 
mobile in the cloud with diamonds. improved.
mobile in the cloud with diamonds. improved.mobile in the cloud with diamonds. improved.
mobile in the cloud with diamonds. improved.
 
MvvmQuickCross for Windows Phone
MvvmQuickCross for Windows PhoneMvvmQuickCross for Windows Phone
MvvmQuickCross for Windows Phone
 
Voorhoede - Front-end architecture
Voorhoede - Front-end architectureVoorhoede - Front-end architecture
Voorhoede - Front-end architecture
 
Mini-Training: AngularJS
Mini-Training: AngularJSMini-Training: AngularJS
Mini-Training: AngularJS
 

Último

Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxhariprasad279825
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebUiPathCommunity
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsSergiu Bodiu
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii SoldatenkoFwdays
 
Search Engine Optimization SEO PDF for 2024.pdf
Search Engine Optimization SEO PDF for 2024.pdfSearch Engine Optimization SEO PDF for 2024.pdf
Search Engine Optimization SEO PDF for 2024.pdfRankYa
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubKalema Edgar
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 3652toLead Limited
 
SAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptxSAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptxNavinnSomaal
 
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc
 
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo Day
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo DayH2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo Day
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo DaySri Ambati
 
Story boards and shot lists for my a level piece
Story boards and shot lists for my a level pieceStory boards and shot lists for my a level piece
Story boards and shot lists for my a level piececharlottematthew16
 
Powerpoint exploring the locations used in television show Time Clash
Powerpoint exploring the locations used in television show Time ClashPowerpoint exploring the locations used in television show Time Clash
Powerpoint exploring the locations used in television show Time Clashcharlottematthew16
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024Stephanie Beckett
 
Commit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyCommit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyAlfredo García Lavilla
 
WordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your BrandWordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your Brandgvaughan
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxLoriGlavin3
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...Fwdays
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Scott Keck-Warren
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfAlex Barbosa Coqueiro
 

Último (20)

Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptx
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio Web
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platforms
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko
 
Search Engine Optimization SEO PDF for 2024.pdf
Search Engine Optimization SEO PDF for 2024.pdfSearch Engine Optimization SEO PDF for 2024.pdf
Search Engine Optimization SEO PDF for 2024.pdf
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding Club
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365
 
SAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptxSAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptx
 
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
 
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo Day
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo DayH2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo Day
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo Day
 
Story boards and shot lists for my a level piece
Story boards and shot lists for my a level pieceStory boards and shot lists for my a level piece
Story boards and shot lists for my a level piece
 
Powerpoint exploring the locations used in television show Time Clash
Powerpoint exploring the locations used in television show Time ClashPowerpoint exploring the locations used in television show Time Clash
Powerpoint exploring the locations used in television show Time Clash
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024
 
Commit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyCommit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easy
 
WordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your BrandWordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your Brand
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdf
 

MVC Puree - Approaches to MVC with Umbraco

  • 2. Just to introduce myself… • I’m Andy Butland • I work for Zone, a digital agency where I head up the .NET development team in London • We develop web sites and applications primarily using ASP.Net MVC and Umbraco • Blog (sporadically) at http://web-matters.blogspot.it/ • Find here a copy of slides and links to various resources • Contact: abutland73@gmail.com / @andybutland
  • 3. And what are we talking about… “MVC Purée”? • We’ll be discussing best practices with using MVC in Umbraco: • “For the session title, how about ‘MVC purist’?” • “Hmm, don’t like the sound of that… makes me sound too much of a pedant, and we all have to be pragmatic too.” • “OK… we’ll go with ‘MVC purée’”
  • 4. Contents 1 2 3 6 4 MVC best practices 5 7 Using MVC with Umbraco Strongly typed views and mapping from Umbraco data Cleaning up views Dependency injection Unit testing Wrap up and Q & A
  • 5. 1. MVC best practices
  • 6. Journey to Umbraco MVC “Classic” ASP ASP.Net Webforms ASP.Net MVC Umbraco (XSLT) Umbraco (Razor) Umbraco (MVC) Learnings and best practices…
  • 7. MVC patterns and practices MODEL CONTROLLER VIEW VIEW MODELS Simple, strongly typed views, with no domain logic Custom view models for each view Mapping from domain models to view models Application components supported with unit tests and composed via dependency injection COMPONEN T Separation of concerns
  • 8. 2. Using MVC with Umbraco
  • 9. MVC techniques with Umbraco • Since version 4.10 we’ve been able to use MVC as well as traditional Web Forms for rendering Umbraco templates. • MVC rendering has been implemented in a flexible way, giving a lot of scope to the developer in building their application. • Logic, querying and data access in the views • Using surface controllers actions with partial views • Hijacking routes
  • 10. Logic and querying in the views • This technique is most similar to that used in traditional Umbraco templating and can be used be all developers, not just those using Visual Studio. • It’s not ideal though from a more purist MVC perspective. UMBRACO DEFAULT CONTROLLERRequest TEMPLATE/ VIEWPopulates and passes a standard RenderModel Within the view we can use the Umbraco helper to: • Access page properties • Query for other nodes • Run Examine searches
  • 11. Example: using the Umbraco helper in our view@inherits Umbraco.Web.Mvc.UmbracoTemplatePage <h1>@Model.Content.GetPropertyValue("heading")</h1> @{ var rootNode = Umbraco.TypedContentAtRoot().First(); var categoryNodes = rootNode .Descendants("CategoryFolder") .First() .Children; foreach (var item in categoryNodes) { <div>@item.GetPropertyValue("title")</div> } } Property access not obvious for front-end developers less familiar with Umbraco APIs. The Umbraco helper provides access to functions that arguably should not be the concern of the view.
  • 12. Surface controller actions and partials • We can prepare and pass our own view model by making an @Html.Action call from within our template, to a surface controller that returns a partial. • It’s downside is a more complex request flow, and the necessity of creating two templates. UMBRACO DEFAULT CONTROLLERRequest TEMPLATE/ VIEW SURFACE CONTROLLER ACTION PARTIAL VIEW
  • 13. Example: using a surface controller and partial@inherits Umbraco.Web.Mvc.UmbracoTemplatePage @{ Layout = "_Layout.cshtml"; } @Html.Action("Register", "AccountSurface") [ChildActionOnly] public PartialViewResult Register() { var vm = new RegisterViewModel(); PopulateRegisterViewModel(vm); return PartialView("Register", vm); } @model RegisterViewModel <h1>@Model.Heading</h1> Our Umbraco template view calls out to a surface controller action method… … which populates a custom view model and passes this to a partial view… … that can benefit from strong typing.
  • 14. Route hijacking • We can intercept the request with our own controller, populate a custom view model and pass it to the strongly typed view. • We now have a very clean view with little logic, and proper separation of concerns from an MVC perspective. • There is a little more work to do from a developer perspective, but we can mitigate that… CUSTOM CONTROLLER Request TEMPLATE/ VIEWPopulates and passes a custom view model
  • 15. Example: route hijacking using System.Web.Mvc; using Umbraco.Web.Mvc; public class EventPageController : RenderMvcController { public ActionResult EventPage() { // Populate view model and // render view } } The controller name must match the document type alias, e.g. EventPage The action method name must match the template name BETTER STILL… see Hybrid Framework method of inheriting from SurfaceController and implementing IRenderMvcController
  • 16. 3. Strongly typed views and mapping from Umbraco data
  • 17. Custom view model • We can create a simple POCO class to represent our view • In most cases will represent a single document type • However often we’ll want to pull in data from other nodes, or even other data sources • We’ll map the Umbraco content to our view model • Our view can reference that as it’s @model • It’s now simply displaying properties and collections from the view model, with no business logic or data access • Actually need have no reference to Umbraco at all… • …unless you want it of course. You can still inherit from the Umbraco base page if you need to.
  • 18. Example: custom view model (definition) public class NewsLandingPageViewModel { public string Heading { get; set; } public IHtmlString BodyText { get; set; } public IList<NewsItem> LatestNewsItems { get; set; } } Directly mapped from fields on the document type Generated from a node query
  • 19. Example: custom view model (mapping) public ActionResult NewsLandingPage() { var vm = new NewsLandingPageViewModel { Heading = CurrentPage.GetPropertyValue<string>("heading"), BodyText = CurrentPage.GetPropertyValue<IHtmlString>("bodyText"), LatestNewsItems = CurrentPage.Descendants("NewsItem") .OrderByDescending(x => DateTime.Parse( x.GetPropertyValue<string>(“publicationDate"))) .Take(10) .Select(x => new NewsItem { Heading = CurrentPage.GetPropertyValue<string>("heading"), PublicationDate = DateTime.Parse( x.GetPropertyValue<string>("publicationDate"))), }) .ToList(), }; return View("NewsLandingPage", vm); }
  • 20. Using the “Umbraco Mapper” package • The package has been created to streamline the mapping of Umbraco content to view models • Convention based mapping from single instances and collections of IPublishedContent • Ability to override those conventions for particular properties as required • Mapping from other sources such as XML and JSON • Supply of custom mapping methods for handling custom types
  • 21. Example: mapping using conventions public ActionResult NewsLandingPage() { var vm = new NewsLandingPageViewModel(); var latestNewsNodes = CurrentPage.Descendants("NewsItem") .OrderByDescending(x => DateTime.Parse( x.GetPropertyValue<string>(“publicationDate"))) .Take(10); var mapper = new UmbracoMapper(); mapper.Map(CurrentPage, vm) .MapCollection(latestNewsNodes, vm.LatestNewsItems); return View("NewsLandingPage", vm); }
  • 22. Example: overriding conventions mapper.Map(CurrentPage, vm, new Dictionary<string,PropertyMapping> { { "Copy", new PropertyMapping { SourceProperty = "bodyText", } } }) .MapCollection(latestNewsNodes, vm.LatestNewsItems, new Dictionary<string,PropertyMapping> { { "Category", new PropertyMapping { SourceProperty = "Name", LevelsAbove = 1, } } }); Maps from property with different name Maps from node at a higher level in the tree. COMING SOON… attribute based property mappings
  • 23. Example: a custom mapping (1) public class GeoCoordinate { public decimal Longitude { get; set; } public decimal Latitude { get; set; } public int Zoom { get; set; } } ... var mapper = new UmbracoMapper(); mapper.AddCustomMapping(typeof(GeoCoordinate).FullName, CustomMappings.MapGeoCoordinate);
  • 24. Example: a custom mapping (2) public static object MapGeoCoordinate(IUmbracoMapper mapper, IPublishedContent contentToMapFrom, string propName, bool isRecursive) { var propertyValueAsCsv = contentToMapFrom .GetPropertyValue<string>(propName, isRecursive, null); if (!string.IsNullOrEmpty(propertyValueAsCsv)) { var parts = propertyValueAsCsv.Split(','); if (parts != null && parts.Length == 3) { return new GeoCoordinate { Latitude = decimal.Parse(parts[0]), Longitude = decimal.Parse(parts[1]), Zoom = int.Parse(parts[2]), }; } } return null; }
  • 26. Dependency injection: what and why? • By injecting our dependencies to a class, rather than “newing” them up within one, we: • Program to interfaces – improving the testability of our code • Reduce coupling • Develop components with single responsibilities • An IoC container can then help us with the instantiation of the concrete classes at runtime
  • 27. Example: injecting services to a controller public class HomePageController : BaseController { private readonly IDataService _dataService; public HomePageController(IDataService dataService) { _dataService = dataService; } public ActionResult HomePage() { // Do something with the data service var data = _dataService.GetData(); ... } } An instance of IDataService - as well as any dependencies it may have - is injected into the controller’s constructor at run-time.
  • 28. Example: integrating Ninject with Umbraco PM> Install-Package Ninject.MVC3 Install Ninject from NuGet Creates a file NinjectWebCommon.cs in App_Start private static void RegisterServices(IKernel kernel) { kernel.Bind<IDataService>().To<MyDataService>(); } Any time a component “requests” an IDataService, they’ll get a concrete DataService
  • 30. Isolating our unit under test from dependencies• When unit testing, we aim to confirm the function of a particular method or class, by replacing any dependencies it has with versions that are under the control of our tests • We avoid brittle data or slow running processes • We isolate our tests to just the small piece being examined UNIT UNDER TEST DEPENDENT CLASS DEPENDENT CLASS … which allows us to we replace them with mocks or stubs we control Dependencies are referenced through interfaces…
  • 31. Example: using mocks (with Moq) [TestMethod] public void WebServiceTests_SuccessResponse_ReturnsStatusAndMessage() { // Arrange var service = new WebServiceWrapper(MockHttpClient(), "http://www.example.com/"); // Act var result = service.Request("exchange-rates", ResponseContentType.JSON).Result; // Assert Assert.IsTrue(result.Success); Assert.IsNotNull(result.Response); Assert.IsNull(result.ErrorMessage); } Within the test, we are avoiding calling the external web service directly by mocking the HTTP call
  • 32. Example: using mocks (with Moq) (2) private static IHttpClient MockHttpClient() { var mock = new Mock<IHttpClient>(); mock.Setup(x => x.GetAsync(It.IsAny<string>())) .Returns(Task<HttpResponseMessage>.Factory.StartNew(() => { var response = new HttpResponseMessage(HttpStatusCode.OK); response.Content = new StringContent(@"{ 'rates': [{ 'currencyCode': 'EUR', 'rate': 1.24 }]}";); return response; })); mock.Setup(x => x.GetAsync(It.Is<string>(y => y.Contains("invalid")))) .Returns(Task<HttpResponseMessage>.Factory.StartNew(() => { return new HttpResponseMessage(HttpStatusCode.BadRequest); })); return mock.Object; } At runtime we create our own “HttpClient” by mocking the interface and return fixed responses.
  • 33. Unit testing with Umbraco • We can use various techniques to control our dependencies, e.g. mocks and stubs • Umbraco doesn’t make it particularly easy… • Problems with unit testing surface controllers • Issues with extension and static methods • … but be no means impossible • Avoid the problem - move logic into a separate class, leaving a simple controller of little value to test • Utilise the base test classes • Look into MS Fakes
  • 34. Example: unit testing a surface controller [HttpPost] public ActionResult CreateComment(CommentViewModel model) { if (!ModelState.IsValid) { return CurrentUmbracoPage(); } TempData.Add("CustomMessage", "Thanks for your comment."); return RedirectToCurrentUmbracoPage(); } If validation fails, should return to view. If successful, should have value in TempData and redirect
  • 35. Example: unit testing a surface controller (2) [TestMethod] public void CreateComment_WithValidComment_RedirectsWithMessage() { // Arrange var controller = new BlogPostSurfaceController(); var model = new CommentViewModel { Name = "Fred", Email = "fred@freddie.com", Comment = "Can I test this?", }; // Act var result = controller.CreateComment(model); // Assert Assert.IsNotNull(result); } Will fail, with null reference for UmbracoContext
  • 36. Example: testing a “command handler” class public class BlogPostSurfaceControllerCommandHandler { public ModelStateDictionary ModelState { get; set; } public TempDataDictionary TempData { get; set; } public bool HandleCreateComment(CommentViewModel model) { if (!ModelState.IsValid) { return false; } TempData.Add("CustomMessage", "Thanks for your comment."); return true; } } Logic moved to class with no Umbraco dependency…
  • 37. Example: testing a “command handler” class (2)public class BlogPostSurfaceController : SurfaceController { BlogPostSurfaceControllerCommandHandler _commandHandler; public BlogPostSurfaceController() { _commandHandler = new BlogPostSurfaceControllerCommandHandler(); _commandHandler.ModelState = ModelState; _commandHandler.TempData = TempData; } [HttpPost] public ActionResult CreateCommentWithHandler(CommentViewModel model) { if (!_commandHandler.HandleCreateComment(model)) { return CurrentUmbracoPage(); } return RedirectToCurrentUmbracoPage(); } } … which is referenced in the surface controller… … leaving a thin controller method, with little value for testing.
  • 38. Example: testing a “command handler” class (3)[TestMethod] public void CreateComment_WithValidComment_ReturnsTrueWithMessage() { // Arrange var handler = new BlogPostSurfaceControllerCommandHandler(); handler.ModelState = new ModelStateDictionary(); handler.TempData = new TempDataDictionary(); var model = new CommentViewModel { Name = "Fred", Email = "fred@freddie.com", Comment = "Can I test this?", }; // Act var result = handler.HandleCreateComment(model); // Assert Assert.IsTrue(result); Assert.IsNotNull(handler.TempData["CustomMessage"]); } The logic in the handler though, can now be tested.
  • 39. Using the Umbraco core base test classes • Allow us to test our surface controller without modification: • We need to clone the source code, build and reference the Umbraco.Tests.dll in our project • We have to use NUnit • We then have access to some base classes we can inherit from, to run tests with the appropriate contexts set up • But even then, there are some reflection hoops to jump through • Once done though, we can successfully run tests on Umbraco surface controllers
  • 40. Example: base test classes [TestFixture] [DatabaseTestBehavior(DatabaseBehavior.NoDatabasePerFixture)] public class BlogPostSurfaceControllerTests : BaseRoutingTest { [Test] public void ExampleTest() { var controller = GetController(); var model = new CommentViewModel { Email = "fred@freddie.com", Comment = "Can I test this?", }; var result = controller.CreateComment(model); var redirectResult = result as RedirectToUmbracoPageResult; Assert.IsNotNull(redirectResult); Assert.AreEqual(1000, redirectResult.PublishedContent.Id); Assert.IsNotNull(controller.TempData["CustomMessage"]); } } We can test for specific Umbraco results and other controller actions. Using base class methods and reflection, the controller can be instantiated with necessary contexts. The test class inherits from the Umbraco base test class.
  • 41. Working around issues with extension methods • Some Umbraco methods – particularly on IPublishedContent – are implemented as extension methods • These can’t be mocked or stubbed • Using MS Fakes • We can add a fakes assembly for any dll • And at runtime replace a method’s implementation with one of our own • VS.Net Premium or Ultimate only though 
  • 42. Example: can’t mock IPublishedContent methodprivate static IPublishedContent MockIPublishedContent() { var mock = new Mock<IPublishedContent>(); mock.Setup(x => x.Id).Returns(1000); mock.Setup(x => x.Name).Returns("Test content"); mock.Setup(x => x.CreatorName).Returns("A.N. Editor"); // This won’t work... mock.Setup(x => x.GetPropertyValue(It.IsAny<string>())) .Returns((string alias) => MockIPublishedContentProperty(alias)); return mock.Object; } GetPropertyValue() is an extension method, which can’t be mocked.
  • 43. Example: using Microsoft Fakes [TestMethod] public void MapFromIPublishedContent_MapsCustomProperties() { using (ShimsContext.Create()) { var model = new SimpleViewModel(); var mapper = GetMapper(); var content = new StubPublishedContent(); Umbraco.Web.Fakes.ShimPublishedContentExtensions .GetPropertyValueIPublishedContentStringBoolean = (doc, alias, recursive) => { switch (alias) { case "bodyText": return "This is the body text"; default: return string.Empty; } }; mapper.Map(content, model); Assert.AreEqual("This is the body text", model.BodyText); } } We replace the method implementation with our own at runtime.
  • 44. 6. Cleaning up views
  • 45. Strongly typed partials • Often our HTML will contain blocks of similar layout that are repeated on multiple pages • We can use @Html.Partial to “DRY” up our views • By having our view models implement multiple small interfaces, or by using inheritance, we can strongly type our partial but avoid instantiating objects for each one
  • 46. Example: custom view model for partial public class HeroSectionViewModel { public string Heading { get; set; } public string Standfirst { get; set; } } @model CodegardenSamples.Models.HeroSectionViewModel <h1>@Model.Heading</h1> <p>@Model.Standfirst</p> ... @model CodegardenSamples.Models.HomePageViewModel @Html.Partial("_HeroSection“, new HeroSectionViewModel { Heading = Model.Heading, Standfirst = Model.Heading }) We can strongly type our partials just as we can full page views BUT… we have to handle instantiating this partial's model in our view
  • 47. Example: view models with inheritance public abstract class BaseViewModel { public string Heading { get; set; } public string Standfirst { get; set; } } public class HomePageViewModel : BaseViewModel { } public class ContentPageViewModel : BaseViewModel { } ... @model CodegardenSamples.Models.HomePageViewModel @Html.Partial("_HeroSection") ... @model CodegardenSamples.Models.BaseViewModel <h1>@Model.Heading</h1> <p>@Model.Standfirst</p> As our view models inherit from a base class… … a strongly typed partial can be created that can reference the model of the parent page. BUT… using inheritance in this way can be restrictive – we can only inherit from one base.
  • 48. Example: view models with interfaces (1) public interface IHeroSection { string Heading { get; } string Standfirst { get; } } public interface ISideBar { string SideBarTitle { get; } string SideBarIntro { get; } } public class HomePageViewModel : IHeroSection, ISideBar { public string Heading { get; set; } public string Standfirst { get; set; } public string SideBarTitle { get; set; } public string SideBarIntro { get; set; } } Having our view model implement multiple small interfaces…
  • 49. Example: view models with interfaces (2) @model CodegardenSamples.Models.HomePageViewModel @Html.Partial("_HeroSection") @Html.Partial("_SideBar") ... @model CodegardenSamples.Models.IHeroSection <h1>@Model.Heading</h1> <p>@Model.Standfirst</p> ... @model CodegardenSamples.Models.ISideBar <h2>@Model.SideBarTitle.</h2> <p>@Model.SideBarIntro</p> … again means we can reference the model in a strongly typed manner in the partials.
  • 50. 7. Wrap up and Q &A
  • 51. In summary • No one true way to build an Umbraco site… pick what works for you and your team • If you like the “purist” MVC approach, you can apply these best practices without too much additional effort, and still be “pragmatic” about delivery to your clients
  • 52. Lastly, some thanks… • Neil, Ali, Rob, Raffaele, Nnamdi, Ollie and the rest of my colleagues at Zone • Numerous discussions, questions and advice as we’ve evolved techniques and technologies over the years • Anthony, Darren, Ismail, Jeavon, Jeroen, Shannon, Warren and many others • Blogs, forum threads and other community contributions that have influenced the thinking behind our work and this presentation