Home | Blog | Screencasts | Projects
# Saturday, April 25, 2009

CRM 4 has lots of excellent customization options, one of which is the auto-numbering of entities:

 

image

 

The Settings – Administration section allows you to setup some options for auto-numbering your entities, you can choose the prefix, and the suffix length:

 

image

 

This is pretty powerful, but how would approach this problem if you needed your entities to have a more complex numbering scheme? Being a developer, everything looks like a development task to me. So I would start with a plugin that implements the IPlugin interface:

 

       public class AutoNumber : IPlugin

 

Then we need to implement the Execute method such as:

 

        public void Execute(IPluginExecutionContext context)
        {
           
            if (context.InputParameters.Properties.Contains("Target"))
            {
                DynamicEntity entity = (DynamicEntity)context.InputParameters.Properties["Target"];

                if (entity != null)
                {
                    if (!entity.Properties.Contains("AttributeName"))
                    {
                        entity.Properties.Add(new StringProperty("AttributeName", GetAutoNumber()));

                    }
                }
            }
        }

 

So the above code will check to make sure our input properties contains the ‘Target’ parameter, this will be the entity that is affected by the plugin. From here we can cast it to a DynamicEntity  which will make it easier to work with. Once we have an instance of DynamicEntity the whole task of updating and attributes becomes trivial, the above code just creates a new property and calls some method that will generate the complex AutoNumber.

 

Once the plugin has been developed, the next important step is to register the plugin. The best tool for the job is probably the CRM Plugin Registration Tool:

 

PlugIn_thumb

 

The first step is to register the plugin assembly:

 

image

 

This will prompt you with a form that allows you to select the assembly and the location it will be stored i.e. Database, file system or GAC.

 

Once the assembly has been registered we can register a ‘new step’, this is done from the same menu as the assembly registration.

You will be prompted to fill out the following form:

 

image

 

For our plugin we will choose the following options:

Message: Create  (since we are doing an auto number, it makes sense to only run the plugin at the create stage)

Primary Entity:  Your choice

Plugin: Your assembly that was loaded at the previous step

Post Stage: we want to run it after the entity has been created, otherwise the built in CRM auto numbering will overwrite our value.

Synchronous: lets fire the event synchronously, no reason not to, the end user will be presented with a consistent interface this way.

Step Deployment: your choice

Triggering Pipeline: Parent

 

As you can see its all fairly simple and straight forward, now you can have an auto number that be what ever you need, it might be simple like some combination of a financial year or complex where a lookup to some external system needs to take place, but at the end of the day at least there is a nice mechanism in place to help you out.

Saturday, April 25, 2009 6:50:35 PM (E. Australia Standard Time, UTC+10:00)  #    Comments [1] - Trackback
code | CRM

By default when you edit the tool part settings of a SharePoint web part and you click on the ‘…’ if the property is a string, you will get the default property builder:

 

image

 

Clicking the above ‘…’ will launch the default string property builder:

Standard SharePoint Property Text Builder

 

This popup is actually the ‘zoombldr.aspx’ page located in the _layouts virtual directory.

 

There are a few problems with this builder, well it’s not so much the builder, the string property’s of the web part are serialised in a manner that strips out carriage returns. So if you were to spend time formatting the contents of this dialog, it will get wiped out when you save the contents. This is most apparent when you edit the XSLT of the core search results web part.

 

How can you override this property?

 

The key is to make use of the HtmlDesigner attribute on your web part property. There are two ways to make use of the attribute, the first way is to explicitly define the page which you want to load (i.e. change ‘url to page’):

 

        [System.Web.UI.WebControls.WebParts.WebBrowsable(true),
        System.Web.UI.WebControls.WebParts.WebDisplayName("Template"),
        System.Web.UI.WebControls.WebParts.WebDescription(""),
        System.Web.UI.WebControls.WebParts.Personalizable(
        System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared),
        System.ComponentModel.Category("Settings"),
        System.ComponentModel.DefaultValue("")
        ]
        [HtmlDesignerAttribute("url to page", DialogFeatures = "dialogHeight:500px;dialogWidth:650px;help:no;status:no;resizable:yes")]
        public string CustomProp
        {
            get { return customProp; }
            set { customProp = value; }
        }

 

