Pages

Thursday, January 14, 2010

Declaritively show content in EpiServer

Not sure if this is a new idea (I doubt it - very few things ever are). I'm a big fan of reducing the amount of display logic in code-behind, especially showing and hiding regions of the page dependant on content stored in the CMS. What I wanted was a custom control in which you could declare a list of EpiServer properties which would need to be not empty for the control to render. I bet you've done this kind of thing a thousand times with a standard <asp:placeholder ... /> and code-behind. Here's a self-contained custom control which takes away all your code-behind pain and mess and allows you to just write markup in your .aspx/.ascx:
public class EpiPlaceHolder : PlaceHolder
{
    protected override void OnInit(EventArgs e)
    {
        this.Load += new EventHandler(EpiPlaceHolder_Load);
        base.OnInit(e);
    }

    public string Property
    {
        get;
        set;
    }

    public bool ShowOnFalse
    {
        get;
        set;
    }

    public PageData CurrentPage
    {
        get;
        set;
    }

    void EpiPlaceHolder_Load(object sender, EventArgs e)
    {
        bool isVisible = false;

        if (CurrentPage == null)
        {
            CurrentPage = ((EPiServer.TemplatePage)Page).CurrentPage;
        }

        if (Property != null)
        {
            string propertyName = Property;

            if (propertyName.Contains(","))
            {
                string[] props = propertyName.Split(new char[] { ',' });

                foreach (string prop in props)
                {
                    isVisible = IsVisibleForProperty(prop);

                    if (!isVisible)
                    {
                        break;
                    }
                }
            }
            else
            {
                isVisible = IsVisibleForProperty(propertyName);
            }
        }

        Visible = (ShowOnFalse) ? !isVisible: isVisible;
    }

    private bool IsVisibleForProperty(string propertyName)
    {
        object currentProperty = CurrentPage[propertyName];

        bool isVis = false;
        if (currentProperty != null)
        {
            bool tryParseProperty = false;
            if (bool.TryParse(currentProperty.ToString(), out tryParseProperty))
            {
                isVis = tryParseProperty;
            }
            else
            {
                isVis = true;
            }
        }

        return isVis;
    }
}
Here's how to use it:
<pdp:EpiPlaceHolder ID="EpiPlaceHolder1" runat="server" Property="LeadImage">
    <EPiServer:Property ID="Property2" runat="server" PropertyName="LeadImage" />
</pdp:EpiPlaceHolder>

Randomize a List<T>

Here's another instalment in a sporadic series of useful C# extension methods. This time we return a List in a random order:

public static class ICollectionExtensions
{
/// <summary>
/// Randomizes the specified source.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source">The source.</param>
/// <returns></returns>
public static ICollection<T> Randomize(this ICollection<T> source)
{
Random rng = new Random();

T[] a = new T[source.Count];
source.CopyTo(a, 0);

byte[] b = new byte[a.Length];
rng.NextBytes(b);

Array.Sort(b, a);

return new List<T>(a);
}
}

Thursday, October 22, 2009

More EpiServer extension methods

Here's another extension method for returning a normal EpiServer Url to a page from a LinkItem (from a PropertyLinkCollection).

public static class LinkItemExtensions
{
public static string ToExternal(this LinkItem item)
{
string externalUrl = item.Href;

PermanentLinkMapStore.TryToMapped(item.Href, out externalUrl);

return externalUrl;
}
}


I'm using the PermanentLinkMapStore following a read of this post.

I've been using it when binding a LinkItemCollection to a ListView like this:
PropertyLinkCollection linksProperty = CurrentPage.ToProperty("Links");

var links = from l in linksProperty.Links
select new
{
Href = l.ToExternal(),
Title = l.Title,
Text = l.Text,
Extension = Path.GetExtension(l.Href)
};

lvLinks.DataSource = links;
lvLinks.DataBind();

Wednesday, October 14, 2009

EpiServer Extension methods

