ASP.NET MVC: Loading data for select lists into edit model using attributes

After reading ASP.NET MVC 2 in Action and watching a presentation from MvcConf by Jimmy Bogard, I decided to implement some of their ideas in my current project. They use the concept of AutoMapViewResults, which are pretty neat. They use AutoMapper to map the model to a displaymodel or inputmodel. If you want to see exactly, what's going on there, you should watch Jimmy's presentation. The result is a really tiny controller:

public ActionResult Show(Event id)
{
  return AutoMapView<EventsShowModel>(id);
}

or

public ActionResult Edit(Event id)
{
  return AutoMapView<EventsEditModel>(id);
}

One problem now is that in editmodels you often need some additional data, for example for select lists. I found no satisfying solution achieving this with AutoMapper. So after asking about this on Stack Overflow, I decided to try it with a custom attribute. And here is what I came up with. It works for every entity type I have in my database, so there's only this one attribute for all of my select lists in my project.

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
public class LoadSelectListDataAttribute : Attribute
{
    public Type DataType { get; set; }  // defines the entity type I want to populate the select list with
    public string TextPropertyName { get; set; }  // defines the property of the entity, that is used for the text in the select box
    public string ValuePropertyName { get; set; }  // defines the property of the entity, that is used for the value in the select box
    public string SelectedValuePropertyName { get; set; }  // defines some other property on the editmodel, that contains the selected value
    public object SelectedValue { get; set; }  // defines a fixed selected value (if SelectedValuePropertyName is set, then this will be overriden)

    public LoadSelectListDataAttribute() { }
    public LoadSelectListDataAttribute(Type dataType, string textPropertyName, string valuePropertyName) : this(dataType, textPropertyName, valuePropertyName, null) { }
    public LoadSelectListDataAttribute(Type dataType, string textPropertyName, string valuePropertyName, string selectedValueProperty)
    {
        DataType = dataType;
        TextPropertyName = textPropertyName;
        ValuePropertyName = valuePropertyName;
        SelectedValuePropertyName = selectedValueProperty;
    }
}
// the class that actually handles the editmodel/attribute
public static class LoadSelectListDataHandler
{
    public static void Handle(object objectToHandle)
    {
        var properties = objectToHandle.GetType().GetProperties();
        foreach (var property in properties)  // iterate through each property of the editmodel
        {
            if (typeof(IList<SelectListItem>).IsAssignableFrom(property.PropertyType))  // checks if the property is of type IList<SelectListItem>
            {
                var attribute = (LoadSelectListDataAttribute)Attribute.GetCustomAttribute(property, typeof(LoadSelectListDataAttribute));  // checks LoadSelectListDataAttribute
                if (attribute != null)
                {
                    string selectedValue = (string)attribute.SelectedValue;
                    if (attribute.SelectedValuePropertyName != null)
                    {
                        // if SelectedValuePropertyName is set on the attribute then load the appropriate value
                        selectedValue = objectToHandle.GetType().GetProperty(attribute.SelectedValuePropertyName).GetValue(objectToHandle, null).ToString();
                    }
                    var service = (IService)IoC.Resolve(typeof(IService<>).MakeGenericType(attribute.DataType));  // get the Service for the specified data type using IoC (IoC.Resolve is just a wrapper about my actual IoC container, currently StructureMap)
                    var items = service.All();
                    var data = (from x in items
                                let text = x.GetType().GetProperty(attribute.TextPropertyName).GetValue(x, null).ToString()  // get text from entity
                                let value = x.GetType().GetProperty(attribute.ValuePropertyName).GetValue(x, null).ToString()  // get value from entity
                                select new SelectListItem
                                {
                                    Text = text,
                                    Value = value,
                                    Selected = value == selectedValue
                                }).OrderBy(x => x.Text).ToList();
                    property.SetValue(objectToHandle, data, null);  // set the value of the editmodel's property (with the LoadSelectListDataAttribute) to the generated list
                }
            }
        }
    }
}

How to use this attribute?

// decorate your viewmodel/editmodel
public class EventsEditModel
{
	public string Name {get; set;}

	[LoadSelectListData(typeof(Location), "Name", "Id", "LocationId")]
	public IList<SelectListItem> Locations {get; set;}
	public int LocationId {get; set;}
}


// handle viewmodel/editmodel in AutoMapViewResult
public class AutoMapViewResult : ViewResult
{
    public AutoMapViewResult(string viewName, string masterName, object model)
    {
        ViewName = viewName;
        MasterName = masterName;

        var viewModel = Mapper.Map(model, model.GetType(), typeof(TDestination));  // map model to viewmodel/editmodel
        LoadSelectListDataHandler.Handle(viewModel);  // load select list data, if LoadSelectListDataAttribute is applied
        ViewData.Model = viewModel;
    }
}

What do you think about this approach? Feedback is welcome, or even better other ideas, solutions etc.

Code download: LoadSelectListDataAttribute.zip (950,00 bytes)