Notice that you could also modify the parameters that get passed into the JavaScript popup window creation script, in the case above I have made the popup window larger than the default size. The problem with the above approach is that the URL that gets passed as the first parameter to the HtmlDesignerAttribute must be a constant value, since it’s used in the attribute declaration. However Microsoft have provided us with a nice way to change this behaviour.

 

        [System.Web.UI.WebControls.WebParts.WebBrowsable(true),
        System.Web.UI.WebControls.WebParts.WebDisplayName("Template"),
        System.Web.UI.WebControls.WebParts.WebDescription(""),
        System.Web.UI.WebControls.WebParts.Personalizable(
        System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared),
        System.ComponentModel.Category("Settings"),
        System.ComponentModel.DefaultValue("")
        ]
        [HtmlDesignerAttribute(BrowserBuilderType.Dynamic, DialogFeatures = dialogHeight:500px;dialogWidth:650px;help:no;status:no;resizable:yes")]
        public string CustomProp
        {
            get { return customProp; }
            set { customProp = value; }
        }

 

I’ve now added the BrowserBuilderType.Dynamic option. To get this to work, we now need to override the GetCustomBuilder method of your web part:

 

         protected override string GetCustomBuilder(stringpropertyName)
        {
            if(propertyName == "CustomProp")
            {
                return"url.aspx?"+ "some custom params";
            }
            return base.GetCustomBuilder(propertyName);
        }

 

Now every property that has BrowserBuilderType.Dynamic passed into the HtmlDesignerAttribute will get passed into the GetCustomBuilder method, this gives you the chance to create a URL that passes parameters to your custom builder page.

 

Ok, so now you got the web part covered, what about the JavaScript that needs to run on your custom property builder page?

 

The best thing to do is to look at the current zoomdldr.aspx page, when the page loads you can get the current arguments from:

 

window.dialogArguments

 

Then to save the arguments, save the string value back to:

 

window.returnValue

 

 

Now that you’ve got a fair idea about creating custom property builders, you can create builders that are specific to you needs, below is a screenshot of a property builder form that lets the user enter some c# code that gets compiled an injected into the web part, the need was to provide a nice interface that the end user (i.e. a programmer) could use. The property builder uses the EditArea control to format the code nicely. The user can then click the compile button which will do a compilation of the code and report any errors. I’ve also added some JavaScript code to get around the removal of the carriage return characters, which was a major pain point for us.

 

Custom Property Builder

 

 

This approach will give you the power to create property builders that more closely suit your needs and goals.

Saturday, April 25, 2009 12:44:51 PM (E. Australia Standard Time, UTC+10:00)  #    Comments [0] - Trackback
code | Sharepoint | Tip
# Tuesday, April 21, 2009

There is always lots of criticism about SharePoint from developers, things like bloated pages etc. But there is lots of good stuff in SharePoint, so I thought it might be fun to come up with a list of things that a typical asp.net application might be able to look at SharePoint for guidance.

 

1. Search - Have a good search story

We now have a whole generation that have grown up using a search engine, they expect a search box that they can type free form data, so by far the biggest feature that ASP.NET applications could learn a lot is the search capability of SharePoint. The good news is that you could use a product like search server express to crawl parts of your application and then use the search server web services like I did with my ASP.NET MVC search engine example.

 

2. Extensibility – Make your system extensible where it makes sense

SharePoint has a number of extensibility points like Features, Site Definitions, Web Parts which all work in different ways to allow fine grade control over the system. The model that SharePoint provides works well for the tasks that it is trying to achieve, your ASP.NET application may benefit from a plugin model that a framework like MEF provides, where crazy things like plugins for plugins are supported. Maybe you want to expose a RESTful service via ADO.NET Data Services, in any case extensibility has proved for many web applications (twitter, facebook) to be an important consideration.

 

3. Workflow - Hosting done right

Workflow has become a hot topic in recent years, lots of tasks are well suited to be modelled with a workflow, I’m sure your ASP.NET application has some actions that need some auditing or approval before action can be taken. SharePoint does a particularly good job at hosting Window Workflow Foundation, even if we exclude the building of custom workflow’s on the fly in SharePoint Designer, a great deal can be taken away from the way SharePoint has implemented WorkFlow, it’s made it simple for most users to understand.

 

4. Well designed API  

Well not everyone would agree with the ‘well designed’ bit, but for the most part you can achieve almost all the administrative activities from the API. Although a well built ASP.NET application should in theory have loosely coupled components that should facilitate an API, in practice most ASP.NET applications have code thrown all over the place, with event handlers performing data access etc. Ideally the well designed API should fit with the Extensibility point from number two, it’s no good having an extensible system if the API is unusable.

 

5. Background Processes – How do you handle them?

One thing that SharePoint does particularly well is the background processing of tasks like Active Directory imports, site cleanup processes and scheduled emails. The API from the above point extends to the scheduling plugin’s as well, the main point here is that SharePoint runs these tasks as a windows service, this is in contrast to applications like DotNetNuke which do background and scheduled processing on the application OnStart event. Both are valid approaches given certain circumstances, my point is that the difference between a professional product like SharePoint and a custom ASP.NET application is the little house keeping items, SharePoint tries to take care of it all, so should your ASP.NET application.

 

6. Integration with the Microsoft Software Ecosystem

SharePoint being a Microsoft product plays nice with other Microsoft systems, your ASP.NET application can do the same, things like using Exchange for email events to support incoming email. Making use of active directory groups for authentication and authorisation or even for organisation charts and manager notifications, this integration is what really differentiates Microsoft’s products and makes people feel more comfortable. Does your ASP.NET application provide custom WMI performance counters for it’s operations?

Tuesday, April 21, 2009 10:48:44 PM (E. Australia Standard Time, UTC+10:00)  #    Comments [1] - Trackback
Misc
# Sunday, April 19, 2009

A little while ago, I was working with a customer who wanted to have the ability to write some annotations on a graph that was presented in a SharePoint site. The idea was that the graph was quite complex and it would help the end user understand it more if it was annotated by a domain expert.

So I put together a little proof of concept web part that makes use of all that JQuery goodness. I figured that it didn’t matter if you were annotating complex graphs, or tagging someone’s face like the way flickr and facebook does, the concept was the same:

 

image

 

image

 

image

 

So like I said the code is proof of concept, not production ready, but I thought I’d post it here anyway, if only to inspire ideas.

I’ve stored the data in a list with the schema of

 

Title:  default setting (single line of text)

JSON: single line of text

 

Or:

image

 

The data will look like this once some annotations have been added:

 

image

 

So I’ve just stored the JSON serialised object as is. The web part will iterate over each item on that list and pump that JSON into some JavaScript that is written to the page, so when the page load’s it will insatiate a JavaScript array of annotations and then render them. Which looks something like:

 

notes = [{ "x1": "11", "y1": "28", "height": "91", "width": "73", "note": "This is bill", "imgID": "0"}, { "x1": "86", "y1": "54", "height": "81", "width": "95", "note": "This is Kate", "imgID": "0"}];

 

You might ask why I have the imgID property, this is used if more than one image on the page is annotated.

 

To have a play around with the web part, you can download the WSP here, you just need to deploy the solution in the normal manner (stsadm –o addsolution –filename <path to httpcode.imageannotate.wsp>

 

Next step is to activate the Image Annotation web part feature on the site:

 

image

 

Add the web part as normal from the web part menu:

 

image

 

Once the web part is on the page, you need to set List setting to the SharePoint list that will store the JSON data (this list must conform to the schema above):

 

image

 

 

The client ID is the most technical part, this is a selector that gets used directly by JQuery, so any valid JQuery selector will work. A couple of examples:

This will find the default image on the homepage of a standard collabration portal:

img[src*='/PublishingImages/newsarticleimage.jpg']

 

All the images produced by a Dundas chart:

img[src*='SharePointChartAxd.axd']

 

Hopefully you get the idea.

 

I haven’t added any way to delete the annotation from the image itself, rather you need to go to the custom list and delete the item from there. The web part is smart enough to not allow users who can’t add items to the list. Credit should also go to odyniec.net for the imgAreaSelect JQuery plugin which I’ve made use of.

The web part injects lots of JavaScript into the page, so the code might look a little messy because of this, but the basic premise is pretty straight forward.

 

The source code can be found here. It includes a HttpHandler (ImageAnnotate.ashx) for handling all the ajax calls from the client side script. Remember this is just some sample code, use it at your own risk.

Sunday, April 19, 2009 6:49:54 PM (E. Australia Standard Time, UTC+10:00)  #    Comments [0] - Trackback
code | JQuery | Sharepoint
# Tuesday, March 24, 2009

I came across an interesting problem the other day, I needed to do some string manipulation with SQL Server, normally dealing with this sort of stuff sucks, I wanted to split a string like  ‘item1, item1, item3’ .. so splitting on a given character. The first thing that popped to my mind was a Table Valued function, something like this:

 

-- Splits a column based on a specified delimiter
CREATE FUNCTION dbo.Split
(
      
@List nvarchar(2000),
      
@SplitOn nvarchar(5)
)
RETURNS @RtnValue table
(

      
Id int identity(1,1),
      
Value nvarchar(2000)
)
AS
BEGIN

       While
(Charindex(@SplitOn,@List)>0)
      
Begin
               Insert Into
@RtnValue (value)
              
Select  Value = ltrim(rtrim(Substring(@List,1,Charindex(@SplitOn,@List)-1)))
              
Set @List = Substring(@List,Charindex(@SplitOn,@List)+len(@SplitOn),len(@List))
      
End

       Insert Into
@RtnValue (Value)
              
Select Value = ltrim(rtrim(@List))

              
Return
END

 

There are many examples of this type of function on web, my point isn’t the actual implementation, rather the way you can use it.

I can now run the query:

 

select Value from dbo.Split('item1, item2, item3',',')

 

image

 

But my problem was a little different from the trivial example above, I wanted to pass data into this function that was the result of my query, rather than just passing arguments in statically like above and using the function as a table, I really wanted to apply the function to the data from my table, on a row by row level.

After a little searching I found the CROSS APPLY SQL syntax, with this I can now do exactly what I want:

 

select SF.Value from TestTable TT
cross apply dbo.Split(TT.Value, ',') SF

 

The above query simple selects the data from the TestTable, then cross apply’s the Split function passing it the TestTable’s value column (which contains the data ‘Item1, Item2, Item3, Item4’, also note that I’m selecting the column from the Split function.

 

This Produces the desired results:

image

 

Given a table like:

 

image

 

And data like:

 

image

 

I thought this was pretty cool. SQL can be fun.

Tuesday, March 24, 2009 10:51:29 AM (E. Australia Standard Time, UTC+10:00)  #    Comments [0] - Trackback
code | SQL Server
# Thursday, March 19, 2009

If you are using the BDC in your MOSS application, you may have noticed that the AUDEvent table in the SSP database can grow quite large and quickly. You might think that the stsadm command –o trimauditlog would do the trick in reducing this table, but in fact it does not work for the SSP database only content databases.

 

You can specify the audit setting in the Application Definition File. Simply add the Property ‘Audit’ to your Entity’s Properties section:

 

<Entities>
    <
Entity EstimatedInstanceCount="0" Name="dbo.Sales">
      <
Properties>
        <
Property Name="Audit" Type="System.Boolean">false</Property>
      </
Properties>

The setting of false will prevent any audit entries being written to the AUDEvent table.

Thursday, March 19, 2009 9:48:23 AM (E. Australia Standard Time, UTC+10:00)  #    Comments [0] - Trackback
BDC | MOSS | Sharepoint | Tip

Yesterday I presented at the Brisbane SharePoint User Group, my topic was Using the Business Data Catalog with User Profiles. It was loosely based on this screencast I did last year.

The entire presentation was recorded using Live Meeting, it can be viewed by going to: https://www112.livemeeting.com/cc/microsoft/viewRecordings

and use the Recording Code: 7B8B6D

I’ve put the PowerPoint presentation for my talk here.

Thursday, March 19, 2009 9:32:01 AM (E. Australia Standard Time, UTC+10:00)  #    Comments [0] - Trackback
BDC | setup | Sharepoint

A while ago I wrote about a simple method to give web parts an easy to configure design experience. The method I came up with made use of the ASP.NET Page method ParseControl.

In my original example, the developer had to do some manual work like finding the templated control once it was added to the page, this was a tedious and ugly design. At the time it was more of an idea rather than something that I had build seriously, but recently I’ve put a bit more thought into the idea of web parts with a configurable UI template.

I’ve built on the original example, but with the goal of abstracting the ParseControl and ControlBinding implementation.

This example simply writes a title and description:

 

image

 

We want to give end users the ability to change the way this title and description look without any web part code changes.

This is done via a template, which is a property of the web part:

 

image

 

Notice this is ASP.NET syntax

 

The template can be easily changed:

 

image

 

To produce a different rendering, without the need for any developer assistance:

 

image

 

From a developers point of view, we really want to just declare some variables and be able to use them as if they were part of the web part, without thinking about FindControl or any implementation detail.

 

I put together a base web part that looks for any private variables with a custom attribute called ControlBinding and via reflection sets this variable to the control instance specified in the attribute, so for the above example the code for the web part would look like:

 

public class TemplateTest : WebPartTemplateBase
{
        [ControlBinding("titleLabel")]
        Label titleLabel = null;

        [ControlBinding("descriptionLabel")]
        Label descriptionLabel = null;

        protected override void OnPreRender(EventArgs e)
        {

            if (titleLabel != null)
            {
                titleLabel.Text = "Some text set from code";
            }

            if (descriptionLabel != null)
            {
                descriptionLabel.Text = "some text description that was set in code";
            }
           
            base.OnPreRender(e);
        }
}

 

So now all the developer needs to do is add an attribute to private variables with the name of the templated control they want to obtain an instance to. This fits very much with the code behind / partial class philosophy that web developers are used to working with.

 

If we look at some more complex scenario’s we start finding a few little issues, for example lets say that we want to use a GridView to do some data binding, in a normal aspx page we would use code nuggets to do the data binding:

 

<asp:GridView ID="GridView1" runat="server">
        <
Columns>
            <
asp:TemplateField>
                <
ItemTemplate>
                 <
asp:label id="dataItemLabel" runat="server"
                                   
Text='<%DataBinder.Eval(Container, "SomeValue"%>'></asp:label>
                </
ItemTemplate>
            </
asp:TemplateField>
        </
Columns>
</
asp:GridView>

 

If we used this in our template we would find that it doesn’t work. There is however a work around:

We can subscribe to the RowDataBound event and we can find the controls we wish to data bind to:

 

 

        [ControlBinding("GridView1")]
        GridView gridView = null;

        protected override voidOnPreRender(EventArgs e)

         {

            if(gridView != null)
            {
                gridView.RowDataBound += newGridViewRowEventHandler(gridView_RowDataBound);
                gridView.DataSource = GetData();
                gridView.DataBind();
            }
           
            base.OnPreRender(e);
        }

        void gridView_RowDataBound(objectsender, GridViewRowEventArgs e)
        {
            Label itemLabel = e.Row.FindControl("dataItemLabel") as Label;

            if(itemLabel != null)
            {
                itemLabel.Text = e.Row.DataItem.ToString(); //cast and use the data item
          
}
        }

 

In which case the template would look like:

 

<asp:GridView ID="GridView1" runat="server">
        <
Columns>
            <
asp:TemplateField>
                <
ItemTemplate>
                 <
asp:label id="dataItemLabel" runat="server"></asp:label>
                </
ItemTemplate>
            </
asp:TemplateField>
        </
Columns>
</
asp:GridView>

 

I’m still in the early stages of experimenting with this pattern, so it could be the case that it’s totally unsuitable for some scenario’s.

If your interested in exploring this pattern a little more, you can download the code to the base class here.

Thursday, March 19, 2009 9:07:00 AM (E. Australia Standard Time, UTC+10:00)  #    Comments [1] - Trackback
code | Sharepoint | Tip
# Sunday, March 15, 2009

If your coming to SharePoint from a non .NET developer world, the web.config file might be a confusing and daunting file. I’ve broken out the major sections that any SharePoint administrator or newbie developer should know:

 

CallStack: By default SharePoint will display a nice friendly error message:

image

But this will often be a vague message that isn’t really helpful in troubleshooting the real problem. To enable a detailed error message, find the following line in your web.config file:

 

<SafeMode MaxControls="200" CallStack="false" DirectFileDependencies="10" TotalFileDependencies="50" AllowPageLevelTrace="false">

 

Then change the CallStack="false" to CallStack="true" This should also be coupled with another change in the web.config file:

 

Custom Errors:

The default will be to show custom errors, this will hide the ‘yellow screen’ that is the ASP.NET error reporting page:

 

image

 

<customErrors mode="On" />

 

By changing mode="On" to mode="Off" The yellow screen will be enabled, which should provide a more detailed report of the error.

 

Safe Controls:

SharePoint has measures in place that ensures that only trusted controls are used throughout the system. SharePoint will only load controls that have a SafeControl entry in the web.config file. Normally the SharePoint solution will write these entries in the web.config file, which is under administrative control (because an administrator is deploying it). But often a developer won’t worry about packaging up a control if they are still developing, so they need to manually add a control to the Safe Controls section of the web.config file, a typical entry looks like:

 

<SafeControl Assembly="Microsoft.SharePoint.Portal, Version=11.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" Namespace="Microsoft.SharePoint.Portal.WebControls.Alerts" TypeName="*" Safe="True" />

 

Blob cache:

A SharePoint administrator should be aware of the next setting, because it can increase the performance of your site. The idea is that SharePoint will write any images, CSS or JS to a folder on disk rather than requesting the file from the content database. The following entry should be modified to enable the blob cache (more info here).

 

<BlobCache location="C:\blobCache" path="\.(gif|jpg|png|css|js)$" maxSize="10" enabled="false" />

 

To enable the blob cache, change enabled="false" to enabled="true" and make sure that the location exists.

 

Trust level:

Another important concept that new developers need to come to terms with is that by default SharePoint will run under a custom trust level called ‘WSS_Minimal’. This means that they can’t deploy assemblies into the ‘bin’ directory, with the ‘WSS_Minimal’ setting they would need to deploy to the GAC. Normally in a development environment where the developer is constantly changing and testing the code, they will want to change the following line:

 

<trust level="WSS_Minimal" originUrl="" />

 

to: <trust level="Full" originUrl="" />

 

Proxy Settings:

Often in a corporate environment a proxy server will sit between your SharePoint farm and the internet. So operations like retriving external RSS feeds will not work with some configuration changes. The web.config file can be used to store the proxy information, by default the section will look like:

 

<system.net>
    <
defaultProxy>
      <
proxy autoDetect="true" />
    </
defaultProxy>
  </
system.net>

 

to add a new proxy:

 

<system.net>
    <
defaultProxy>
      <
proxy useSystemDefault="false" proxyaddress="http://proxy" bypassonlocal="false"/>
    </
defaultProxy>
  </
system.net>

 

Hopefully now the web.config file isn’t such a mystery for the new SharePoint administrator or developer.

Sunday, March 15, 2009 3:17:00 AM (E. Australia Standard Time, UTC+10:00)  #    Comments [0] - Trackback
Sharepoint | Tip
# Thursday, March 12, 2009

My last post described a really simple scenario where I used a HttpHandler to configure StructureMap, then I showed some code that is used to replace the ‘new’ operation used to create a new instance like: ObjectFactory.GetInstance<NumberPrinter>()

 

This might seem a little clunky because you need to remember to use the StructureMap ObjectFactory, not all programmers are created equal, so it might slip through the cracks. What would a framework look like if it embraced the concept of DI?

Well it turns out that the ASP.MVC framework was designed from the ground up (or a mile up as the case may be, it was started on a plane flight). To configure StructureMap with ASP.NET MVC, firstly it’s OK to modify the global.asax (since this isn’t a SharePoint solution):

 

        protected void Application_Start()
        {
           
            StructureMapConfiguration.AddRegistry(new DIRegistry());

            ControllerBuilder.Current.SetControllerFactory( typeof(StructureMapControllerFactory)   );

        }

Here we configure StructureMap with the same method as my last post by using the StructureMapConfiguration.AddRegistry method. The second line is more interesting, we tell the MVC framework that we want StructureMapControllerFactory to be the factory class that creates our controllers. So the code to this class looks like:

 

public class StructureMapControllerFactory : DefaultControllerFactory
   
{
        protected override IController GetControllerInstance(Type controllerType)
        {
            try
           
{
                return ObjectFactory.GetInstance(controllerType) as System.Web.Mvc.Controller;

            }
            catch (StructureMapException x)
            {
                System.Diagnostics.Debug.WriteLine(ObjectFactory.WhatDoIHave());
                throw;
            }
        }       
    }

 

That’s pretty cool, so what it means now is that we can create our controllers to take an instance of anything we want to inject. Continuing from the example with a simple Number interface:

 

public class TestController : Controller
{
        INumber _number;
        public TestController(INumber number)
        {
            _number = number;
        }

        public ActionResult Index()
        {
           
            return View(_number.GetNumber());
        }

}

 

This means that the controller has been passed an instance of INumber that has been created by StructureMap. Once you do the initial setup, it’s now pretty hard to make any mistakes in regards to not calling the proper Create methods.

Thursday, March 12, 2009 11:30:00 AM (E. Australia Standard Time, UTC+10:00)  #    Comments [0] - Trackback
ASP.NET MVC | code | StructureMap

One of my favourite Dependency Injection (DI) containers is Jeremy Miller’s StructureMap, it’s full featured and light weight, but what I really like the most is the fluent configuration API which means we can write code like this:

ForRequestedType<INumber>()
                .TheDefaultIsConcreteType<Number>();

 

I also like that you don’t need to put custom attributes in your code.

 

It’s all pretty abstract so lets assume we have a simple interface, I’m deliberately keeping it simple:

   

public interface INumber
{
     int GetNumber();
}

 

A simple implementation of this interface:

 

public class Number : INumber
{      
        public int GetNumber()
        {
            return 999;
        }      
}

 

 

I’ve built a really simple (read: don’t use this in anything other than an experiment, the DI container in SharePoint gets configured for every request, including for images and scripts). This handler calls StructureMapConfiguration.AddRegistry with a class that derives from Registry which contains our fluent configuration information.

 

Why have I implemented this as a HTTPHandler?

If you read the Guidance on How to enable Unity in a SharePoint Application (unity is Microsoft’s DI container), the first step is to modify the Global.asax file. I have a problem with doing this for the simple reason that your touching the SharePoint system files, any upgrade will likely cause problems. It really is a shame that you can’t access the application onstart event using another mechanism.

I most certainly agree that the application onstart event is the perfect place to setup your DI container and if your happy to modify and deploy your changes to a global.asax file then go right ahead, put the code below into this file.

 

So it got me thinking, would a DI container like StructureMap work if it was setup in the begin request method of a HttpHandler? so my test code is:

 

namespace DIHttpModule
{
    public class DIHttpModule : IHttpModule
   
{  
        public void Dispose()
        {
          
        }

        public void Init(HttpApplication context)
        {
            context.BeginRequest += new EventHandler(context_BeginRequest);
        }

        void context_BeginRequest(object sender, EventArgs e)
        {
            StructureMapConfiguration.AddRegistry(new DIServiceRegistry());

            INumber numberGen = ObjectFactory.GetInstance<INumber>();

            ((HttpApplication)sender).Response.Write("Number: " + numberGen.GetNumber().ToString());
        }      
    }

    public class DIServiceRegistry : Registry
   
{
        protected override void configure()
        {
            ForRequestedType<INumber>()
                .TheDefaultIsConcreteType<Number>();
        }
    }
}

 

So after I deploy and configure my HttpHandler I do in fact see the number ‘999’ display at the top of each page (it also breaks JavaScript etc. because it writes this value into every request)

What has happened is that the call ObjectFactory.GetInstance<INumber>() has created an instance of Number(), we didn’t have to explicitly new up a Number() object.

That is a pretty basic example, lets expand it a little:

 

public class NumberPrinter
{
        private INumber _number;

        public NumberPrinter(INumber number)
        {
            _number = number;
        }

        public string PrintNumber()
        {
            return _number.GetNumber().ToString();
        }
}

 

Notice the constructor, it takes an INumber interface, the constructor is special as far as StructureMap is concerned, if StructureMap is asked to create an instance of NumberPrinter it will see that the constructor takes an INumber parameter and will attempt to pass in an instance of that type.

 

So assuming we changed the HttpModule to the following code:

 

NumberPrinter printer = ObjectFactory.GetInstance<NumberPrinter>();
((HttpApplication)sender).Response.Write(" Number : " + printer.PrintNumber());

 

We can see that again our output is ‘999’, StuctureMap was smart enough to create an Instance of INumber and pass it in, if you think about it, that’s pretty cool. It means that you can replace any concrete Instance of INumber with a single configuration change, that is powerful when we want to test our object in isolation, which is a whole other topic.

 

That’s a pretty trivial example, we did the StructureMap configuration and called ObjectFactory.GetInstance in the same method, now lets try it out in a web part which will be invoked later in the page life cycle (also remove the response.write from the HttpModule):

 

public class NumberWebPart : System.Web.UI.WebControls.WebParts.WebPart
{
        protected override void RenderContents(System.Web.UI.HtmlTextWriter writer)
        {
            NumberPrinter printer = ObjectFactory.GetInstance<NumberPrinter>();
            writer.Write("Web Part: " + printer.PrintNumber());
        }
}

 

Running this web part gives the expected result:

 

image

 

Again this is a really trivial example, that’s the point, you can clearly see that we are using a DI tool called StructureMap to create instances of our objects. Sure you could do away with the HttpHandler and move the configuration into the web.config file. But I do think the HttpHandler method warrants more thought however, because its useful when you introduce NHibernate to the mix, which is another framework that needs to do some expensive start up, which is also suited to the global.asax methods, but it does have some characteristics that should be managed per request, but that’s another post.

 

For now, hopefully I’ve proved some thought around DI and how can we manage the SharePoint deployments that really yell out for Application OnStart events without modifying the system file.

 

 

Thursday, March 12, 2009 9:10:00 AM (E. Australia Standard Time, UTC+10:00)  #    Comments [0] - Trackback
code | Sharepoint | StructureMap
Statistics
Total Posts: 191
This Year: 0
This Month: 0
This Week: 0
Comments: 41