Passing anonymous functions with trailing parameters in CoffeeScript

This may be obvious but it took me a few minutes to figure it out

1 2 3
setTimeout ->
alert 'hello world'
,5000
view raw gistfile1.coffee hosted with ❤ by GitHub

Compiles to

1 2 3
setTimeout(function() {
return alert('hello world');
}, 5000);
view raw gistfile1.js hosted with ❤ by GitHub

ASP.NET MVC 3 and Select Lists

The full source code for this post can be found at https://github.com/jhicks/ASPNETMVCSelectLists

The generally guidance for populating drop down lists and list boxes in ASP.NET MVC is to put a collection of SelecListItem instances into the ViewBag or ViewData collections then write some code in the View to create the drop down list. It looks something like:

[HttpGet]
public ActionResult Index()
{
    ViewData[“MyList”] = new List<SelectListItem> 
    { 
        new SelectListItem {Text =1”, Value =1}
    };
    return View();
}
 
[HttpPost]
public ActionResult Index(MyEditModel form)
{
    if(ModelState.IsValid) 
    {
        // do something
        return RedirectToAction("Index");
    }
 
    // have to repopulate the ViewData with list item data
    ViewData[“MyList”] = new List<SelectListItem> 
    { 
        new SelectListItem {Text =1”, Value =1}
    };
    return View(form);
}

And in the view

@Html.DropDownListFor(m => m.SelectedItem, ViewData[“MyList”])
@Html.ListBoxFor(m => m.SelectedItems, ViewData[“MyList”])

 

What’s wrong with this, you may ask?  Well nothing really, however I don’t like it.  First, the controller is tightly coupled to the representation of the data by knowing that the view requires data to populate a drop down list.  Second, if there is a validation error on form submission, you will once again have to go grab the data and put it back in the ViewData collection.  And last, getting the list data is never as easy as what I have shown here.  Some screens have multiple drop down lists and that data comes from a plethora of sources including databases, web services, and static lists (i.e. US states).

The solution?  Model Metadata!  ASP.NET MVC provides a way to add custom metadata to our view models by way of the System.Web.Mvc.ModelMetadataProvider.  The default implementation is System.Web.Mvc.DataAnnotationsModelMetadataProvider which is what parses all those Data Annotation attributes on your view models.  My solution extends DataAnnotationsModelMetadataProvider to look for an attribute that identifies a class that provides the select list data to the view.

 

The Implementation

To get started, define an interface to provide the drop down list data.

public interface ISelectListProvider
{
    void Provide(ModelMetadata modelMetadata);
}

 

Now we need an attribute to tell our model metadata provider which ISelectListProvider to use. It extends the UIHintAttribute to tell the ASP.NET MVC templating system which view template to use.

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class SelectListProvidedByAttribute : UIHintAttribute
{
    private static readonly Type RequiredType = typeof(ISelectListProvider);
 
    public Type SelectListProvider { get; private set; }
 
    public SelectListProvidedByAttribute(Type selectListProvider) : base("SelectListProvider")
    {
        if (!RequiredType.IsAssignableFrom(selectListProvider))
        {
            var message = String.Format("{0} does not implement {1}", selectListProvider, RequiredType);
            throw new ArgumentException(message, "selectListProvider");
        }
 
        SelectListProvider = selectListProvider;
    }
}
@* Views/Shared/EditorTemplates/SelectListProvider.cshtml *@
@{
    var mmd = ViewContext.ViewData.ModelMetadata;
    var values = mmd.AdditionalValues["SelectList"] as IEnumerable<SelectListItem>;
}
@(mmd.ModelType.IsArray ? Html.ListBox("", values) : Html.DropDownList("", values))

 

And finally a ModelMetadataProvider implementation that ties it all together:

public class CustomModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
    protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
    {
        var attributeList = attributes.ToList();
        var modelMetadata = base.CreateMetadata(attributeList, containerType, modelAccessor, modelType, propertyName);
 
        ProvideSelectList(modelType, propertyName, modelMetadata, attributeList);
 
        return modelMetadata;
    }
 
    private static void ProvideSelectList(Type modelType, string propertyName, ModelMetadata modelMetadata, IEnumerable<Attribute> attributeList)
    {
        var attr = attributeList.OfType<SelectListProvidedByAttribute>().SingleOrDefault();
        if (attr == null)
        {
            return;
        }
 
        var providerType = attr.SelectListProvider;
 
        // NOTE: replace this with some type of IoC in real app
        //var provider = DependencyResolver.Current.GetService(providerType) as ISelectListProvider;
        var provider = Activator.CreateInstance(providerType) as ISelectListProvider;
 
        if (provider == null)
        {
            throw new Exception(String.Format("Could not find a select list provider of type {0} as defined on property {1} of {2}", providerType, propertyName, modelType));
        }
 
        provider.Provide(modelMetadata);
    }
}

 