Following on from my previous post I realised that the majority of the Extension methods that I use in everyday coding are EpiServer related.

Here are some really useful ones:


/// <summary>
/// Gets the children.
/// </summary>
/// <param name="page">The page.</param>
/// <returns></returns>
public static PageDataCollection GetChildren(this PageData page)
{
PageDataCollection children = new PageDataCollection();

if (page.PageLink != null && page.PageLink.ID > 0)
{
children = DataFactory.Instance.GetChildren(page.PageLink);

FilterPublished publishedDateFilter = new FilterPublished(PagePublishedStatus.Published);
FilterSort indexSorter = new EPiServer.Filters.FilterSort(FilterSortOrder.Index);

publishedDateFilter.Filter(children);
indexSorter.Sort(children);
}

return children;
}

/// <summary>
/// Gets the external URL.
/// </summary>
/// <param name="page">The page.</param>
/// <returns></returns>
public static string GetExternalUrl(this PageData page)
{
string result = String.Empty;

if (page != null)
{
UrlBuilder builder = new UrlBuilder(page.LinkURL);

EPiServer.Global.UrlRewriteProvider.ConvertToExternal(builder, page, Encoding.UTF8);

result = builder.ToString();
}

return result;
}


/// <summary>
/// Returns a concrete PropertyData (or child class)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="page">The page.</param>
/// <param name="propertyName">Name of the property.</param>
/// <returns></returns>
public static T ToProperty<T>(this PageData page, string propertyName) where T : PropertyData
{
return page.Property[propertyName] as T;
}

/// <summary>
/// Returns a PageData property or string.Empty if null
/// </summary>
/// <param name="page">The page.</param>
/// <param name="propertyName">Name of the property.</param>
/// <returns></returns>
public static string SafeProperty(this PageData page, string propertyName)
{
return (page.HasProperty(propertyName)) ? page[propertyName].ToString() : string.Empty;
}


/// <summary>
/// Gets the page for a Pagereference.
/// </summary>
/// <param name="pageRef">The Pagereference.</param>
/// <returns></returns>
public static PageData GetPage(this PageReference pageRef)
{
return DataFactory.Instance.GetPage(pageRef);
}

Some useful Extensions methods

Extension methods have been around a while now and I like them more every day. Oh I know they are just aliased static methods, but they are convenient for small common situations where it seems like the framework developers missed something out.

Here's a few of my that I find useful:

NameValueCollection Extensions:

/// <summary>
/// Returns the collectionas a standard URL type QueryString
/// </summary>
/// <param name="self">The self.</param>
/// <returns></returns>
public static string ToQueryString(this NameValueCollection self)
{
string qsString = string.Empty;
if (self.Count > 0)
{
StringBuilder qsBuilder = new StringBuilder("?");

for (int i = 0; i < self.Keys.Count; i++)
{
if (self.Keys[i] != null && self.Keys[i].Length > 0)
{
qsBuilder.AppendFormat("{0}={1}&", HttpContext.Current.Server.UrlEncode(self.Keys[i]), HttpContext.Current.Server.UrlEncode(self[i]));
}
}

qsString = qsBuilder.ToString();
}

return qsString;
}


/// <summary>
/// Creates a copy the specified NameValueCollection.
/// </summary>
/// <param name="self">The NameValueCollection.</param>
/// <returns></returns>
public static NameValueCollection Copy(this NameValueCollection self)
{
return new NameValueCollection(self);
}


String Extensions

/// <summary>
/// Capitalizes the specified word.
/// </summary>
/// <param name="word">The word.</param>
/// <returns></returns>
public static string Capitalize(this string word)
{
if (word.IsNullOrEmpty())
{
return word;
}

// The aggregate is because IEnumerable.ToString doesn't return the characters as a string, it returns the type's name as a string.
return word[0].ToString().ToUpper() + word.Skip(1).Aggregate("", (s, c) => s + c);
}


