Sunday, July 18, 2010

Dynamic .NET 4.0 to the rescue

With the new .NET 4.0 features in mind I was reviewing some code from a previous project written in C# 3.0. The application was interacting with Excel quite a lot. One of the requirements of the project was that Excel should be able to represent a remote database structure (with one-many and many-many relationships)  accessed through a web service with drop downs for related values.

The old code for doing the dynamic adding of VBA combo boxes in the cells of the relevant columns looked like the following

internal void AddComboBox(Worksheet sheet, Range relateCell, FieldMetadata fieldMetadata, string selectedValue)
{
    Microsoft.Office.Interop.Excel.Shape comboBox = sheet.Shapes.AddOLEObject(
        "Forms.ComboBox.1",
        Missing.Value, 
        Missing.Value,
        Missing.Value, 
        Missing.Value,
        Missing.Value, 
        Missing.Value,
        relateCell.Left,
        relateCell.Top,
        relateCell.Width,
        relateCell.Height
    );

    OLEObject oleComboBox = (OLEObject)comboBox.DrawingObject;
    MSForms.ComboBox cb = (MSForms.ComboBox)(oleComboBox.Object);

    ...

    VbaHandler.SetComboBoxEventHandler(ExcelApplication.ActiveWorkbook, sheet, oleComboBox);
    RegisterCallbackHandlerForMacroModule(sheet);

    // save ComboBox to disable garbage collection on it
    SupportComboBoxes.Add(new KeyValuePair<int, string>(relateCell.Row, GetDataColumnNameByIndex(sheet, relateCell.Column)), oleComboBox);
}


Using optional parameters, named parameters and the dynamic keyword made the code nicer and remove a lot of the plumbing syntax from previous versions of the .NET framework. The code now looks like


internal void AddComboBox(Worksheet sheet, Range relateCell, FieldMetadata fieldMetadata, string selectedValue)
{
    Microsoft.Office.Interop.Excel.Shape comboBox = sheet.Shapes.AddOLEObject(
        ClassType : "Forms.ComboBox.1",
        Left      : relateCell.Left,
        Top       : relateCell.Top,
        Width     : relateCell.Width,
        Height    : relateCell.Height
    );

    dynamic oleComboBox = comboBox.DrawingObject;
    dynamic cb = oleComboBox.Object;

    ...

    VbaHandler.SetComboBoxEventHandler(ExcelApplication.ActiveWorkbook, sheet, oleComboBox);
    RegisterCallbackHandlerForMacroModule(sheet);

    // save ComboBox to disable garbage collection on it
    SupportComboBoxes.Add(new KeyValuePair<int, string>(relateCell.Row, GetDataColumnNameByIndex(sheet, relateCell.Column)), oleComboBox);
}

Tuesday, July 06, 2010

Serializing anonymous classes

I ran into a situation where I found a need for a compound class that contained bits and pieces from quite a few other classes. The class would only be used for pushing data to the front end as a JSON formatted response in this rather special scenario. Rather than defining a new model/class I found that you can use an anonymous class and serialize it.

A brief note on the missing [DataContract]/[DataMember] and [Serializable] attributes, they are not needed for serializing public fields or properties in .NET 3.5 SP1.


Here are the predefined contrived classes
namespace MvcApplication1.Models
{
    public class Contact
    {
        public string Name { get; set; }
        public string PreferedEmail { get; set; }
    }
}

namespace MvcApplication1.Models
{
    public class ContactDetails
    {
        public string[] Emails { get; set; }
    }
}


controller
using System.Web.Mvc;
using MvcApplication1.Models;
using System.Web.Script.Serialization;

namespace MvcApplication1.Controllers
{
    public class HomeController : Controller
    {
        /// 
        /// Shows serializing a new anonymous class that contains some preexisting classes
        /// And serializing this using the JavaScriptSerializer
        /// 
        /// 
        public ActionResult SerializeAnonymousClassToJson()
        {
            var compositeContacts = new
            {
                MyContact = new Contact()
                {
                    Name = "Donald Duck",
                    PreferedEmail = "donaldduck@disney.com"
                },
                MyContactDetails = new ContactDetails()
                {
                    Emails = new[] 
                    { 
                        "donald@disney.com", 
                        "unclescroogenephew@scrooge.com" 
                    }
                }
            };
            JavaScriptSerializer jss = new JavaScriptSerializer();
            ViewData["Json"] = jss.Serialize(compositeContacts);
            return View();
        }
    }
}

and view.
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<dynamic>" %>
<%= ViewData["Json"]%> 

and the serialized data looks like this

{"MyContact":{"Name":"Donald Duck","PreferedEmail":"donaldduck@disney.com"},"MyContactDetails":{"Emails":["donald@disney.com","unclescroogenephew@scrooge.com"]}}