• Setting the Powershell Console’s Title to the Current SVN URL

    I’m testing out branching per feature, as such I wanted a way to know where my working directory was pointing. Since I’m using power shell I added some functions to my profile script to set the window title to the svn url. This doesn’t auto update w/ an svn switch, but set-svntitle can be called manually.

    Anyway, here’s the script.

    $projects = "C:\data\working"
    function set-title{Param([string] $title); $Host.Ui.RawUi.WindowTitle = $title}
    
    function set-svntitle
    {
      $currentsvn = svn info $projects | select-string "URL: "
      set-title $currentsvn 
    }
    
    set-svntitle 
    
  • NServiceBus Distributor Overview

    Overview

    The client sends messages to the distributors input queue on a remote machine. The server (or receiver) contacts the distributor asking for messages. As messages are sent to the distributor, it looks up the message endpoint in its configuration and sends the message to one of the servers waiting for work. It won’t send more to that server until it calls back to the distributor asking for more work. This stops those servers from ending up with a ton of unprocessed messages. Ayende has a good review of the Distributor here. The Colour Coding blog and this post on the Mailing list have more information about getting the samples working.

    Client

    The client configures the Message to go to the distributor data bus. This will be a remote queue in a distributed system.

      <MsmqTransportConfig
        InputQueue="client"
        ErrorQueue="error"
        NumberOfWorkerThreads="1"
        MaxRetries="5"
      />
      
      <UnicastBusConfig 
          DistributorControlAddress="" 
          DistributorDataAddress="">
        <MessageEndpointMappings>
          <add Messages="Messages" Endpoint="distributordatabus@xxxxxx0032" />
        </MessageEndpointMappings>
      </UnicastBusConfig>
    

    Server

    The server configures the Distributor control and data addresses on the UnicastBusConfig.

      <MsmqTransportConfig
        InputQueue="messagebus"
        ErrorQueue="error"
        NumberOfWorkerThreads="1"
        MaxRetries="5"
      />
      
      <UnicastBusConfig 
          DistributorControlAddress="distributorcontrolbus@xxxxxx0032" 
          DistributorDataAddress="distributordatabus@xxxxxx0032">
        <MessageEndpointMappings>
        </MessageEndpointMappings>
      </UnicastBusConfig>
    

    Distributor

    This distributor using the distributor control bus for it’s input. Thd distributor data bus in setup in the appSettings along with the distributor storage queue. The UnicastBusConfig need to have the message objects it will receive with the input queue of the server, messagebus in this case. This value should not be a remote value as the server will contact the distributor asking for work. It’s important for the DistributorControlAddress and DistributorDataAddress to be empty otherwise the Distributor will be in an endless loop sending stuff to itself. It would seem that the distributor would know which workers can handle which messages, but it seems like that is not the case. In other words, if you want the server to be a client in some cases with the client being the server in those cases, you need to setup another distributor. It helps to think of the Distributor as the Distributor for a specific service. Like the Business Process 1 Distributor.

      <MsmqTransportConfig
          InputQueue="distributorControlBus"
          ErrorQueue="error"
          NumberOfWorkerThreads="1"
          MaxRetries="5"
      />
      
      <UnicastBusConfig DistributorControlAddress="" DistributorDataAddress="">
        <MessageEndpointMappings>
          <add Messages="Messages" Endpoint="messagebus" />
        </MessageEndpointMappings>
      </UnicastBusConfig>
      
      <appSettings>
        <add key="DataInputQueue" value="distributorDataBus"/>
        <add key="NumberOfWorkerThreads" value="1"/>
        <add key="ErrorQueue" value="error"/>
        <add key="StorageQueue" value="distributorStorage"/>
        <add key="NameSpace" value="http://www.UdiDahan.com"/> <!&mdash; relevant for a Serialization of "interfaces" or "xml" &mdash;>
        <add key="Serialization" value="xml"/> <!&mdash; can be either "xml", or "binary" &mdash;>
      </appSettings>
    

  • N2cms Navigation Options on a MVC Site

    Add the n2 SlidingCurtain control with a DragDropControlPanel control inside it after the opening body tag. This will link to the editing system of n2cms.

    <n2:SlidingCurtain ID="SlidingCurtain1" runat="server">
        <n2:DragDropControlPanel ID="DragDropControlPanel1" runat="server" />
    </n2:SlidingCurtain>
    

    Now there are a bunch of N2 functions that can be used to interface with the navigation system.

    To setup the menu the following can be used, the first list item shows a link to the home page. The second list item shows the sub pages.

    <ul id="menu">
        <li><a href="<%= N2.Find.StartPage.Url %>">Home</a></li>
        <%= N2.Web.Tree.From(N2.Find.StartPage, 2).Filters(new N2.Collections.NavigationFilter()).ExcludeRoot(true)%>
    </ul>
    

    Next to setup the sub navigation something like this could be used.

    <div class="leftColumn" style="border:solid 1px black">
        <h2><%= N2.Utility.Evaluate(N2.Find.AtLevel(N2.Find.CurrentPage, N2.Find.StartPage, 2), "Title") %></h2>
        <ul>
            <%= N2.Web.Tree.Between(N2.Find.CurrentPage, N2.Find.StartPage, true, 2).Filters(new N2.Collections.NavigationFilter()).ExcludeRoot(true) %>
        </ul>
    </div>
    

    Bread crumbs can be setup using.

    <% foreach(N2.ContentItem item in N2.Find.EnumerateBetween(N2.Find.StartPage, N2.Find.CurrentPage, false)) { %>
        <%= N2.Web.Link.To(item) %> /
    <% } %>
    <%= N2.Utility.Evaluate(N2.Find.CurrentPage, "Title") %>
    

    Lastly, you can update the page title using something similar to what’s been used above.

    <title><%= N2.Utility.Evaluate(N2.Find.CurrentPage, "Title") %><asp:ContentPlaceHolder ID="TitleContent" runat="server" /></title>
    
  • Setup N2Cms to Use the Asp.Net Providers for Authentication and Authorization

    </p>

    Continued from Setup N2Cms on an Asp.Net MVC

    Setup the data

    Add the asp.net sql tables by executing aspnet_regsql.exe

    aspnet_regsql.exe -C "data source=.\SQLEXPRESS;Integrated Security=SSPI ;User Instance=true" -A all -d c:\projects\red27\app_data\red27.mdf

    http://weblogs.asp.net/lhunt/archive/2005/09/26/425966.aspx

    Next add the Administrators and Editors roles to the db using the aspnet_Roles_CreateRole stored procedure.

    aspnet_Roles_CreateRole &lsquo;/&rsquo;, &lsquo;Administrators&rsquo; <br />aspnet_Roles_CreateRole &lsquo;/&rsquo;, &lsquo;Editors&rsquo;

    Next update the web config roleManager to be enabled and remove all but the AspNetSqlRoleProvider provider.

    http://code.google.com/p/n2cms/wiki/WebConfig

    <roleManager enabled="true">
      <providers>
        <clear />
        <add connectionStringName="N2CMS" applicationName="/" name="AspNetSqlRoleProvider" type="System.Web.Security.SqlRoleProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
      </providers>
    </roleManager>
    

    Create the admin account

    Now create a new user by going to the /Account/Register page. Then log off.

    Log in to the /edit section of the site using the credentials found in the forms authentication section.

    <authentication mode="Forms">
      <forms loginUrl="edit/login.aspx" protection="All" timeout="30000" path="/">
        <credentials passwordFormat="Clear">
          <user name="admin" password="changeme"/>
        </credentials>
      </forms>
    </authentication>
    

    Click on Manage Users and then edit your newly created account, adding it to the Administrators and Editors roles. Log off and back on with your new account.

    Clean Up

    Now remove the credentials section from the forms authentication section.

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

    Open the web.config in the Edit directory and remove the users attribute from the authorization section.

    <authorization>
      <allow roles="Administrators,Editors"/>
      <deny users="*"/>
    </authorization>
    
  • 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>