Posted by: Dave
Posted on: 8/19/2010 at 9:54 PM
Tags: , , Categories: Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (1) | Post RSSRSS comment feed
Administration:

Windows 7 Wallpaper Contest

Microsoft Switzerland holds a contest for the upcoming release of Windows 7. They look for background images with typical Swiss subjects. This picture I took back in 2006 made it to this week's selection:

Eiger, M?nch und Jungfrau

Please vote for my Photo for it makes it into the official Windows 7 Background Gallery.

Thanks!


Posted by: Dave
Posted on: 7/19/2009 at 5:13 PM
Tags: Categories: Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (13) | Post RSSRSS comment feed
Administration:

TabsExtension for BlogEngine.NET v0.2

Maybe you've already heard about Enhanced BlogEngine.NET, a customized BlogEngine.NET which supports multiple languages. Because I'd like to write my posts in english and german in the future, I gave it a try and installed it on a different domain. I had to change some lines of my theme and then tried to copy my posts to the new installation. This took some time but finally I got it and I could play around...

Unfortunately the concept behind Enhanced BlogEngine.NET cannot really convince me. The posts are saved in different files for each language, so there would be one file for the english version, one for the germen etc. Comments and ratings are stored separatly for each language in the according files. Even tags and categories exist for different languages and are only displayed, if the according language is selected. So the only advantage over to separate blogs are in my opinion the link in each multilingual post that leads to a version in another language and that I don't have to switch between to blogs to write my posts. There are maybe scenarios, where Enhanced BlogEngine.NET is fitting, but for me it is no option at the time.

Instead of that I thought about writing an "Enhanced TabsExtension" for BlogEngine.NET, which I could use for my multilingual posts. And what came out in version 0.2 of this extension you can see below:

New language tabs
Version 0.2 of my TabsExtension now supports "language tabs". Give your tab a name like "en" or "de" and the tab's labelled with the appropriate title and flag. The flag images come from the BlogEngine folder /pics/flags. Since there are no language flags but country flags you maybe have to copy and rename some of them (e.g. us.png or gb.png to en.png)

How to use
All instructions you can find in this post about the TabsExtension.

Download
TabsExtension_v0.2.zip (3 kb)

Neu: Sprach-Tabs
Die Version 0.2 meiner TabsExtension unterstützt nun "Sprach-Tabs". Wird einem Tab der name "en", "de" etc. gegeben, so wird automatisch die entsprechende Flagge und die Sprache dazu angezeigt. Die Flaggen werden direkt aus dem Ordner /pics/flags geholt. Weil dort aber nicht Sprach- sondern Landes-Flaggen liegen, müssen unter Umständen noch einige Bilder umbenannt werden. So ist z.B. keine Flagge für Englisch (en) vorhanden, sodass entweder us.png oder gb.png kopiert und in en.png umbenannt werden muss.

Wie funktioniert die TabsExtension?
Alles andere funktioniert wie in diesem Post über die TabsExtension beschrieben.

Download
TabsExtension_v0.2.zip (3 kb)


Posted by: Dave
Posted on: 3/29/2009 at 4:12 PM
Tags: , Categories: Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (8) | Post RSSRSS comment feed
Administration:

April theme on ASP.NET MVC Design Gallery

I recently submitted a new theme to the ASP.NET MVC Design Gallery. It?s called April and is a fresh theme dominated by green tones. Naturally I included the jQuery login panel as well as in my previous theme.

April Theme Screenshot

So have a look at the April theme, download it and vote for it, if you like it.


Posted by: Dave
Posted on: 2/6/2009 at 8:15 PM
Tags: , , Categories: Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (4) | Post RSSRSS comment feed
Administration:

Opo Perspective - An ASP.NET Webmail: Part 03

In this post I’ll talk a bit about the architecture of Opo Perspective. I wrote in my first post that this application should be very flexible and extensible. Here are some thoughts about this topic. Most of the ideas presented here are already realized, have a look at the source on CodePlex.

The mail messages’ way

The following graphic illustrates how a mail message makes it’s way from the mail server to the web page (click for larger view).

 PerspectiveMessagePipeline

The MailStore is an external place, where the mail messages are received from. This could be any sort of mail server or, if the mail server is running on the same system for example, a directory with the mail messages.

The MailCache is an internal respository where the messages are saved. Messages displayed in the webmail are retrieved from the MailCache.

This is done through the MailService where also could be dealt with some business logic .

IPerspectivePipeline

There are two places where a PerspectivePipeline comes into play. The first one is intended to filter, tag, delete, … mails that are stored in the MailCache. In the graphic there's a SpamPipilinePlugin, which searches the message's header for such inserted by spam filters and adds a "Spam" tag to the message. This is one example how the pipeline could be used.

The second pipeline is intended to manipulate the message before displaying. One possibility would be to convert links in the text body of the message to html hyperlinks so they can be clicked within the message. Or we could prevent images from beeing shown initially and insert a link at the top of the message that loads the images on demand.

Since all involved classes are based on interfaces it's easy to write your own mail store, mail cache or pipeline plugin.