/// <summary>
/// Splits the specified string into a list of white-space separated word strings.
/// </summary>
/// <param name="s">The s.</param>
/// <returns></returns>
public static IEnumerable Wordify(this string s)
{
return s.Split(new char[]{' ', '\n', '\t', '\r', '.', ',', ';', ':', '-'}, StringSplitOptions.RemoveEmptyEntries);
}

Wednesday, September 16, 2009

Custom styles in EPiServer WYSIWYG

If you've been using EPiServer for a while you'll know the frustration of the built in WYSIWYG editor for rich text entry. It's IE only and rather clunky.



There is a way to add your own custom HTML and CSS classes into the editor which get added to a drop down box.



By selecting an item from this drop down you can insert both HTML tags and classes into the text.

To do this you need to set the 'Path to CSS file for Editor' either in the Web.Config



<sitesettings uieditorcsspaths="/styles/episerver.css" />


In the CSS file the secret is a custom style attribute called EditMenuName which gives the name to put in the drop down list in the editor:

/*
Inserts <h1> </h1>
*/
h1
{
   EditMenuName:Heading1;
}

/*
Inserts <h2> </h2>
*/
h2
{
   EditMenuName:Heading2;
}

/*
Inserts <p class="intro"> </p>
*/
p.intro
{
   EditMenuName:IntroParagraph;
   padding-bottom: 1em;
   border-bottom: 1px solid #E8E8E8;
}

/*
Inserts <div class="box white"> </div>
*/
div.box white
{
   EditMenuName:Rounded Corner Box;
   border: solid 1px #E8E8E8;
   padding: 10px;
}

Thursday, June 18, 2009

Simple UrlRewriter for EpiServer

On an EpiServer project that I just finished there was a requirement that all URLs generated by the CMS should be lowercase. I couldn't really see the issue. I know that web servers are allowed to be case sensitive with URLs, but didn't really see the big deal.

However, looking at EpiServer, I thught that I might re-write a PageData extension method which I wrote previously which generates the external URL of a page:

public static string GetExternalUrl(this PageData page)
{
string result = String.Empty;

if (page != null)
{
UrlBuilder builder = new UrlBuilder(page.LinkURL);
EPiServer.Global.UrlRewriteProvider.ConvertToExternal
(builder, page, Encoding.UTF8);

result = builder.ToString();
}

return result;
}

This might have worked, but was still a bit of a kludge, so I turned to the undocumented waters of the EPiServer.Web.FriendlyUrlRewriteProvider

The FriendlyUrlRewriteProvider has a virtual method called:

ConvertToExternalInternal(EPiServer.UrlBuilder url, object internalObject, Encoding toEncoding)

Which is called by the EPiServer.Global.UrlRewriteProvider.ConvertToExternal(...) method I use in the Extension method above. Overriding that mehtod and converting the UrlBuilder's path property to lowercase seemed to do the trick.

Here's the final class:

public class LowerCaseUrlRewriteProvider : EPiServer.Web.FriendlyUrlRewriteProvider
{
public LowerCaseUrlRewriteProvider()
: base()
{

}

protected override bool ConvertToExternalInternal
(EPiServer.UrlBuilder url, object internalObject, Encoding toEncoding)
{
base.ConvertToExternalInternal(url, internalObject, toEncoding);

url.Path = url.Path.ToLower();

return true;
}
}



Update: Setting it up in the Web.Config

You now need to add this to the urlRewrite EpiServer config element in the Web.Config:

<urlRewrite defaultProvider="LowerCaseUrlRewriteProvider">
<providers>
<add name="LowerCaseUrlRewriteProvider" type="Shared.EpiServer.Web.LowerCaseUrlRewriteProvider,Shared.EpiServer" />
<add name="EPiServerFriendlyUrlRewriteProvider" type="EPiServer.Web.FriendlyUrlRewriteProvider,EPiServer" />
<add description="EPiServer identity URL rewriter" name="EPiServerIdentityUrlRewriteProvider"
type="EPiServer.Web.IdentityUrlRewriteProvider,EPiServer" />
<add description="EPiServer bypass URL rewriter" name="EPiServerNullUrlRewriteProvider"
type="EPiServer.Web.NullUrlRewriteProvider,EPiServer" />
</providers>
</urlRewrite>

