Pages

Showing posts with label C#. Show all posts
Showing posts with label C#. Show all posts

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);
}

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>

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.

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

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.