EPiServer
Development
I love building beautifull, fast and robust
web sites for high profile customers.
When they have editorial content EPiServer
CMS provides us with a solid foundation to
build upon.
The boring stuff is already in place. We as
developers can focus on delivering business
value from the get-go.
But, I also have a passion for ”agile”
software architecture.
A passion for clean code and flexible
systems.
A passion for studying my field.
A passion for learning about principles and
practices such as the SOLID principles.
But, I also have a passion for ”agile”
software architecture.
A passion for clean code and flexible
systems.
A passion for studying my field.
A passion for learning about principles and
practices such as the SOLID principles.


    ’
’
While EPiServer is mature after a decade of
development it was first released long
before many of us had ever heard about Test
Driven Development or MVC.
While EPiServer is mature after a decade of
development it was first released long
before many of us had ever heard about Test
Driven Development or MVC.
In other words; it’s built in a way that
makes it hard to apply modern
development practices and principles.
’   ’
EPiServer development revolves around
pages, represented as PageData objects.
While these are primarily intended for
editorial content they aren’t limited to that.
In fact we can use them to model just about
anything. Orders, events, comments etc.
When we do that we never have to think
about how to persist them.
EPiServer has done an excellent job at
abstracting the database to the point we’re
we take for granted that saving, reading and
deleting will just work.
We also instantly get an administrative
interface for whatever we have modeled.
And access rights management, support for
multiple language versions, versioning and
so on.
Of course all of these are optimized for
editorial content.
Of course all of these are optimized for
editorial content.
But the fact that we can create just about
anything without having to think about
persistence and get at least some sort of
administrative UI is powerful.
Another strength is its flexibility.
I can often find myself frustrated when I try
to extend the system, but the fact of the
matter is that compared to many other
commercial products it’s highly extendable.
And while I would have preferred a more
open architecture where I could just switch
out or extend standard components there
are extension points for pretty much
everything.
Often our customers have bought EPiServer
for a simple public web site.
But once they’ve bought it, they want us to
use it for everything.
In those cases there are three things that
frustrate especially much.
Ironically, the thing that frustrate me the
most is closely related to the thing that I like
the most – the way page types and thereby
PageData objects are modeled.
In the database through a UI.
The fact that page types are defined in the
database has several negative consequences.
While we’re able to model just about
anything in terms of data, we can’t have
logic, behavior, in our model objects.
The logic still has to go somewhere.
Often it ends up in the presentation layer.
We’re also not able to use inheritance or
polymorphism.
There’s no relationship between different
page types.
Similar data has to be modeled multiple
times. So does changes.
Deployment is painful.
We can’t just deploy code or scripted
database changes. We have to deploy data.
And we have to use magic strings and casts
to access data.
Actively bypassing the first test that the
compiler otherwise provides us with.
The Dependency Inversion Principle states
that our code should “Depend on
abstractions, not on concretions”.
This is essential for truly flexible systems.
And for unit testing.
Unfortunately abstractions are scarce in the
EPiServer API.
The class we use for saving, retrieving and
deleting PageData object, DataFactory, is a
singleton with non-virtual methods.
And it doesn’t implement any interface that
defines many of its members.
This makes it hard to isolate our code from
it, and thereby from the rest of the EPiServer
API, the database and HTTP context.
My final, big frustration with EPiServer
CMS is its tight coupling to Web Forms.
While I’m not convinced that ASP.NET
MVC, or any other MVC framework for that
matter, is a perfect fit for many of the sites
we build on EPiServer, Web Forms severly
limits our ability to create flexible systems
built using Test Driven Development.
Even if the EPiServer API would have had
abstractions, how would we have gotten
concrete implementations of them into our
code?
If we had been using ASP.NET MVC we
could have create a custom controller
factory.
But with Web Forms the framework
instantiates components such as user
controls.
Not to mention that the page life cycle and
on-by-default viewstate functionally isn’t
exactly architectural beauty or embracing
how the web works.
Unfortunately though, the current version of
EPiServer CMS has a few fairly important
features such as XForms and Dynamic
Content that requires Web Forms.
’
In 2009 an open source project called Page
Type Builder was born.
At its core it does two things…
It scans the application domain for classes
that appear to define page types and creates
and updates the corresponding page types
in the database.
We can create page types using code, and
only code. We don’t have to move data
when we’re deploying. Only code.
And we can use inheritance between page
types.
It also listens to events from the EPiServer
API and intercepts requests for PageData
objects.
If a PageData object that is about to be
returned is of a page type that can be
mapped to a class it creates an instance of
that class and copies the data from the
original object.
Finally, it swaps out the object being
returned with the instance of the class.
This means that DataFactory no longer
returns plain PageData objects.
It returns instances of classes that we’ve
created.
Those still inherit from PageData, meaning
that they play nice with the rest of
EPiServer’s functionality.
But they can have methods and logic in
property getters and setters.
public class Article
{
}
using PageTypeBuilder;