Wednesday, April 29, 2009

MVC Controller for webapp using Routes

Foloowing on from my previous post my MVC home brew is coming along slowly. I decided to implement Routes (Rails style routing for Python), basing my wsgi class on one I found on the App Engine Recipe site.

I altered the wsgi class very slightly to use a constructor passing in the request and response objects.
# Initialize matched controller from given module.
__import__(module_name)
module = sys.modules[module_name]

if (module is not None and hasattr(module, class_name)):
controller = getattr(module, class_name)(request, response)
else:
raise ImportError('Controller %s could not be initialized.' % (class_name))

The Controller itself acts as a base class for the actual concrete controllers which will be used in the application itself.
import os
import urlparse
import cgi
import logging
import sys
import traceback
from google.appengine.ext.webapp import template
from SessionManager import SessionManager
from routes.util import url_for

class Controller(object):
def __init__(self, request, response):
self.request = request
self.response = response
self.sessionManager = SessionManager(self.request, self.response)
self.session = self.sessionManager.current()
self.initialize()

def initialize(self):
pass

def handle_exception(self, e, debug):
#self.error(500)
logging.exception(e)
if debug:
lines = ''.join(traceback.format_exception(*sys.exc_info()))
self.response.clear()
self.response.out.write('%s' % (cgi.escape(lines, quote=True)))

def error(self, code):
"""Clears the response output stream and sets the given HTTP error code.

Args:
code: the HTTP status error code (e.g., 501)
"""
self.response.set_status(code)
self.response.clear()

def rendertemplate(self, context, path):
if (path is None):
path = __file__ + '.html'

if (context is None):
context = dict()

context['session'] = self.session
context['request'] = self.request

path = os.path.join(os.path.dirname(__file__), path)
logging.warn(path)
self.response.out.write(template.render(path, context))

def redirect(self, uri, permanent=False):
"""Issues an HTTP redirect to the given relative URL.

Args:
uri: a relative or absolute URI (e.g., '../flowers.html')
permanent: if true, we use a 301 redirect instead of a 302 redirect
"""
if permanent:
self.response.set_status(301)
else:
self.response.set_status(302)

absolute_url = urlparse.urljoin(self.request.uri, uri)
self.response.headers['Location'] = str(absolute_url)
self.response.clear()

The Controller class has a couple of methods of note:

def rendertemplate(self, context, path): which takes a dictionary (context) and the relative path to an HTML template. The templates themselves will use the Django template markup tags.

def redirect(self, uri, permanent=False): which does an HTTP 301 or 302 redirect.

Here's an example concrete Controller class where the view method is the action as defined using Routes.

import twitter as twitter
from controllers.Controller import Controller as controllerBase

class ConcreteController(controllerBase):
def view(self, id):

data = doSomething(id)

template_values['data'] = data

self.rendertemplate(template_values, '../views/template.html')

Saturday, April 25, 2009

Session handling with Memcache API in Google AppEngine

I've been trying to teach myself Python by witting a basic Twitter client in Google AppEngine. 

One of the things which I found lacking from the Webapp and Django frameworks was anything to manage sessions well. So, realising that the Memcache API has a timeout I figured that I could use that as the basis for session management. 

I ended up creating 2 classes, a SessionManager class to create, retrieve and delete sessions ans a Session object which would act as a simple dictionary to store arbitrary session values in. 

I would then be able to use the Session and SessionManager classes like this in my Controller class: 
class Controller(object):
def __init__(self, request, response):
self.request = request
self.response = response
self.sessionManager = SessionManager(self.request, self.response)
self.session = self.sessionManager.current()
self.initialize()


The SessionManager constructor needs both Request and Response objects because it needs to read and write Cookies. 