An Example

An example is in store to show how all this ties together to make your life a lot easier.  It consists of a simple view model that has drop down lists for an enum, an array of enums, and an entity keyed by a value type, a nullable value type, and a reference type.

public enum BuildingType
{
    SingleFamily,
    MultiFamily,
    Commercial,
    Apartment,
    Condo,
    Townhouse
}
 
public class HomeEntity
{
    public long Id { get; set; }
    public string Name { get; set; }
}
 
public class HomeModel
{
    [SelectListProvidedBy(typeof(EnumSelectListProvider<buildingtype>))]
    public BuildingType BuildingType { get; set; }
 
    [SelectListProvidedBy(typeof(EnumSelectListProvider<buildingtype>))]
    public BuildingType[] ArrayOfBuildingTypes { get; set; }
 
    [SelectListProvidedBy(typeof(HomeSelectListProvider))]
    public long HomeId { get; set; }
 
    [SelectListProvidedBy(typeof(HomeSelectListProvider))]
    public long? HomeIdNullable { get; set; }
 
    [SelectListProvidedBy(typeof(HomeSelectListProvider))]
    public string HomeIdAsString { get; set; }
}

 

The view to show the model is pretty simple.  Notice how it uses the Html.EditorFor instead of specifing Html.DropDownListFor or Html.ListBoxFor.

@* Views/Home/Index.cshtml *@
@using SelectLists.Models
@model HomeModel
 
@{
    ViewBag.Title = "Home";
}
 
@using (Html.BeginForm())
{
    <div>
        <h3>Single Not Nullable</h3>
        @Html.EditorFor(m => m.BuildingType)
    </div>
 
    <div>
        <h3>Array</h3>
        @Html.EditorFor(m => m.ArrayOfBuildingTypes)
    </div>
 
    <div>
        <h3>Custom Type By Nullable Value</h3>
        @Html.EditorFor(m => m.HomeIdNullable)
    </div>
 
    <div>
        <h3>Custom Type By Value</h3>
        @Html.EditorFor(m => m.HomeId)
    </div>
 
    <div>
        <h3>Custom Type by Reference Value</h3>
        @Html.EditorFor(m => m.HomeIdAsString)
    </div>
    <input type="submit" value="Submit" />
}

 

The controller is pretty simple since it no longer has to deal with placing the list items into the ViewData.

public class HomeController : Controller
{
    [HttpGet]
    public ActionResult Index()
    {
        return View();
    }
 
    [HttpPost]
    public ActionResult Index(HomeModel form)
    {
        // ... validation failed, display the form again
        return View(form);
    }
}

 

And finally the ISelectProvider implementations that provide the select lists for the enum type and entity type.

public abstract class SelectListProviderBase : ISelectListProvider
{
    private static readonly SelectListItem EmptyItem = new SelectListItem {Text = "",Value = ""};
 
    public void Provide(ModelMetadata modelMetadata)
    {
        var modelType = modelMetadata.ModelType;
        var value = modelMetadata.Model;
 
        var items = new List<SelectListItem>();
        items.AddRange(GetListItems());
 
        object selectList;
        if (modelType.IsArray)
        {
            selectList = new MultiSelectList(items, "Value", "Text", value as Array);
        }
        else
        {
            if (modelType.IsClass || (modelType.IsGenericType &amp;&amp; modelType.GetGenericTypeDefinition().Equals(typeof(Nullable<>))))
            {
                items.Insert(0,EmptyItem);
            }
 
            selectList = new SelectList(items, "Value", "Text", value);
        }
        modelMetadata.AdditionalValues.Add("SelectList", selectList);
    }
 
    protected abstract IEnumerable<selectlistitem> GetListItems();
}
 
public class EnumSelectListProvider<TEnum> : SelectListProviderBase
{
    protected override IEnumerable<SelectListItem> GetListItems()
    {
        var enumType = typeof (TEnum);
        var values = Enum.GetValues(enumType);
        return from object entry in values
                select new SelectListItem
                {
                    Text = Enum.GetName(enumType, entry),
                    Value = entry.ToString()
                };
    }
}
 