public class Article
{
}
using PageTypeBuilder;



public class Article : TypedPageData
{
}
using PageTypeBuilder;

[PageType]
public class Article : TypedPageData
{
}
using PageTypeBuilder;

[PageType]
public class Article : TypedPageData
{
}
’
using PageTypeBuilder;

[PageType]
public class Article : TypedPageData
{
}
using PageTypeBuilder;

[PageType(
    Filename = "~/Templates/Article.aspx")]
public class Article : TypedPageData
{
}
using PageTypeBuilder;

[PageType(
    Filename = "~/Templates/Article.aspx",
    Description = "My page type")]
public class Article : TypedPageData
{
}
using PageTypeBuilder;

[PageType(
    Filename = "~/Templates/Article.aspx",
    Description = "My page type")]
public class Article : TypedPageData
{
}
using PageTypeBuilder;

[PageType]
public class Article : TypedPageData
{
}
using PageTypeBuilder;

[PageType]
public class Article : TypedPageData
{
    public virtual string MainBody { get; set; }
}
using PageTypeBuilder;

[PageType]
public class Article : TypedPageData
{
    [PageTypeProperty]
    public virtual string MainBody { get; set; }
}
using PageTypeBuilder;

[PageType]
public class Article : TypedPageData
{
    [PageTypeProperty]
    public virtual string MainBody { get; set; }
}




                  ’
using PageTypeBuilder;

[PageType]
public class Article : TypedPageData
{
    [PageTypeProperty(Type = typeof(PropertyString))]
    public virtual string MainBody { get; set; }
}
get
{
      return this.GetPropertyValue(p => p.MainBody);
}

set
{
      this.SetPropertyValue(p => p.MainBody, value);
}
public partial class ArticlePage
    : TemplatePage
{
}




                ’
using PageTypeBuilder.UI;

public partial class ArticlePage
    : TemplatePage<Article>
{
}




                ’
using PageTypeBuilder.UI;

public partial class ArticlePage
    : TemplatePage<Article>
{
    protected override void OnLoad(EventArgs e)
    {
           var bodyText = CurrentPage.MainBody;
    }
}
<html>
    <body>
           <%= CurrentPage.MainBody %>
    </body>
</html>
Our page types can inherit base classes and
implement interfaces.
We’re able to use polymorphism.
One page type can return it’s name as link
text in menus while another can return
something completely different.
While the code that renders the menu only
knows that they both implement
IMenuItem.
Our page types can have behavior as well as
data.
They can have methods.
Properties can have logic in their getters and
setters.
We can build page providers that utilize
that we’re dealing with different classes.
So far I’ve heard about page providers built
using Fluent NHibernate and Raven DB.
We can query using LINQ to Objects in a
strongly typed way.
And we can serialize our pages when they
are saved and push them over to a search
server, to later query for them using a
strongly typed, LINQ-like API.
EPiAbstractions is an open source project
that wraps EPiServer’s API.
It provides concrete implementations,
facades or adapters, that delegate to
EPiServer’s classes.
These in turn implement interfaces.
Allowing our code to depend on
abstractions.
It also provides a simplified API that
doesn’t map directly to EPiServer’s but is
easier to work with.
For this API it also provides in-memory-
implementations, allowing us to write
production like code in tests.
Finally it also provides factory classes for
creating test data.
’
[PageType]
public class Article : TypedPageData
{
    public IEnumerable<Comment> GetComments(int max)
    {
           return DataFactory.Instance
                  .GetChildren(PageLink)
                  .OfType<Comment>()
                  .Take(max);
    }
}
[PageType]
public class Article : TypedPageData
{
    public IEnumerable<Comment> GetComments(int max)
    {
           return DataFactory.Instance
                  .GetChildren(PageLink)
                  .OfType<Comment>()
                  .Take(max);
    }
}            ’
[PageType]
public class Article : TypedPageData
{
    IDataFactoryFacade dataFactory;
    public IEnumerable<Comment> GetComments(int max)
    {
           return dataFactory
                  .GetChildren(PageLink)
                  .OfType<Comment>()
                  .Take(max);
    }
}


                                    ’