Here's the SessionManager class: 
from google.appengine.api import memcache
import datetime
import random

class SessionManager(object):
def __init__(self, request, response, timeout=1200):
self.timeout = timeout
self.request = request
self.response = response
self.cookieName = 'SID'
def current(self):
cookievalue = self.request.cookies.get(self.cookieName)
if ((cookievalue is not None) and (memcache.get(cookievalue) is not None)):
return Session(cookievalue, self.timeout, False)
else:
return self.createSession()
def createSession(self):
newId = self.createNewId()
memcache.set(key=newId, value=True, time=self.timeout, )
now = datetime.datetime.now()
inc = datetime.timedelta(seconds=self.timeout)
now += inc
self.setCookie(key=self.cookieName,value=newId,expires=now)
return Session(newId, self.timeout, True)

def destroySession(self):
self.clearCookie(self.cookieName)
def createNewId(self):
newHash = str(hash(datetime.datetime.utcnow().strftime('%Y%m%d%H%M%S%f'))) + str(random.random())
while memcache.get(newHash) is not None:
newHash = self.CreateNewId()
return newHash
def setCookie(self,key,value,expires,path='/'):
self.response.headers.add_header('Set-Cookie', key+'='+value+ ' path='+path+'; expires '+expires.strftime('%a, %d-%b-%Y %H:%M:00 %Z'))
def clearCookie(self,key):
self.setCookie(key=key,value='',expires=datetime.datetime.now())


I'm creating a unique sessionId by concatenating the current date/time with a random number. 

Here's the Session class: 
class Session(object):
def __init__(self, id, timeout, isNew=False):
self.id = id
self.IsNew = isNew
self.keys = dict()
self.timeout = timeout
memcache.set(key=self.id+'__keys', value=self.keys, time=timeout)
def __getitem__(self, key):
return memcache.get(self.id+'_'+key)
def __setitem__(self,key,value):
memcache.set(key=self.id+'_'+key, value=value, time=self.timeout)
self.keys[key] = value
memcache.set(key=self.id+'__keys', value=self.keys, time=self.timeout)
def hasKey(self, key):
self.keys = memcache.get(self.id+'__keys')
return (key in self.keys)


That's kind of it really. It's working well at the moment but I've not used it with very large numbers of concurrent users.

Sunday, February 22, 2009

Virtual site handler in .net

I cam across a situation the other day where I found that my ASP.NET was limited to 1 website and I wanted to be able to host more than one website. Each site wasn't going to be that big or clever so I decided to see if I could come up with an IHttpModule which would be able to make it serve different pages depending on which domain was requested.

The core of the solution I came up with is the VirtualSiteManager class which given a request will return a re-written path which an IHttpModule can rewrite to.
public class VirtualSiteManager
{
public VirtualSiteManager(string defaultSiteName)
{
_defaultSiteName = defaultSiteName;
Sites = new Dictionary<string, VirtualSite>();
}

private string _defaultSiteName;

private static VirtualSiteManager _instance;
private static Type _lock = typeof(VirtualSiteManager);

public static VirtualSiteManager Instance
{
get
{
lock (_lock)
{
if (_instance == null)
{
_instance = (VirtualSiteManager) WebConfigurationManager.GetSection("virtualSite");
}
}

return _instance;
}
}

public VirtualSite DefaultSite
{
get
{
VirtualSite defaultSite = null;

if (Sites.ContainsKey(_defaultSiteName))
{
defaultSite = Sites[_defaultSiteName];
}

return defaultSite;
}
}

public Dictionary<string, VirtualSite> Sites
{
get;
private set;
}

public void AddSite(VirtualSite site)
{
if (!Sites.ContainsKey(site.Host))
{
Sites.Add(site.Host, site);
}
else
{
Sites[site.Host] = site;
}
}

public void AddSite(string name, string host, string virtualPath)
{
AddSite(new VirtualSite(name, host, virtualPath));
}

public string RewritePath(HttpContext context)
{
string host = context.Request.Url.Host;
VirtualSite handlingSite = (Sites.ContainsKey(host)) ? Sites[host]: DefaultSite;
string currentPath = context.Request.CurrentExecutionFilePath;

context.Response.AddHeader("X-Debug-currentPath", currentPath);

string returnPath = handlingSite.VirtualPath + (currentPath.StartsWith("/") ? currentPath.Substring(1) : currentPath);

if (returnPath.EndsWith("/"))
{
returnPath += "index.aspx";
}

return returnPath;
}
}