public class HomeSelectListProvider : SelectListProviderBase
{
    protected override IEnumerable<SelectListItem> GetListItems()
    {
        // in production this would come from the db or NHibernate or maybe from a web service
        for (var counter = 1; counter &lt; 6; counter++)
        {
            yield return new SelectListItem
            {
                Text = "Home " + counter, Value = counter.ToString()
            };
        }
    }
}

 

Summary

Move code that generates drop down list data out of controllers and into metadata on the view models where it belongs. The model metadata extension point in ASP.NET MVC 3 is a powerful tool to accomplish this. It makes the controllers more DRY and reduces the coupling between them and the views.

Getting Started With db4o

Recently I was starting up a new project and got to the point where I needed to persist some of my objects to a database.  Instead of doing what I normally do, NHibernate and SQL Server, I decided to try something new.  A colleague of mine told me about db4o a few weeks ago so I decided to take it for a spin.

Just in case you don’t already know, an object-oriented database stores your objects directly in the database without having to map it into a relational structure.  No object-relational impedance mismatch that we usually get with relational databases.

db4o uses a custom file format to store your objects.  It supports both local in-process access as well as remote access over TCP/IP.   It supports all your standard CRUD stuff, has transactions, and extensive query support through a custom query API and LINQ.

Get db4o

Head on over to http://www.db4o.com and download db4o.  In addition to the db4o runtime, you can also install ObjectManager Enterprise which is a Visual Studio plugin for browsing objects in a db4o database.  OME is an object browser for a db4o database. 

The SDK and runtime are in the bin\net-3.5 directory of the installation location.  Db4objects.Db4o.dll, Db4objects.db4o.CS.dll, and Db4objects.Linq.dll are the core libraries.  There are other libraries in the same folder that add additional features.

Host db4o

There are two ways to host db4o: embedded single client and client/server.  The client/server mode can be configured for local or remote connections.

Embedded Single-Client Mode

Embedded single client mode is great if you have a desktop app that needs to persist some data to the local file system.  It allows only a single connection to the database.  Other connections will get an exception when opening the database file.  The embedded database API is in the Db4objects.Db4o.dll library

To open or create a new embedded database, call Db4oEmbedded.OpenFile, passing in a configuration object and the path to the database file.  The returned object will be of type IObjectContainer and is the primary interface for interacting with your database.

// db4o uses the .yap file extension
using(IObjectContainer db = Db4oEmbedded.OpenFile(Db4oEmbedded.NewConfiguration(),@".\Database.yap"))
{
    // do something here
    // close the database connection
    db.Close();
}

Client/Server Mode

When you need more than a single connection to the database, as typical in web applications, you need to use the client/server mode. Whether you need remote, out of process connections or local inprocess connections will determine how the client/server mode is configured. For in process connections only, configure the server to run with a port number of zero(0). To allow remote connections, give the server a port number greater than zero. Remote connections use TCP/IP. The client/server API is in the Db4objects.db4o.dll library.

To start the database server, call Db4oClientServer.OpenServer, passing in a configuration object, the path to the database file, and a port number.  A port number greater than zero will allow TCP/IP connections to db4o. For servers that will allow remote connections, you need to grant access to a username and specify a password for the username

IObjectServer localServer = Db4oClientServer.OpenServer(Db4oClientServer.NewServerConfiguration(),@".\LocalOnlyDatabase.yap",0);
IObjectServer remoteCapableServer = Db4oClientServer.OpenServer(Db4oClientServer.NewServerConfiguration(), @".\RemoteCapableDatabase.yap",1677);
 
// give access to a user.  clients will connect using this username/password
remoteCapableServer.GrantAccess("username","password");
 
// remember to close the server the hosting process stops
localServer.Close();
remoteCapableServer.Close();

How you open a client is determined by if you are connecting to a local or a remote server. For local servers, you call the OpenClient method of the IObjectServer. For remote servers, you call Db4oClientServer.OpenClient, passing in a configuration, host name, port, username, and password

using(IObjectContainer localClientConnection = localServer.OpenClient())
{
    // do the work here
    // make sure to close the connection
    localClientConnection.Close();
}
 
using(IObjectContainer remoteClientConnection = Db4oClientServer.OpenClient(Db4oClientServer.NewClientConfiguration(),"host",1677,"username","password"))
{
    // do the work
    // close the connection
    remoteClientConnection.Close();
}

The CRUD

db4o handles the basic CRUD operations.

public class MyDataStructure
{
    public string Name { get;set; }
}
 
// defined in some class
using(IObjectContainer db = Db4oEmbedded.OpenFile(Db4oEmbedded.NewConfiguration(),@".\Database.yap"))
{
    var data = new MyDataStructure {Name = "db4o crud example"};
 
    // CREATE
    db.Store(data);
 
    // RETRIEVE
    data = db.AsQueryable<mydatastructure>().Single(x => x.Name == "db4o crud example");
 
    // UPDATE
    data.Name = "updated name";
    db.Store(data); // same method as CREATE
 
    // DELETE
    db.Delete(data);
 
    // close the database connection
    db.Close();
}

There are a few gotchas when doing updates/deletes with object graphs.  You must tell db4o how to cascade when you open the connection.  This is accomplished through the configuration object that is passed as the first parameter to the OpenClient/OpenServer methods.

public class Parent
{
    public int Counter { get; set; }
}
 
public class Child
{
    public string Name { get; set; }
    public Parent Parent { get; set; }
}
 
public class SomeService
{
    public void UpdateCounter(string childName)
    {
        var config Db4oClientServer.NewClientConfiguration();
        config.ObjectClass(typeof(Child)).CascadeOnUpdate(true);
 
        using(var db = Db4oClientServer.OpenClient(config,"host",1677,"username","password");
        {
            var child = db.AsQueryable<child>().Single(x => x.Name == childName);
            child.Parent.Counter++;
            db.Store(child);
        }
    }
}

Querying

db4o has extensive querying support including query-by-example (QBE), native queries, SODA, and LINQ. LINQ is the preferred way to query in .net applications and should be used for the majoriy of your queries. An example of a LINQ query was provided in the previous example. SODA queries are db4o’s low level API. Native queries use the native language, C# in this case, to query the object store. QBE is where you give db4o a prototype of what you want and it will find everything matching it

SODA Queries

using(IObjectContainer db = Db4oEmbedded.OpenFile(Db4oEmbedded.NewConfiguration(),@".\Database.yap"))
{
    IQuery query = db.Query();
    query.Constrain(typeof(MyDataStructure);
    query.Descend("Name").Constrain("some name");
    IObjectSet results = query.Execute();
 
    foreach(MyDataStructure item in results)
    {
        Console.WriteLine(item.Name);
    }
 
    db.Close();
}

Query-by-Example

using(IObjectContainer db = Db4oEmbedded.OpenFile(Db4oEmbedded.NewConfiguration(),@".\Database.yap"))
{
    var prototype = new MyDataStructure { Name = "some name"; }
    IObjectSet results = db.QueryByExample(prototype);
 
    foreach(MyDataStructure item in results)
    {
        Console.WriteLine(item.Name);
    }
 
    db.Close();
}

Native Queries

using(IObjectContainer db = Db4oEmbedded.OpenFile(Db4oEmbedded.NewConfiguration(),@".\Database.yap"))
{
    IList<mydatastructure> results = db.Query<MyDataStructure(x => x.Name == "some name");
 
    foreach(MyDataStructure item in results)
    {
        Console.WriteLine(item.Name);
    }
 
    db.Close();
}

Transactions

Similar to other database systems, db4o provides a transaction mechanism. Opening a transaction is implicit when you open a connection to the database and implicitly committed when you close it. During the lifespan of an open connection, you can commit any changed state to the database before closing the connection. You can also perform a rollback to reset the state of an open connection to the last commit point or to the opening state of the connection if you have not done a commit. 

Rolling back the database poses a problem though.  When doing a rollback, we rollback the database but not any of our live objects.  The solution to this is found in the Ext library in the form of the Refresh method.

using(IObjectContainer db = Db4oEmbedded.OpenFile(Db4oEmbedded.NewConfiguration(),@".\Database.yap"))
{
    var data = new MyDataStructure { Name = "db4o"};
 
    db.Store(data);
    db.Commit();
    data.Name = "name im gonna rollback later";
    db.Store(data);
    db.Rollback();
 
    // at this point name is "name im gonna rollback later";
    var realData = db.AsQueryable<mydatastructure>().Single();
 
    Console.WriteLine(readData.Name);
 
    db.Close();
}

Wrapping Up

There is a lot more to db4o than I talk about here. Read through the documentation that comes with it and then head over to the developer forums at the db4o site to get answers for any outstanding questions you may have.