Setup N2Cms on an Asp.Net MVC

The following are my notes on setting up a n2cms site starting with a default MVC project and copying in the needed stuff from the Example_Mvc sample from the n2cms site.

Starting with a new MVC site. Reference the dll’s in the \Example_Mvc\Mvc\wwwroot\Bin folder. I copied them to an externals folder under my solution and referenced them there.

Global.asax

  1. Add IEngine parameter to the RegisterRoutes method.

  2. Add a new ContentRoute to the RouteCollection.

  3. Add an ignore for .ashx routes.

  4. Initialize the N2 engine and pass to the RegisterRoutes in the Application_Start()

  5. Override Init and attach this to the EventRroker instance.

 

    public class MvcApplication : System.Web.HttpApplication

    {

        public static void RegisterRoutes(RouteCollection routes, IEngine engine)

        {

            routes.IgnoreRoute("{resource}.axd/{pathInfo}"</span>);</p>

            routes.IgnoreRoute("{resource}.ashx/{</em>pathInfo}");

 

            // This route detects content item paths and executes their controller

            routes.Add(new ContentRoute(engine));

 

            routes.MapRoute(

                "Default",                                              // Route name

                "{controller}/{action}/{id}",                           // URL with parameters

                new { controller = "Home", action = "Index", id = ""// Parameter defaults

            );

 

        }

 

        protected void Application_Start()

        {

            // normally the engine is initialized by the initializer module but it can also be initialized this programmatically

            // since we attach programmatically we need to associate the event broker with a http application

            IEngine engine = N2.Context.Initialize(false);

 

            RegisterRoutes(RouteTable.Routes, engine);

        }

 

        public override void Init()

        {

            EventBroker.Instance.Attach(this);

            base.Init();

        }

    }

</p></div>

Web.config

Add the n2 config sections.

 

    <sectionGroup name="n2" type="N2.Configuration.SectionGroup, N2">

      <section name="host" type="N2.Configuration.HostSection, N2" requirePermission="false"/>

      <section name="engine" type="N2.Configuration.EngineSection, N2" requirePermission="false"/>

      <section name="database" type="N2.Configuration.DatabaseSection, N2" requirePermission="false"/>

      <section name="edit" type="N2.Configuration.EditSection, N2" requirePermission="false"/>

    </sectionGroup>

</p>

Change the connection string name to N2CMS

Add the n2 section.

<n2>

    <!— If you install a database from scrach you’ll need to insert some required pages. This can be done by the web based installer located at http://yoursite/install/edit —>

    <host rootID="1" startPageID="1">

      <web extension="" rewrite="None">

        <urls enableCaching="false"/>

      </web>

    </host>

    <engine>

      <assemblies>

        <!— These are only needed for medium trust

       

       

        —>

      </assemblies>

    </engine>

    <!— Other flavours: SqlServer2005, SqlServer2000, MySql, SqLite —>

    <database connectionStringName="N2CMS" flavour="SqlServer2005"/>

    <edit>

      <installer checkInstallationStatus="true"/>

    </edit>

  </n2>

</p>

Add expressionBuilders to the system.web compilation section.

 

      <expressionBuilders>

        <add expressionPrefix="CurrentItem" type="N2.Web.Compilation.CurrentItemExpressionBuilder, N2"/>

        <add expressionPrefix="CurrentPage" type="N2.Web.Compilation.CurrentPageExpressionBuilder, N2"/>

        <add expressionPrefix="Code" type="N2.Web.Compilation.CodeExpressionBuilder, N2"/>

        <add expressionPrefix="StartPage" type="N2.Web.Compilation.StartPageExpressionBuilder, N2"/>

        <add expressionPrefix="HasValue" type="N2.Web.Compilation.HasValueExpressionBuilder, N2"/>

      </expressionBuilders>

</p>

Change the forms authentication section.

      <forms loginUrl="edit/login.aspx" protection="All" timeout="30000" path="/">

      </forms>

</p>

Add the n2 tag prefix to the pages controls section.

        <add tagPrefix="n2" assembly="N2" namespace="N2.Web.UI.WebControls"/>

</p>

Add the n2.ashx httpHandler

      <add path=".n2.ashx</span>" verb="</em>" type="N2.Web.AjaxRequestHandler, N2" /></p> </p></div>

Add a handler to the system.webServer handlers section.

      <add name="n2.ajax" path=".n2.ashx</span>" verb="</em>" type="N2.Web.AjaxRequestHandler, N2" /></p> </p></div>

The Start and Root Controller

Now we need to add a a ContentController class to the Controllers folder. And an AbstractPage and ContentPage to the Models folder. And a Content folder and DefaultView.aspx page the Views\Content folder. This will become the start and root nodes for the N2 CMS.

Models\AbstractPage.cs

using N2;

using N2.Details;

 

namespace Red27.Site.Models

{

    [WithEditableTitle, WithEditableName]

    public class AbstractPage : ContentItem, INode

    {

        public string PreviewUrl

        {

            get { return Url; }

        }

    }

}

</p>

Models\ContentPage.cs

using N2;

using N2.Details;

 

namespace Red27.Site.Models

{

    [Definition("Content Page", Installer = N2.Installation.InstallerHint.PreferredStartPage)]

    public class ContentPage : AbstractPage

    {

        [EditableFreeTextArea("Text", 100)]

        public virtual string Text

        {

            get { return (string)(GetDetail("Text") ?? string.Empty); }

            set { SetDetail("Text", value, string.Empty); }

        }

 

        public override string TemplateUrl

        {

            get { return "/Views/Content/DefaultView.aspx"; }

        }

    }

}

</p>

Controllers\ContentController.cs

using N2.Web;

using Red27.Site.Models;

using N2.Web.Mvc;

 

namespace Red27.Site.Controllers

{

    [Controls(typeof(AbstractPage))]

    public class ContentController : ContentController<AbstractPage>

    {

    }

}

</p>

Views\Content\DefaultView.aspx

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" AutoEventWireup="true"

    Inherits="ViewPage"</span> %> </p>

 

<%@ Import Namespace="Red27.Site.Models" %>

<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">

    <h1>

        <%= Model.Title %></h1>

    <%= Model.Text %>

</asp:Content>

</p> </div>

Initial DB Setup

Copy the Edit directory from Example_Mvc\Mvc\wwwroot to the root of your web project. This is the code that handles setup and content management and shouldn’t need to be modified. Add a new mdf database in the app_data directory or point the connection string to some other DB.

Now the site can be compiled and run. This should bring up the /edit/install page to create the db tables and insert the root node value. You may need to stop the development server and restart it to see your ContentPage in the root and start node drop downs. Once this is done you should be able to edit the content of your Start Page!

What’s Next?

A few more details need to be handled, like authentication, some N2 setup on the Site.Master page, and some clean up of unused pages from the MVC project setup.

</p>


Unit Testing NServiceBus Send Methods

Here’s a static method for helping test the Send calls when using NService Bus. It depends on RhinoMocks.
 

public static class BusExpectationExtensions

{

    public static IBus ExpectSend(this IBus bus, IMessage firstMessage, IMessage returnMessage)

    {

        var callback = MockRepository.GenerateStub<ICallback>();

 

        bus.Expect(b => b.Send(firstMessage)).IgnoreArguments().Return(callback);

 

        callback.Expect(c => c.Register(null, null)).IgnoreArguments()

            .Return(new BusAsyncResult(null, null))

            .WhenCalled(invocation =>

            {

                var ar = new BusAsyncResult(invocation.Arguments[0] as AsyncCallback, invocation.Arguments[1]);

                ar.Complete(0, returnMessage);

                invocation.ReturnValue = ar;

            });

        return bus;

    }

}

</p>

To use it, create a stub of IBus, and then call ExpectSend with your send and return messages like;

var bus = MockRepository.GenerateStub<IBus>()

    .ExpectSend(new SendMessage(2, 22), new MessageReturned(2, 22, 3.00m))

    .ExpectSend(new SendMessage(4, 44), new MessageReturned(4, 44, 234.34m));

 

var filter = new SomeFilteringClass(bus);

 

… execute and assert

</p>

Set Up Virtual Directories From The Build

Following up my previous post, an approach to extending msbuild, here’s what we ended up using to setup out web sites and services. We have a very service oriented architecture here and are starting to have a lot of web services that need to be setup before any testing or debugging can take place.

The build file takes some arguments about the location of the CS project file and the name of the Virtual Directory to setup. Currently, we’re only handling virtual directories, but going forward, we like to handle everything needed to get the system up and running an a local machine. Anyway, the file is checking for extension points in the build file using and XML read on the existing build target. This is because msbuild, as far as I could find, doesn’t have a target exists method. I thinking about removing these check, as I don’t think we’re going to end up using them like I initially thought.

However, the bulk of the file is in the **AspNetSetup target. It checks for a default IIS 6 web site and if found it deletes any existing virtual directory and re-creates it. This helps if you have different branches in different locations and want to quickly change the setup. </p>

Anyway, here’s the XML:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="CopyToOutput">

    <PropertyGroup>

        <MSBuildCommunityPackPath>....\ThirdParty.Dependencies\MSBuild.Community.Tasks<span style=“color: blue”></</span>MSBuildCommunityPackPath>

        <MSBuildCommunityTasksPath>.<span style=“color: blue”></</span>MSBuildCommunityTasksPath>

    </PropertyGroup>

    <Import Project="$(MSBuildCommunityPackPath)\MSBuild.Community.Tasks.Targets"/>

 

    <PropertyGroup>

        <MSBuildExtensionPackPath>....\ThirdParty.Dependencies\MSBuild Extension\ExtensionPack</MSBuildExtensionPackPath>

        <ExtensionTasksPath>.<span style=“color: blue”></</span>ExtensionTasksPath>

    </PropertyGroup>

    <Import Project="$(MSBuildExtensionPackPath)\MSBuild.ExtensionPack.tasks"/>

 

    <Import Project="$(ProjectFile)" Condition="$(ProjectFile) != ‘’"/>

    <ItemGroup>

        <ProjectPath Include="$(ProjectFile)"></ProjectPath>

    </ItemGroup>

 

    <PropertyGroup>

        <SetupDependsOnTargets>CoreBeforeSetup;CoreAspNetSetup;CoreAfterSetup</SetupDependsOnTargets>

    </PropertyGroup>

 

    <Target Name="Setup" DependsOnTargets="$(SetupDependsOnTargets)">

    </Target>

 

    <Target Name="CoreBeforeSetup">

        <Message Text="Setup: $(ProjectFile) types ‘$(ProjectTypeGuids)’" Importance="high" />

 

        <XmlRead Prefix="n"

                 Namespace="http://schemas.microsoft.com/developer/msbuild/2003"

                 XPath="/n:Project/n:Target[@Name=‘BeforeSetup’]/@Name"

                 XmlFileName="$(ProjectFile)">

            <Output TaskParameter="Value" PropertyName="FoundTargetName" />

        </XmlRead>

        <CallTarget Targets="BeforeSetup" Condition="$(FoundTargetName) != ‘’" />

    </Target>

 

    <Target Name="</strong>InitializeCoreAspNetSetup" Condition="‘@(AspNetWebsiteDirectories)’ == ‘’">

        <CreateItem

            Include="%(ProjectPath.RootDir)%(ProjectPath.Directory)"

            AdditionalMetadata="IISVirtualDirectory=$(IISVirtualDirectory)"

            Condition="Exists(‘$(OutputDirectory)$(AssemblyName)’) AND ‘$(IISVirtualDirectory)’ != ‘’">

            <Output TaskParameter="Include" ItemName="AspNetWebsiteDirectories" />

        </CreateItem>

    </Target>

 

    <Target Name="CoreAspNetSetup"

            DependsOnTargets="InitializeCoreAspNetSetup</span>"></p>

        <CallTarget Targets="</strong>AspNetSetup" Condition="‘@(AspNetWebsiteDirectories)’ != ‘’"/>

    </Target>

 

    <Target Name="__AspNetSetup">

        <Iis6Website TaskAction="CheckExists" Name="Default Web Site" >

            <Output PropertyName="Iis6Exists" TaskParameter="Exists"/>

        </Iis6Website>

 

        <Message Text="Website: %(AspNetWebsiteDirectories.IISVirtualDirectory) At %(AspNetWebsiteDirectories.FullPath)"

                 Importance="high"

                 Condition="$(Iis6Exists)"/>

 

        <WebDirectoryDelete VirtualDirectoryName="%(AspNetWebsiteDirectories.IISVirtualDirectory)"

                            ContinueOnError="true"

                            Condition="$(Iis6Exists)"/>

 

        <WebDirectoryCreate VirtualDirectoryName="%(AspNetWebsiteDirectories.IISVirtualDirectory)"

                            VirtualDirectoryPhysicalPath="%(AspNetWebsiteDirectories.FullPath)"

                            Condition="$(Iis6Exists)"/>

    </Target>

 

    <Target Name="CoreAfterSetup">

        <XmlRead Prefix="n"

                 Namespace="http://schemas.microsoft.com/developer/msbuild/2003"

                 XPath="/n:Project/n:Target[@Name=‘AfterSetup’]/@Name"

                 XmlFileName="$(ProjectFile)">

            <Output TaskParameter="Value" PropertyName="FoundTargetName" />

        </XmlRead>

        <CallTarget Targets="AfterSetup" Condition="$(FoundTargetName) != ‘’" />

    </Target>

</Project>

</p></div></p>


An Approach to Extending MSBuild

download

I’m working toward setting up a master build for our enterprise code using MSBuild. My first pass was to include a common build project in each of our csproj files. This was a bad idea for two reasons. One it’s a pain to update each csproj file. And two, VS complains when you open a csproj file with modifications. So, it was off to find a different approach, this was inspired by Sayed Ibrahim. The idea is to call your common build project file with the csproj file name and import that, giving you access to that files properties. One issue is the relative paths are now based on your common build projects location, this didn’t end up being to much of an issue. So anyway, here some code … err XML:

    <Target Name="CoreDeploy" DependsOnTargets="CoreBuild">

        <MSBuild Projects="SubProjects\Publish.proj" Targets="Deploy"

                 Properties="ProjectFile=%(ProjectsToBuild.FullPath);OutputDirectory=$(OutputRootDirectory);IISVirtualDirectory=%(ProjectsToBuild.IISVirtualDirectory)">

        </MSBuild>

    </Target>

</p>

The above calls into the Publish.proj file, which in turn includes the passed in ProjectFile.

 

    <Import Project="$(ProjectFile)" Condition="$(ProjectFile)!=‘’"/>

    <ItemGroup>

        <ProjectPath Include="$(ProjectFile)"></ProjectPath>

    </ItemGroup>

</p>

The above bit of code includes the project file in the Deploy.proj allowing it’s targets, properties, and items to be called.

Using the above approach I could have common setup project and a common deploy project, I’ll blog about these in following posts.


Setting Up AutoMapper For Use From A Container

I like getting all my service from the container and I like using AutoMapper. So I added the AutoMapper to the container, a WindsorContainer in this case.

The main interface is IMappingEngine, which is implemented by MappingEngine which also implements IMappingEngineRunner. This contains a getter to an IConfiguration interface which is also an IConfigurationExpression. So to replace Mapper you need an instance of IMappingEngine and then you can configure the engine as follows:

public CustomMapper(IMappingEngine engine)

{

    engine = engine;</p>

 

    var runner = </em>engine as IMappingEngineRunner;

    if (runner == null)

        return;

 

    var configuration = runner.Configuration as IConfigurationExpression;

    if (configuration == null)

        return;

 

    configuration.CreateMap<IApplication, B.Application>();

}

</p></div>

Next setting up the container, the MappingEngine needs an IConfiguration, the Configuration needs some IObjectMapper instances.

private static void RegisterMappingEngine()

{

    container.Register(AllTypes.Of<IObjectMapper>()</p>

                            .FromAssembly(typeof (IObjectMapper).Assembly)

                            .Configure(configurer => configurer.Named(configurer.ServiceType.Name))

                            .Configure(configurer => configurer.LifeStyle.Transient));

 

    </em>container.Register(Component.For<IConfiguration>().ImplementedBy<Configuration>()

                            .LifeStyle.Transient.ServiceOverrides(

                            ServiceOverride.ForKey("mappers").Eq(

                                "CustomTypeMapMapper",

                                "TypeMapMapper",

                                "NewOrDefaultMapper",

                                "StringMapper",

                                "EnumMapper",

                                "ArrayMapper",

                                "EnumerableMapper",

                                "NullableMapper",

                                "AssignableMapper")

                            ));

 

    _container.Register(Component.For<IMappingEngine>().ImplementedBy<MappingEngine>()

                            .LifeStyle.Transient);

}

</p></div>

These are all set as Transient because each CustomMapper can be Singleton and I don’t want the mappings to over lap between the CustomMappers.

</p>


Testing Log4Net Output

We have a custom logging framework setup which is using log4net under the covers. When doing that the ILogger should be used, not the ILog. The ILogger sets up the correct Class information.

Below is some code for testing the output from log4net, this code is just using log4net to write the message, which is not valuable as a test, but when the logging call is replaced with a custom framework you can make sure you’re getting what you expect in your log events.

private MemoryAppender appender;</p>

private LoggingEvent </em>event;

 

[SetUp]

public void Context()

{

    appender.Clear();</p>

    // Using log4net here, but should be replace with a custom logging class

    // which calls ILogger.Log under it.

    LogManager.GetLogger(GetType()).Warn("my test desc.", new Exception("my test exception."));

    </em>event = appender.GetEvents()[0];</p>

}

 

[FixtureSetUp]

public void SetupLog4NetMemoryAppender()

{

    </em>appender = new MemoryAppender();

    appender.Name = "Unit Testing Appender";</p>

    </em>appender.Layout = new log4net.Layout.PatternLayout("%date{dd-MM-yyyy HH:mm:ss,fff} %5level [%2thread] %message (%logger{1}:%line)%n");

    appender.Threshold = Level.All;</p>

    </em>appender.ActivateOptions();

 

    Logger root = ((Hierarchy) LogManager.GetRepository()).Root;

    root.AddAppender(appender);</p>

    root.Repository.Configured = true;

}

 

[FixtureTearDown]

public void RemoveLog4NetMemoryAppender()

{

    Logger root = ((Hierarchy)LogManager.GetRepository()).Root;

    root.RemoveAppender(</em>appender);

    root.Repository.Configured = true;

}

 

[Test]

public void Should_have_When_logging_type_in_log()

{

    var data = _event.GetLoggingEventData();

    var info = data.LocationInfo;

    info.ClassName.ShouldEqual("Core.Tests.Instrumentation.When_logging_with_CustomLogger");

    info.MethodName.ShouldEqual("Context");

}

</p></div>

BTW, the code highlighting was done using the Copy As HTML plug in from here.

</p>


TDD Reduces Defects!

I just read Realizing quality improvement through test driven development. It’s a summary of 4 TDD teams at IBM and Microsoft compared to similar non TDD teams. With all the variables in any software project it’s hard to draw a hard conclusion, but this shows definite trends toward fewer defects minimal increased time, and I’d argue less time, because the reduced number of defects. I think the time measurement here is really subjective as it’s based on the judgment of the project managers. Regardless, I think these types of studies help prove the importance of good software practices. 


Brutalist Framework