I store the domains and what their virtual path's are in the Web.Config like this:
<configSections>
<section name="virtualSite" type="RomfordEvan.Web.Components.VirtualSite.VirtualSiteHandler, RomfordEvan.Web.Components"/>
</configSections>
<virtualSite default="default">
<site name="default" host="www.domain1.com" virtualPath="/"/>
<site name="localhost" host="localhost" virtualPath="/"/>
<site name="domain2" host="www.domain2.com" virtualPath="/domain2/"/>
</virtualSite>

The IConfigurationSectionHandler to read the Web.Config and create the VirtualSiteManager looks like this:
public class VirtualSiteHandler : IConfigurationSectionHandler
{
#region IConfigurationSectionHandler Members

public object Create(object parent, object configContext, System.Xml.XmlNode section)
{
XmlAttribute def = section.Attributes["default"];

string defaultSiteName = string.Empty;

if (def != null && def.InnerText.Length > 0)
{
defaultSiteName = def.InnerText;
}

VirtualSiteManager manager = new VirtualSiteManager(defaultSiteName);

XmlNodeList siteNodes = section.SelectNodes(".//site");

foreach (XmlNode siteNode in siteNodes)
{
manager.AddSite(siteNode.Attributes["name"].InnerText, siteNode.Attributes["host"].InnerText, siteNode.Attributes["virtualPath"].InnerText);
}

return manager;
}

#endregion
}

This works fine with the following caveats:
  • Under IIS6 it requires wildcard mapping to the ASP.NET ISAPI filter.
  • You will need to add HttpHandlers for all the file types which your site will serve (see below)

<add verb="GET,POST" path="*/" validate="true" type="System.Web.UI.PageHandlerFactory" />
<add verb="GET" path="*.html" validate="true" type="System.Web.StaticFileHandler" />
<add verb="GET" path="*.js" validate="true" type="System.Web.StaticFileHandler" />
<add verb="GET" path="*.gif" validate="true" type="System.Web.StaticFileHandler" />
<add verb="GET" path="*.jpg" validate="true" type="System.Web.StaticFileHandler" />
<add verb="GET" path="*.png" validate="true" type="System.Web.StaticFileHandler" />
<add verb="GET" path="*.css" validate="true" type="System.Web.StaticFileHandler" />

Wednesday, February 18, 2009

Generic, type safe Configuration Manager

I like to store lots of config data in the Web.Config and I get fed up with having to cast the ConfigSection to the concrete class:
MyConfigSectionHandler handler =
(MyConfigSectionHandler)
WebConfigurationManager.GetSection(sectionName);

So I've written a helper class where now you can use:
MyConfigSectionHandler config =
ConfigManager.Instance.GetSection<MyConfigSectionHandler>
(sectionName);

Here's the code - (I've implemented it as a Singleton for a reason I don't remember now):
public class ConfigManager
{
private static ConfigManager _instance;
private static object _lock = typeof(ConfigManager);

private ConfigManager()
{

}

/// <summary>
/// Gets a singleton instance.
/// </summary>
/// <value>The instance.</value>
public static ConfigManager Instance
{
get
{
lock (_lock)
{
if (_instance == null)
{
_instance = new ConfigManager();
}

return _instance;
}
}
}

/// <summary>
/// Gets the section.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="sectionName">Name of the section.</param>
/// <returns></returns>
public T GetSection<T>(string sectionName)
where T : class
{
return WebConfigurationManager.GetSection(sectionName) as T;
}
}

Friday, July 13, 2007

The Factory pattern with Attributes

I've been using the Factory design pattern for some time, it's nice and simple, creating different objects depending on what parameters you pass to a given 'Create' function. The only problem is that for most of the situations that I come across, all the Create function seems to do is contain a switch case statement, which is rather in-elegant and not really scalable.


public Cheese CreateCheese(string cheeseType)
{
Cheese newCheese = null;

switch (cheeseType)
{
case "Cheddar":
newCheese = CheddarCheese();
break;
case "Edam":
newCheese = EdamCheese();
break;
}

return newCheese;
}

The problem obviously is that (in this case) if you now wanted to add a new type of cheese, that you would have to add a new case statement to the list.

I recently found a few good examples of how to get around this problem using Reflection and custom Attributes, I especially liked the Implementing the factory pattern using attributes and activation solution by John Gunnarsson.

However, what I didn't like about this was that you swapped your switch case statement for a series of statements adding types to a collection:

factory.RegisterCheeseType(typeof(CheddarCheese));
factory.RegisterCheeseType(typeof(EdamCheese));

My alternate solution is to store this list a custom section within the config file. This way you can define the classes that the Factory can create outside the Factory Assembly and reference them in the config file. This is a good thing because it provides an extensible architecture allowing other developers to extend the functionality without recompiling the main assembly

The Factory class would now look like this:

CheeseFactory.cs



public class CheeseFactory
{
private CheeseFactorySettings settings;

public CheeseFactory()
{
settings = (CheeseFactorySettings)ConfigurationSettings.GetConfig("cheeseFactory");
}

public Cheese CreateCheese(string cheeseType)
{
return settings.GetCheese(cheeseType);
}
}


What we're doing here is to pass the actual decision as to which class to make to this CheeseFactorySettings class. You could argue that we're duplicating the 'Create' Factory method in both the CheeseFactorySettings class and the CheeseFactory class, and that that's a bad thing. If you think that smells a bit too much, then just stick the collection traversing logic in the CheeseFactorySettings.GetCheeseFactory() method into the CheeseFactory.CreateCheese() method. In any case, the CheeseFactorySettings class is defined below:

CheeseFactorySettings.cs



public class CheeseFactorySettings : CollectionBase
{
public CheeseFactorySettings(XmlNode section)
{
foreach (XmlNode node in section.ChildNodes)
{
if (node.Name == "formatter")
{
List.Add(Type.GetType(node.Attributes["type"].InnerText));
}
}
}

public Cheese GetCheeseFactory(string format)
{
foreach (Type implementation in List)
{
CheeseFactoryAttribute[] attributeList = (CheeseFactoryAttribute[])implementation.GetCustomAttributes(typeof(CheeseFactoryAttribute), true);

foreach (object attribute in attributeList)
{
if (attribute is CheeseFactoryAttribute)
{
if (((CheeseFactoryAttribute) attribute).Format == format)
{
return (Cheese) Activator.CreateInstance(implementation);
}
else
{
break;
}
}
}
}

throw new InvalidCastException("Could not find a Cheese implementation for this format");
}
}


A possible addition you could do to this class is to implement the OnValidate method of the CollectionBase class to prevent classes that do not inherit from Cheese from being added to the collection of types. (The base.OnValidate call checks to see if the newly added value is not a null reference).


protected override void OnValidate(object value)
{
base.OnValidate(value);

if (!(value as Type).IsSubclassOf(typeof(Cheese)))
{
throw new InvalidCastException("Cheese types must inherit from Cheese");
}
}


There's a couple more final classes to go before the whole barebones of the solution is complete. Firstly the IConfigurationSectionHandler implementation to read the values from the config file at runtime. This is pretty standard stuff:

CheeseFactorySectionHandler.cs