’
protected void Application_Start(...)
{
    var container = new Container();




}
protected void Application_Start(...)
{
    var container = new Container();
    container.Configure(x =>
       x.For<IDataFactoryFacade>()
        .Singleton()
        .Use<DataFactoryFacade>());




}
protected void Application_Start(...)
{
    var container = new Container();
    container.Configure(x =>
       x.For<IDataFactoryFacade>()
        .Singleton()
        .Use<DataFactoryFacade>());

    PageTypeResolver.Instance.Activator
       = new StructureMapTypedPageActivator(container);
}
[PageType]
public class Article : TypedPageData
{
    IDataFactoryFacade dataFactory;
    public Article(IDataFactoryFacade dataFactory)
    {
           this.dataFactory = dataFactory;
    }

    public IEnumerable<Comment> GetComments(int max)
    {
           return dataFactory
                  .GetChildren(PageLink)
                  .OfType<Comment>()
                  .Take(max);
    }
}
[PageType]
public class Article : TypedPageData
{
    IPageRepository pageRepository;
    public Article(IPageRepository pageRepository)
    {
           this.pageRepository = pageRepository;
    }

    public IEnumerable<Comment> GetComments(int max)
    {
           return pageRepository
                  .GetChildren(PageLink)
                  .OfType<Comment>()
                  .Take(max);
    }
}
[PageType]
public class Article : TypedPageData
{
    IPageRepository pageRepository;
    public Article(IPageRepository pageRepository)
    {
           this.pageRepository = pageRepository;
    }

    public IEnumerable<Comment> GetComments(int max)
    {
           return pageRepository
                  .GetChildren<Comment>(PageLink)
                  .Take(max);
    }
}
[Test]
public void GetComments()
{
}
[Test]
public void GetComments()
{
    var epiContext = FakeEPiServerContext.Create();




}
[Test]
public void GetComments()
{
    var epiContext = FakeEPiServerContext.Create();

    var article = CreatePage.OfType<Article>(
           epiContext.PageRepository);




}
[Test]
public void GetComments()
{
    var epiContext = FakeEPiServerContext.Create();

    var article = CreatePage.OfType<Article>();

    var articleReference = epiContext.PageRepository
           .Publish(article);




}
[Test]
public void GetComments()
{
    var epiContext = FakeEPiServerContext.Create();

    var article = CreatePage.OfType<Article>();

    var articleReference = epiContext.PageRepository
           .Publish(article);

    article = epiContext.PageRepository
           .GetPage<Article>(articleReference);
}
[Test]
public void GetComments()
{
    ...

    var comments = CreateSetOfPageData
           .Containing(11)
           .PagesOfType<Comment>()
           .ChildrenOf(article);

    epiContext.PageRepository
           .Publish(comments.Cast<PageData>());
}
[Test]
public void GetComments()
{
    ...

    int max = 10;
    comments = article.GetComments(max);




}
[Test]
public void GetComments()
{
    ...

    int max = 10;
    comments = article.GetComments(max);

    Assert.AreEqual(max, comments.Count());




}
In cases where we’re building complex
systems, or a particularly complex part of a
system, we can address the Web Forms
problem by applying the Model View
Presenter Pattern.
Model View Presenter, or MVP, is a dialect
of the Model View Controller. It has one
significant difference compared to MVC; UI
events are routed to the view instead of to
the controller as in MVC.
While it’s fairly easy to create a basic
implementation of MVP with Web Forms
there are a number of projects that can help
us. One of them is Web Forms MVP.
Given that we’re using Web Forms MVP we
can use the open source project EPiMVP
which provides some ready-to-use
integration with EPiServer.
When using Web Forms MVP and EPiMVP
there are four basic components that interact
when rendering an ASPX page or a user
control; A view (an ASPX or ASCX), a
model object (the PageData object), a
presenter and a view model.
When a request comes in to a page the Web
Forms MVP framework instantiates a
presenter and view model object.
The view, which implements an interface
which defines members that are relevant for
the presenter, is passed to the presenter.
So is the view model object.
The presenter is also aware of the PageData
object.
With access to the view, view model and
PageData object the presenter populates the
view model object with data relevant for
rendering in the view.
It can also interact with the view by
subscribing to events exposed by it or by
calling methods on it.
public class CommentsModel
{
}




                             ’
public interface ICommentsView :
    IEPiView<CommentsModel>
{
    event EventHandler<SubmitCommentEventArgs>
           SubmitComment;
}




                                                 ’
public class SubmitCommentEventArgs : EventArgs
{
    public string AuthorName { get; private set; }
    public string Text { get; private set; }

    public SubmitCommentEventArgs(
           string authorName, string text)
    {
           AuthorName = authorName;
           Text = text;
    }
}
public class CommentsPresenter :
    EPiPresenter<ICommentsView, Article>
{
}




                  ’
public CommentsPresenter(ICommentsView view,
    Article pageData)
    : base(view, pageData)
{
}
private IPageRepository pageRepository;

public CommentsPresenter(ICommentsView view,
    Article pageData,
    IPageRepository pageRepository)
    : base(view, pageData)
{
    this.pageRepository = pageRepository;
}


               ’
protected void Application_Start(...)
{
    var container = ...




}
protected void Application_Start(...)
{
    var container = ...

    PresenterBinder.Factory
       = new StructureMapPresenterFactory(container);




                ’
}
private IPageRepository pageRepository;

public CommentsPresenter(ICommentsView view,
    Article pageData,
    IPageRepository pageRepository)
    : base(view, pageData)
{
    this.pageRepository = pageRepository;
    view.SubmitComment += SubmitComment;
}

                                               ’
                     ’
void SubmitComment(object sender,
    SubmitCommentEventArgs e)
{




}
void SubmitComment(object sender,
    SubmitCommentEventArgs e)
{
    var comment = pageRepository
           .GetDefaultPageData<Comment>(
                  CurrentPage.PageLink);




}
void SubmitComment(object sender,
    SubmitCommentEventArgs e)
{
    var comment = pageRepository
           .GetDefaultPageData<Comment>(
                  CurrentPage.PageLink);
    comment.Author = e.AuthorName;
    comment.Text = e.Text;
    pageRepository.Publish(comment);
}
void SubmitComment(object sender,
    SubmitCommentEventArgs e)
{
    var comment = pageRepository
           .GetDefaultPageData<Comment>(
                  CurrentPage.PageLink);
    comment.Author = e.AuthorName;
    comment.Text = e.Text;
    pageRepository.Publish(comment);

    HttpContext.Response.Redirect(CurrentPage.LinkURL);
}
[PresenterBinding(typeof(CommentsPresenter))]
public partial class Comments :
    EPiMvpUserControl<CommentsModel>,
    ICommentsView
{
    public event EventHandler<SubmitCommentEventArgs>
           SubmitComment;
}
<fieldset>
   <asp:TextBox ID="CommentAuthor"
    TextMode="SingleLine" runat="server" />

   <asp:TextBox ID="CommentText" TextMode="MultiLine"
    runat="server" />

   <asp:Button OnClick="SubmitComment_Click"
    Text="Post Comment" runat="server" />
</fieldset>
protected void SubmitComment_Click(object sender,
    EventArgs e)
{
   if (SubmitComment == null)
      return;

    SubmitComment(this,
       new SubmitCommentEventArgs(
          CommentAuthor.Text,
          CommentText.Text));
}
We’ve seen three frameworks that help us
address some of the core problems we may
face as progressive developers using
EPiServer.
With these in our toolbox we’re better
equipped to tackle complexity when
needed.
Remember to choose the right tools for the
job. Be pragmatic.
Page Type Builder can be used in all
situations and is likely to have the biggest
impact on your overall experience using
EPiServer.
EPiAbstractions allow you to create unit
tests and to use Test Driven Development.
But it can also be misused. EPiServer CMS
and Web Forms wasn’t designed for you to
have 100% code coverage.
Don’t even try.
Use it when drive good design of complex
systems.
Web Forms MVP and EPiMVP probably
seems like it’s complex and hard to
understand.
It is.
But it can be a powerful tool to use when
you have complex user interactions.
It’s also great for being able to inject
dependencies into code that you want to
test.
’
http://pagetypebuilder.codeplex.com
http://epiabstractions.codeplex.com
https://github.com/joelabrahamsson/EPiServer-MVP
http://webformsmvp.com/
http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod


Hand #1: http://www.flickr.com/photos/teacherafael/2243271306/ (with permission)
Hand #2: http://www.flickr.com/photos/fabianluque/5220694009/ (with permission)
Fire: http://www.flickr.com/photos/olemartin/3951562058/
Up/down: http://www.istockphoto.com/stock-photo-4700763-up-down-good-bad-future-past.php
Concrete plant: http://www.istockphoto.com/stock-photo-6416445-young-plant-taking-root-on-a-concrete-footpath.php
Potter: http://www.istockphoto.com/stock-photo-6791142-potter.php
Hands in chains: http://www.istockphoto.com/stock-photo-11441892-dependency.php
Enterprise D: http://www.flickr.com/photos/elzey/523568670/
Chain: http://www.istockphoto.com/stock-photo-2135970-breaking-free.php
Viewstate t-shirt: http://www.flickr.com/photos/piyo/3742166635/
Heart: http://www.flickr.com/photos/saraalfred/3199313309/