Because messages in Opo Perspective are based on the same interface (IPerspectiveEntity) like contacts, appointments etc. the same plugins can be used for these as well.


Posted by: Dave
Posted on: 1/2/2009 at 10:46 PM
Tags: , , Categories: Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (1) | Post RSSRSS comment feed
Administration:

Opo Perspective - An ASP.NET Webmail: Part 02

Organizing mail messages

Most mail clients provide a folder structure to organize emails. As GMail came out in 2005, they relied on labels instead of folders. I really like this approach and will do the same thing for Opo Perspective. Only difference: I'll name them tags, not labels. :)

I'm not sure how strictly GMail follows the tag approach, I don't know if spam or deleted mails "only" get a label or if they are moved to another folder or something similar. For Opo Perspective I want to manage all that with tags. Add the tag "Trash" – the message is deleted, add the tag "Inbox" – the message shows up on the home page.

So every new message should get the default tag "Inbox", or if it is junk mail, the tag "Spam".

There is a second possibility to mark a message, a flag. It's only possible to add one flag to a message (like GMail's "starred").

Organizing contacts, appointments, chat messages, …

Because I want Opo Perspective a extensible application there maybe some day things like appointments or chat messages that also must be organized. Therefore all these object should implement the IPerspectiveEntity, which is defined like follows:


Posted by: Dave
Posted on: 1/2/2009 at 9:37 PM
Tags: Categories: Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (8) | Post RSSRSS comment feed
Administration:

MVC Design Template "october" updated

Some days ago I wrote about my first MVC Design Template. I told about the jQuery login and that it's not very efficient because there was no specific login action action to handle the ajax request.

I now updated the template and included an extra action and view for the partial login. I also decided to stay with the full page postback for the login process because there are possibly places all over the page that have to be changed on successful login. It's not very practical to do that all in javascript and you'd have to look at in in every single page.

Download the updated template from the MVC Design Gallery (and vote for it, of course :-)


Posted by: Dave
Posted on: 1/2/2009 at 4:32 PM
Tags: , , Categories: Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (18) | Post RSSRSS comment feed
Administration:

My first template on ASP.NET MVC Design Gallery

I just got an email from Stephen Walther saying that my first template for the ASP.NET MVC framework has been published on http://www.asp.net/MVC/Gallery. Just look out for the theme called "october" or use this direct link: http://www.asp.net/MVC/Gallery/View.aspx?itemid=66.

Since there's only space for a 180 letters description and a small screenshot on the Design Gallery, I'll give you some more information here:

  • CSS for most HTML elements
    including headers, tables and forms
  • Additional CSS for message boxes
    information, error, success
  • "Ajaxified" login panel
    A click on the login menu button let slide in a login panel (without a full page postback). This is achieved by using jQuery's AJAX functionality.

I was not sure if I could include additional actions in the template, therefore the normal "/Account/Login" action is loaded by jQuery and then filtered for the login form itself. The drawbacks are that there is far too much data loaded (the whole page instead of the login form only) and that a click on the form's login button will conclude in a full page postback.
But it's very easy to replace this functionality with your own partial action like so:

Change

url: '<%= Url.Action("Login","Account") %>',
success: function(data) {
	lc.html(jQuery('form',jQuery(data))).slideToggle();
to
url: '<%= Url.Action("PartialLogin","Account") %>',
success: function(data) {
	lc.html(data).slideToggle();

And here some screenshots:


Posted by: Dave
Posted on: 12/28/2008 at 9:05 PM
Tags: , , Categories: Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (18) | Post RSSRSS comment feed
Administration:

Opo Perspective on CodePlex

After I released the 0.1 version of Opo.Net (http://www.codeplex.com/OpoNet) I finally started with my original project: Opo Perspective - An ASP.NET MVC Webmail Client. I wrote my first post about this project back in June this year. Yep, that's a long time and I'm very excited that I really made it and published some Perspctive code on CodePlext (http://www.codeplex.com/OpoPerspective).

Posted by: Dave
Posted on: 12/26/2008 at 9:34 PM
Tags: , , Categories: Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (3) | Post RSSRSS comment feed
Administration:

Dave's lustiges Buch vom Sterben

And now for something completely different! :-)

These are some pictures of my latest project. I had the idea to this back in 2001 but never made the graphics. The title's (translated from german) "Dave's funny book of dying", on each page there's one sentences which can be read with two different meanings. One of them is really trivial but the other one is a phrase for dying. In german this is quite funny but unfortunately most of them cannot be translated without loosing their meaning...

Die Idee zu diesem Projekt hatte ich bereits 2001, habe aber nie die Grafiken dazu gemacht. Das Ganze heisst "Dave's lustiges Buch vom Sterben" und ist eigentlich eine Sprachspielerei mit einer grafischen Umsetzung dazu. Aber Bilder sagen mehr als tausend Worte, deshalb... - schaut selbst:


Posted by: Dave
Posted on: 12/21/2008 at 9:39 PM
Tags: , , Categories: Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (1) | Post RSSRSS comment feed
Administration: