Accessing State

Published by on
Tags:

When looking at code I often see session/cache access like this:

    public void AddToBasket(Product newProduct)
    {
        Basket myBasket = (Basket) Session["basket"];
        myBasket.AddProduct(newProduct);
        Session["basket"] = myBasket;
    }

The problem with this is that the session is accessed using a natural language. The problem with using a natural language is that the word "basket" could be used by another developer to store something else in the session in a different bit of code and therefore overwriting by mistake any value stored in the state object. The other problem is that every time the developer needs to access the object the developer has to write code to cast the object to the correct type.

A better solution is to hide this all in a property and use a guid as the key for the object in session/cache. The advantage of using the guid is that you can guarantee that no other developer will pick the same key. This ensures that your state object is never over overwritten by mistake:

    public Basket CurrentBasket
    {
        get
        {
            return (Basket)Session["{DED12A99-E792-43ce-8899-253A0ECC0C6B}"];
        }
        set
        {
            Session["{DED12A99-E792-43ce-8899-253A0ECC0C6B}"] = value;                
        }
    }
    public void AddToBasket(Product newProduct)
    {
        CurrentBasket.AddProduct(newProduct);
    }

A developer can now get direct access to the object stored in state without worrying about how the object is stored. This means it could be stored in some other state mechanism by only changing the accessors in the property. Of course you still need to deal with the issue of a null reference when using the state object, but this can be easily handled by modifying the accessor's bodies to handle the null exception to either create a new object or throw an error. The other change I would make is to store the key in a variable to make the code a little more readable and manageable:

    private const string _currentBasketKey = "{4BD1E9CC-AAAF-44f3-B487-9425B76BE89F}";
    public Basket CurrentBasket
    {
        get
        {
            if(Session[_currentBasketKey] == null) Session[_currentBasketKey] = new Basket();
            return (Basket)Session[_currentBasketKey];
        }
        set
        {
            Session[_currentBasketKey] = value;                
        }
    }

BlogEngine to Twitter - Part 2

Published by on
Tags:

A couple of weeks ago I blogged about the BlogEngine to Twitter extension I had written. However since this time I have been playing around with it a noticed a small problem. I would write a post and publish it, but once published I would notice a small mistake. I would then edit the post and publish again. Both these publishes would send a tweet to twitter. If you noticed 2 or 3 mistakes you start to produce twitter spam and everyone you know starts to get annoyed. So I wanted to modify the extension so at the base of the Add Entry page I would have a check box that when checked would send a tweet when the item is published like so:



To get this working on your site is a very simple. We first need to add the check box to the Add Entry page. This can be found in /admin/pages/add_entry.aspx. Insert the checkbox after the other checkboxes like so (line 160)

  157       <td>

  158         <asp:CheckBox runat="server" ID="cbEnableComments" Text="<%$ Resources:labels, enableComments %>" Checked="true" TabIndex="14" />

  159         <asp:CheckBox runat="server" ID="cbPublish" Text="<%$ Resources:labels, publish %>" Checked="true" TabIndex="15" />

  160         <asp:CheckBox runat="server" ID="cbTwitter" Text="<%$ Resources:labels, twitterCheckbox %>" Checked="true" TabIndex="16" />

  161       </td>