public class CheeseFactorySectionHandler : IConfigurationSectionHandler
{
public object Create(object parent, object configContext, XmlNode section)
{
CheeseFactorySettings settings = new CheeseFactorySettings(section);

return settings;
}
}


All we're doing is creating a new CheeseFactorySettings class and passing through the XmlNode from the config file to it's constructor.

Thursday, November 23, 2006

Viewing RSS feeds in a browser

It's an obvious little trick and I don't know why I hadn't thought of it before, but MSDN have updated their RSS feeds with a reference to an XSL so that browsers that support XSL will render the feed as XHTML rather than displaying the source.



All it does is to force the browser to transform the rss xml to XHTML thus:



<?xml version="1.0" encoding="utf-8"?>

<?xml-stylesheet type='text/xsl' href='rsspretty.xsl' version='1.0'?>
<rss xmlns:msdn="http://msdn.microsoft.com/aboutmsdn/rss/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:msdndomain="http://msdn.microsoft.com/aboutmsdn/...
rss/domains" version="2">

<channel>

<title>MSDN: Visual C#</title>

...

</channel>

</rss>

Friday, October 15, 2004

Google Desktop Search

Have you seen this new Desktop search tool from Google? Essentially it's a search engine/application for use on your local machine, rather than the web. By default it indexes all files on your machine.

Google Desktop Search finds:

  • Outlook / Outlook Express
  • Word
  • AOL Instant Messenger
  • Excel
  • Internet Explorer
  • PowerPoint
  • Text
A neat trick (which I guess it does with cookies for the www.google.com site) is that it puts a link on the existing www.google.com site to search your desktop instead of the web. A nice thing that it does is that it searches your Outlook emails - and returns results in super quick time! From a technical point of view, what it looks like is that it installs some kind of mini-web server - you search through the browser on an address of http://127.0.0.1:xxxx/.

Wednesday, October 13, 2004

Book Review: Design Patterns in C#


Design Patterns in C#
Covering all the classic Gang of Four software design patterns in one straighforward book, Steven Metsker gives some decent concrete examples of their application with the corresponding UML.

Design patterns help developers leverage the wisdom of the entire software community to build more robust higher-quality software. Now, for the first time, there is a complete patterns guide specifically for C# and .NET programmers.



What's good about the book is the writing style and the exercises. The author obviously has done a good job of applying design patterns to the .NET framework, utilising events, delegates and reflection. On the downside, what would be additionally useful would be more discussion about applying design patterns in non-standard situations.

All in all, this book would be an excellent addition to the stack of books you keep next to your workstation. My copy is almost always open at the pattern I'm using at the moment.
You can get a sample chapter of the book from the publishers Addison Wesley
Sample chapter

Wednesday, October 06, 2004

Attack of the Canonicalization-alisers

Canonicalization attacks are where malicious users try to gain access to files that they shouldn't by entering file names and paths that the system understands, but that you don't expect. It's quite possible that the following filepaths could (depending on the context) all point to the same file:


c:\dir\test.dat

test.dat

..\..\test.dat


When a URL is received by a Web server, the server maps the request to a file system path that determines the response. The canonicalization routine that is used to map the request must correctly parse the URL to avoid serving or processing unexpected content. For more information about canonicalization, visit the following Microsoft Web site:
http://msdn.microsoft.com/library/.../THCMCh04.asp

Also related to this KB article is somthing that Scott Watermasysk highlighted
ASP.NET Security Issue and Guidance about an ASP.NET Forms authentication vulnerability, which can be cured with the appropriate canonical checks.

Monday, October 04, 2004

Using Reflection to Bind Business Objects to ASP.NET Form Controls

This looks like a really useful way to simplify databinding ASP.NET controls to a generic business object. I've yet to use this in a fully fledged live project and so I'm not sure how accurate their performance metrics are. Off the top of my head I think I would implement these two methods 'BindControlsToObject' and 'BindObjectToControls' in a new class derived from the Page object, but whatever takes your fancy really.