Progressive EPiServer Development

  • 1.
  • 4.
    I love buildingbeautifull, fast and robust web sites for high profile customers. When they have editorial content EPiServer CMS provides us with a solid foundation to build upon. The boring stuff is already in place. We as developers can focus on delivering business value from the get-go.
  • 5.
    But, I alsohave a passion for ”agile” software architecture. A passion for clean code and flexible systems. A passion for studying my field. A passion for learning about principles and practices such as the SOLID principles.
  • 6.
    But, I alsohave a passion for ”agile” software architecture. A passion for clean code and flexible systems. A passion for studying my field. A passion for learning about principles and practices such as the SOLID principles. ’
  • 7.
  • 8.
    While EPiServer ismature after a decade of development it was first released long before many of us had ever heard about Test Driven Development or MVC.
  • 9.
    While EPiServer ismature after a decade of development it was first released long before many of us had ever heard about Test Driven Development or MVC. In other words; it’s built in a way that makes it hard to apply modern development practices and principles.
  • 11.
  • 12.
    EPiServer development revolvesaround pages, represented as PageData objects. While these are primarily intended for editorial content they aren’t limited to that.
  • 13.
    In fact wecan use them to model just about anything. Orders, events, comments etc. When we do that we never have to think about how to persist them. EPiServer has done an excellent job at abstracting the database to the point we’re we take for granted that saving, reading and deleting will just work.
  • 14.
    We also instantlyget an administrative interface for whatever we have modeled. And access rights management, support for multiple language versions, versioning and so on. Of course all of these are optimized for editorial content.
  • 15.
    Of course allof these are optimized for editorial content. But the fact that we can create just about anything without having to think about persistence and get at least some sort of administrative UI is powerful.
  • 16.
    Another strength isits flexibility. I can often find myself frustrated when I try to extend the system, but the fact of the matter is that compared to many other commercial products it’s highly extendable. And while I would have preferred a more open architecture where I could just switch out or extend standard components there are extension points for pretty much everything.
  • 21.
    Often our customershave bought EPiServer for a simple public web site. But once they’ve bought it, they want us to use it for everything. In those cases there are three things that frustrate especially much.
  • 23.
    Ironically, the thingthat frustrate me the most is closely related to the thing that I like the most – the way page types and thereby PageData objects are modeled. In the database through a UI. The fact that page types are defined in the database has several negative consequences.
  • 24.
    While we’re ableto model just about anything in terms of data, we can’t have logic, behavior, in our model objects. The logic still has to go somewhere. Often it ends up in the presentation layer.
  • 25.
    We’re also notable to use inheritance or polymorphism. There’s no relationship between different page types. Similar data has to be modeled multiple times. So does changes.
  • 26.
    Deployment is painful. Wecan’t just deploy code or scripted database changes. We have to deploy data. And we have to use magic strings and casts to access data. Actively bypassing the first test that the compiler otherwise provides us with.
  • 28.
    The Dependency InversionPrinciple states that our code should “Depend on abstractions, not on concretions”. This is essential for truly flexible systems. And for unit testing. Unfortunately abstractions are scarce in the EPiServer API.
  • 29.
    The class weuse for saving, retrieving and deleting PageData object, DataFactory, is a singleton with non-virtual methods. And it doesn’t implement any interface that defines many of its members. This makes it hard to isolate our code from it, and thereby from the rest of the EPiServer API, the database and HTTP context.
  • 31.
    My final, bigfrustration with EPiServer CMS is its tight coupling to Web Forms. While I’m not convinced that ASP.NET MVC, or any other MVC framework for that matter, is a perfect fit for many of the sites we build on EPiServer, Web Forms severly limits our ability to create flexible systems built using Test Driven Development.
  • 32.
    Even if theEPiServer API would have had abstractions, how would we have gotten concrete implementations of them into our code? If we had been using ASP.NET MVC we could have create a custom controller factory. But with Web Forms the framework instantiates components such as user controls.
  • 33.
    Not to mentionthat the page life cycle and on-by-default viewstate functionally isn’t exactly architectural beauty or embracing how the web works. Unfortunately though, the current version of EPiServer CMS has a few fairly important features such as XForms and Dynamic Content that requires Web Forms.
  • 34.
  • 35.
    In 2009 anopen source project called Page Type Builder was born. At its core it does two things…
  • 36.
    It scans theapplication domain for classes that appear to define page types and creates and updates the corresponding page types in the database. We can create page types using code, and only code. We don’t have to move data when we’re deploying. Only code. And we can use inheritance between page types.
  • 37.
    It also listensto events from the EPiServer API and intercepts requests for PageData objects. If a PageData object that is about to be returned is of a page type that can be mapped to a class it creates an instance of that class and copies the data from the original object. Finally, it swaps out the object being returned with the instance of the class.
  • 38.
    This means thatDataFactory no longer returns plain PageData objects. It returns instances of classes that we’ve created. Those still inherit from PageData, meaning that they play nice with the rest of EPiServer’s functionality. But they can have methods and logic in property getters and setters.
  • 40.
  • 41.
  • 42.
    using PageTypeBuilder; public classArticle : TypedPageData { }
  • 43.
  • 44.
  • 45.
  • 48.
  • 49.
    using PageTypeBuilder; [PageType( Filename = "~/Templates/Article.aspx")] public class Article : TypedPageData { }
  • 50.
    using PageTypeBuilder; [PageType( Filename = "~/Templates/Article.aspx", Description = "My page type")] public class Article : TypedPageData { }
  • 51.
    using PageTypeBuilder; [PageType( Filename = "~/Templates/Article.aspx", Description = "My page type")] public class Article : TypedPageData { }
  • 54.
  • 55.
    using PageTypeBuilder; [PageType] public classArticle : TypedPageData { public virtual string MainBody { get; set; } }
  • 56.
    using PageTypeBuilder; [PageType] public classArticle : TypedPageData { [PageTypeProperty] public virtual string MainBody { get; set; } }
  • 57.
    using PageTypeBuilder; [PageType] public classArticle : TypedPageData { [PageTypeProperty] public virtual string MainBody { get; set; } } ’
  • 58.
    using PageTypeBuilder; [PageType] public classArticle : TypedPageData { [PageTypeProperty(Type = typeof(PropertyString))] public virtual string MainBody { get; set; } }
  • 60.
    get { return this.GetPropertyValue(p => p.MainBody); } set { this.SetPropertyValue(p => p.MainBody, value); }
  • 61.
    public partial classArticlePage : TemplatePage { } ’
  • 62.
    using PageTypeBuilder.UI; public partialclass ArticlePage : TemplatePage<Article> { } ’
  • 63.
    using PageTypeBuilder.UI; public partialclass ArticlePage : TemplatePage<Article> { protected override void OnLoad(EventArgs e) { var bodyText = CurrentPage.MainBody; } }
  • 64.
    <html> <body> <%= CurrentPage.MainBody %> </body> </html>
  • 66.
    Our page typescan inherit base classes and implement interfaces. We’re able to use polymorphism. One page type can return it’s name as link text in menus while another can return something completely different. While the code that renders the menu only knows that they both implement IMenuItem.
  • 67.
    Our page typescan have behavior as well as data. They can have methods. Properties can have logic in their getters and setters.
  • 68.
    We can buildpage providers that utilize that we’re dealing with different classes. So far I’ve heard about page providers built using Fluent NHibernate and Raven DB.
  • 69.
    We can queryusing LINQ to Objects in a strongly typed way. And we can serialize our pages when they are saved and push them over to a search server, to later query for them using a strongly typed, LINQ-like API.
  • 71.
    EPiAbstractions is anopen source project that wraps EPiServer’s API. It provides concrete implementations, facades or adapters, that delegate to EPiServer’s classes. These in turn implement interfaces. Allowing our code to depend on abstractions.
  • 72.
    It also providesa simplified API that doesn’t map directly to EPiServer’s but is easier to work with. For this API it also provides in-memory- implementations, allowing us to write production like code in tests. Finally it also provides factory classes for creating test data.
  • 74.
  • 75.
    [PageType] public class Article: TypedPageData { public IEnumerable<Comment> GetComments(int max) { return DataFactory.Instance .GetChildren(PageLink) .OfType<Comment>() .Take(max); } }
  • 76.
    [PageType] public class Article: TypedPageData { public IEnumerable<Comment> GetComments(int max) { return DataFactory.Instance .GetChildren(PageLink) .OfType<Comment>() .Take(max); } } ’
  • 77.
    [PageType] public class Article: TypedPageData { IDataFactoryFacade dataFactory; public IEnumerable<Comment> GetComments(int max) { return dataFactory .GetChildren(PageLink) .OfType<Comment>() .Take(max); } } ’
  • 78.
  • 80.
    protected void Application_Start(...) { var container = new Container(); }
  • 81.
    protected void Application_Start(...) { var container = new Container(); container.Configure(x => x.For<IDataFactoryFacade>() .Singleton() .Use<DataFactoryFacade>()); }
  • 82.
    protected void Application_Start(...) { var container = new Container(); container.Configure(x => x.For<IDataFactoryFacade>() .Singleton() .Use<DataFactoryFacade>()); PageTypeResolver.Instance.Activator = new StructureMapTypedPageActivator(container); }
  • 83.
    [PageType] public class Article: TypedPageData { IDataFactoryFacade dataFactory; public Article(IDataFactoryFacade dataFactory) { this.dataFactory = dataFactory; } public IEnumerable<Comment> GetComments(int max) { return dataFactory .GetChildren(PageLink) .OfType<Comment>() .Take(max); } }
  • 85.
    [PageType] public class Article: TypedPageData { IPageRepository pageRepository; public Article(IPageRepository pageRepository) { this.pageRepository = pageRepository; } public IEnumerable<Comment> GetComments(int max) { return pageRepository .GetChildren(PageLink) .OfType<Comment>() .Take(max); } }
  • 86.
    [PageType] public class Article: TypedPageData { IPageRepository pageRepository; public Article(IPageRepository pageRepository) { this.pageRepository = pageRepository; } public IEnumerable<Comment> GetComments(int max) { return pageRepository .GetChildren<Comment>(PageLink) .Take(max); } }
  • 87.
  • 88.
    [Test] public void GetComments() { var epiContext = FakeEPiServerContext.Create(); }
  • 89.
    [Test] public void GetComments() { var epiContext = FakeEPiServerContext.Create(); var article = CreatePage.OfType<Article>( epiContext.PageRepository); }
  • 90.
    [Test] public void GetComments() { var epiContext = FakeEPiServerContext.Create(); var article = CreatePage.OfType<Article>(); var articleReference = epiContext.PageRepository .Publish(article); }
  • 91.
    [Test] public void GetComments() { var epiContext = FakeEPiServerContext.Create(); var article = CreatePage.OfType<Article>(); var articleReference = epiContext.PageRepository .Publish(article); article = epiContext.PageRepository .GetPage<Article>(articleReference); }
  • 92.
    [Test] public void GetComments() { ... var comments = CreateSetOfPageData .Containing(11) .PagesOfType<Comment>() .ChildrenOf(article); epiContext.PageRepository .Publish(comments.Cast<PageData>()); }
  • 93.
    [Test] public void GetComments() { ... int max = 10; comments = article.GetComments(max); }
  • 94.
    [Test] public void GetComments() { ... int max = 10; comments = article.GetComments(max); Assert.AreEqual(max, comments.Count()); }
  • 96.
    In cases wherewe’re building complex systems, or a particularly complex part of a system, we can address the Web Forms problem by applying the Model View Presenter Pattern. Model View Presenter, or MVP, is a dialect of the Model View Controller. It has one significant difference compared to MVC; UI events are routed to the view instead of to the controller as in MVC.
  • 98.
    While it’s fairlyeasy to create a basic implementation of MVP with Web Forms there are a number of projects that can help us. One of them is Web Forms MVP. Given that we’re using Web Forms MVP we can use the open source project EPiMVP which provides some ready-to-use integration with EPiServer.
  • 99.
    When using WebForms MVP and EPiMVP there are four basic components that interact when rendering an ASPX page or a user control; A view (an ASPX or ASCX), a model object (the PageData object), a presenter and a view model.
  • 101.
    When a requestcomes in to a page the Web Forms MVP framework instantiates a presenter and view model object. The view, which implements an interface which defines members that are relevant for the presenter, is passed to the presenter. So is the view model object. The presenter is also aware of the PageData object.
  • 104.
    With access tothe view, view model and PageData object the presenter populates the view model object with data relevant for rendering in the view. It can also interact with the view by subscribing to events exposed by it or by calling methods on it.
  • 107.
  • 108.
    public interface ICommentsView: IEPiView<CommentsModel> { event EventHandler<SubmitCommentEventArgs> SubmitComment; } ’
  • 109.
    public class SubmitCommentEventArgs: EventArgs { public string AuthorName { get; private set; } public string Text { get; private set; } public SubmitCommentEventArgs( string authorName, string text) { AuthorName = authorName; Text = text; } }
  • 110.
    public class CommentsPresenter: EPiPresenter<ICommentsView, Article> { } ’
  • 111.
    public CommentsPresenter(ICommentsView view, Article pageData) : base(view, pageData) { }
  • 112.
    private IPageRepository pageRepository; publicCommentsPresenter(ICommentsView view, Article pageData, IPageRepository pageRepository) : base(view, pageData) { this.pageRepository = pageRepository; } ’
  • 113.
  • 114.
    protected void Application_Start(...) { var container = ... PresenterBinder.Factory = new StructureMapPresenterFactory(container); ’ }
  • 115.
    private IPageRepository pageRepository; publicCommentsPresenter(ICommentsView view, Article pageData, IPageRepository pageRepository) : base(view, pageData) { this.pageRepository = pageRepository; view.SubmitComment += SubmitComment; } ’ ’
  • 116.
    void SubmitComment(object sender, SubmitCommentEventArgs e) { }
  • 117.
    void SubmitComment(object sender, SubmitCommentEventArgs e) { var comment = pageRepository .GetDefaultPageData<Comment>( CurrentPage.PageLink); }
  • 118.
    void SubmitComment(object sender, SubmitCommentEventArgs e) { var comment = pageRepository .GetDefaultPageData<Comment>( CurrentPage.PageLink); comment.Author = e.AuthorName; comment.Text = e.Text; pageRepository.Publish(comment); }
  • 119.
    void SubmitComment(object sender, SubmitCommentEventArgs e) { var comment = pageRepository .GetDefaultPageData<Comment>( CurrentPage.PageLink); comment.Author = e.AuthorName; comment.Text = e.Text; pageRepository.Publish(comment); HttpContext.Response.Redirect(CurrentPage.LinkURL); }
  • 120.
    [PresenterBinding(typeof(CommentsPresenter))] public partial classComments : EPiMvpUserControl<CommentsModel>, ICommentsView { public event EventHandler<SubmitCommentEventArgs> SubmitComment; }
  • 121.
    <fieldset> <asp:TextBox ID="CommentAuthor" TextMode="SingleLine" runat="server" /> <asp:TextBox ID="CommentText" TextMode="MultiLine" runat="server" /> <asp:Button OnClick="SubmitComment_Click" Text="Post Comment" runat="server" /> </fieldset>
  • 122.
    protected void SubmitComment_Click(objectsender, EventArgs e) { if (SubmitComment == null) return; SubmitComment(this, new SubmitCommentEventArgs( CommentAuthor.Text, CommentText.Text)); }
  • 123.
    We’ve seen threeframeworks that help us address some of the core problems we may face as progressive developers using EPiServer. With these in our toolbox we’re better equipped to tackle complexity when needed. Remember to choose the right tools for the job. Be pragmatic.
  • 124.
    Page Type Buildercan be used in all situations and is likely to have the biggest impact on your overall experience using EPiServer.
  • 125.
    EPiAbstractions allow youto create unit tests and to use Test Driven Development. But it can also be misused. EPiServer CMS and Web Forms wasn’t designed for you to have 100% code coverage. Don’t even try. Use it when drive good design of complex systems.
  • 126.
    Web Forms MVPand EPiMVP probably seems like it’s complex and hard to understand. It is. But it can be a powerful tool to use when you have complex user interactions. It’s also great for being able to inject dependencies into code that you want to test.
  • 127.
  • 128.
    http://pagetypebuilder.codeplex.com http://epiabstractions.codeplex.com https://github.com/joelabrahamsson/EPiServer-MVP http://webformsmvp.com/ http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod Hand #1: http://www.flickr.com/photos/teacherafael/2243271306/(with permission) Hand #2: http://www.flickr.com/photos/fabianluque/5220694009/ (with permission) Fire: http://www.flickr.com/photos/olemartin/3951562058/ Up/down: http://www.istockphoto.com/stock-photo-4700763-up-down-good-bad-future-past.php Concrete plant: http://www.istockphoto.com/stock-photo-6416445-young-plant-taking-root-on-a-concrete-footpath.php Potter: http://www.istockphoto.com/stock-photo-6791142-potter.php Hands in chains: http://www.istockphoto.com/stock-photo-11441892-dependency.php Enterprise D: http://www.flickr.com/photos/elzey/523568670/ Chain: http://www.istockphoto.com/stock-photo-2135970-breaking-free.php Viewstate t-shirt: http://www.flickr.com/photos/piyo/3742166635/ Heart: http://www.flickr.com/photos/saraalfred/3199313309/