Next we need to wire this into the save button click. Open the code behind page for the add entry page (add_entry.ascx.cs) and find the method btnSave_Click. Just after the validation check add line 194:

 

  188   private void btnSave_Click(object sender, EventArgs e)

  189   {

  190     if (!Page.IsValid)

  191       throw new InvalidOperationException("One or more validators are invalid.");

  192 

  193         //Twitter integration

  194         Twitter.SendTweet = cbTwitter.Checked;

Next open the labels.resx file found in /App_GlobalResources and add the entry"twitterCheckbox" with the value "Send as tweet".

All you need to do now is download the new Twitter.cs (3.01 kb) extension file and drop the new one into the Extension directory (/App_Code/Extensions) and you are good to go. Remember from now on a tweet won't be sent unless the "Send as tweet"  check box is checked. By default it is checked.

Happy blogging.

Sitecore Sublayout Parameters

Published by on
Tags:

Ok close on the heels of my previous post comes another useful extension method (I might be getting carried away with these). This extension method is to help those of you developing in Sitecore. I came up against the problem today of needing to extract the parameters associated to sub-layout.

For those unclear on parameters and sub-layout these can be found by going to a template in Sitecore (I am assuming Sitecore 5.3), click on the Presentation tab and then click Layout. Click the Edit button beneath a layout with a sub-layout. Click on the Sub-layout and in the new popup select the parameters tag. You should see the following screen:

Parameters are useful because they allow you to pass in specific values to a layout.Ok so the code I originally looked at was taken from that given by Sitecore:

   25             Database db = Sitecore.Context.Database;

   26             Item item = db.Items["/sitecore/templates/SC Printers/Section"];

   27             string rend = item.Fields["__renderings"].Value;

   28             LayoutDefinition layout = LayoutDefinition.Parse(rend);

   29             DeviceItem dev = Sitecore.Context.Device;

   30             DeviceDefinition device = layout.GetDevice(dev.ID.ToString());

   31             Item mySublayout = db.Items["/sitecore/layout/Sublayouts/Two columns"];

   32             RenderingDefinition rendering = device.GetRendering(mySublayout.ID.ToString());

   33             Response.Write("Parameters: " + rendering.Parameters);

   34             Response.Write("<br/>");

   35             Response.Write("DataSource: " + rendering.Datasource);

However there are several problems with this code. It assumes that the item has a rendering specified and not inherited from a template and it will also fail if the device that Sitecore picks up has not associated layout; for example if I specify a device for Firefox users but then don't specify and layouts for it it shoud fall back to the default device, but this code doesn't. Another problem with the code is that it returns the list of parameters as a string, if you have more than one parameter they are all concantinated into one long string. I wanted to seperate these out so that they were more usable. The solution is the following code:

   21         private static readonly string _renderingField = "__renderings";

   22 

   23         public static KeyValuePair<string,string> [] GetSubLayoutParameters(this Item item, Guid subLayoutId)

   24         {         

   25 

   26             string rend = item.Fields[_renderingField].Value;

   27             //if there are no renderings on the item use the template render

   28             if (rend.IsNullOrEmpty())

   29             {

   30                 rend = item.Fields[_renderingField].InheritedValue;

   31             }

   32             LayoutDefinition layout = LayoutDefinition.Parse(rend);

   33             DeviceItem dev = Sitecore.Context.Device;

   34             DeviceDefinition device;           

   35 

   36             //get the current device

   37             device = layout.GetDevice(dev.ID.ToString());

   38             //if the current device has no layout fail back

   39             if (device.Layout == null)

   40             {

   41                 dev = dev.FallbackDevice;

   42                 device = layout.GetDevice(dev.ID.ToString());

   43             }

   44 

   45             //get the rendering for the device

   46             RenderingDefinition rendering = device.GetRendering(new ID(subLayoutId).ToString());

   47 

   48             //create a list of key values

   49             List<KeyValuePair<string, string>> parameters = new List<KeyValuePair<string, string>>();

   50             string [] pairs = rendering.Parameters.Split('&');

   51 

   52             foreach(string pair in pairs){

   53                 if(!pair.IsNullOrEmpty()){

   54                     string [] vals = pair.Split('=');

   55                     if(vals.Count() == 2){

   56                         parameters.Add(new KeyValuePair<string,string>(vals[0], vals[1]));

   57                     }

   58                 }

   59             }

   60 

   61             return parameters.ToArray();              

   62 

   63         }

You will notice that I have used a couple of the extension methods from my Extension Method - Part 1 post. I hope this makes getting parameters a little easier for everyone out there.

Further reading: How to find the currently executing sublayout in Sitecore

Useful Extensions Methods - Part 1

Published by on
Tags:

I am going to start blogging extension methods that I think are useful. The onesI have detailed below are very simple but I think they make code more readable andquicker to write.

The first is to do with passing in objects to have them formatted in the outputtext. The standard way to do this is:

   12        string name = "mike";

   13        int age = 25;

   14        string text = "Hello{0}, You are {1} years old";

   15 

   16        Console.WriteLine(string.Format(text,name, age));

I don't like this, why should I have to use a static method on the string type?Why can't this be a method on the instance of the string? So I created an extensionfunction to do this:

   26        public staticstring Params(thisstring text, paramsobject[] args)

   27        {

   28            return string.Format(text, args);

   29        }

And now I can do the same as above but swap out line 16 for:

   18        Console.WriteLine(text.Params(name, age));

The second one I find useful is for casting objects to a different types. Take forexample the following code example where I have an interface and a concrete class:

   38     public interfaceICalc

   39     {

   40        int Add(inta, int b);

   41     }

   42 

   43     public class Calc : ICalc

   44     {

   45        public stringCalcName { get { return"My Calc"; } }

   46 

   47        #region ICalc Members

   48 

   49        public intAdd(int a, intb)

   50        {

   51            return a + b;

   52        }

   53 

   54        #endregion

   55     }

 

I create an instance of the concrete class like so:

 

   20            ICalc myCalc = new Calc();

 

Say for some reason (there could be many) I need to access the CalcName propertythat is not defined in the interface, this means I need to cast my current instanceto the concrete type. This would normally be done like so:

 

   23            ((Calc)myCalc).CalcName;

 

This to me is not a nice solution. So I created this extension function:

 

   35         public static T AsType<T>(this object obj)

   36         {

   37             return (T) obj;

   38         }

 

Now I can do the same as above but like so:

 

   21             myCalc.AsType<Calc>().CalcName;

 The joy of extension method is that even simple ones can make coding quicker and easier to read. Let me know if you have some useful extension functions that I could make use of.

MVP pushing data to the view

Published by on
Tags:

The other day I was chatting to my friend and we were discussing the model view presenter (MVP) design pattern. We got onto the discussion of display collections of items in the view. It gave me a few thoughts about the different techniques that could be used. Each technique has some advantages and disadvantages and I hope to discuss each of these in turn.

We are going to have a presenter that performs a simple task. I want it to display a list of products to the user. I am going to keep the presenter as simple as possible; the constructor takes no arguments and the class consists of a single Init method that starts the whole process off. When working with MVP I prefer to have an Init method which I will call to kick the whole process off.

So my presenter has this simple template:

     8     public class ProductListPresenter

    9     {

   10         IProductListView _view;

   11         IProductService _service;

   12 

   13         public ProductListPresenter(IProductListView view, IProductService service)

   14         {

   15             _view = view;

   16             _service = service;

   17         }

   18 

   19         public void Init()

   20         {

   21             //TODO: pass the list of products to the view.

   22         }

   23     }

 I am going to use a simple Product entity to represent each product. The class is as follows:

    8     public class Product

    9     {

   10         public string Name { get; set; }

   11         public double Price { get; set; }

   12         public string Description { get; set; }

   13     }

The first method I am going to look at is passing an array of Products to the view and then letting the view deal with how it iterates over the collection of Products. The interface looks like: 

    8     public interface IProductListView

    9     {

   10         public Product[] Products { get; set; }

   11     } 

The presenters Init method can then pass the list of products can be passed directly into the view from the service (yes this is a very simplistic example). 

    19         public void Init()

   20         {

   21             _view.Products = _service.GetAllProducts();

   22         }

 I can now take advantage of some of .Net’s web controls. For example I want to use the System.Web.UI.WebControls.Repeater control to display product information. In the concrete view I can simple the Products property directly to the Repeater controls data source property:

   15         public Product[] Products

   16         {

   17             get

   18             {

   19                 return (Product[])_productRepeater.DataSource;

   20             }

   21             set

   22             {

   23                 _productRepeater.DataSource = value;

   24             }

   25         }  

As you can see this is a very quick way wiring views and presenters but has the disadvantage where the get accessor has to cast from object to the Product array. However we could remedy this by removing the get accessor from the IProductView interface. Another disadvantage is that this method does not give the presenter control over what information about the product is displayed by the view. For example if the product contains an additional field called “ConfidentialInformation” the developer implementing the view would have access to this field and could accidentally render this data to the user. This really isn’t a problem if the developers working on the views have good spec’s to work from but might cause a problem if you are expecting a third party to develop the views. Another lesser issue is exposing entities from the domain to the view which you may or may not wish to do depending on how strict you want your layer separation to be.

Another method is to have your view expose a method which the presenter calls to render the list to the user. For the  The IPresenterView would change to the following:

    8     public class IProductListView

    9     {

   10         void RenderProduct(string productName, double productCost);

   11     }

The presenters Init method then changes to:

   19         public void Init()

   20         {

   21             foreach (Product product in _service.GetAllProducts())

   22             {

   23                 _view.RenderProduct(product.Name, product.Price);

   24             }

   25         }

  

This method gives complete control over what the view renders to the user but has the disadvantage that it makes using the inbuilt .Net web controls more complex; for example we could not simple wire straight into the data source of the render control like we did before. However we can create a simple web control to do something similar:

 

   10     public class ProductView : HtmlControl,  IProductListView

   11     {

   12         public ProductView() : base("ul") { }

   13 

   14         #region IProductListView Members

   15 

   16         public void RenderProduct(string productName, double productCost)

   17         {

   18             HtmlGenericControl listItem = new HtmlGenericControl("li");

   19             listItem.InnerText = string.Format("{0} - {1:C}", productName, productCost);

   20             this.Controls.Add(listItem);   

   21 

   22         }

   23 

   24         #endregion

   25     }

  

   

As yet I am unsure which technique is better and I think both have the benefits. I find that for speed and simplicity the first technique works better and I can trust the developers of the views not to implement incorrect properties, however there is still something about it that makes me feel uncomfortable. Let me know your thoughts and what other techniques you might have for handling collections of items.

 

All blogs tagged with '.net'

Projects

Have you read?

Custom Sitecore fields and master templates



Blogs by date