Archive for May, 2008

Lambda Expression and Object Mappers

Here’s a simple implementation of an object mapper using lambda expressions that I’ve shown yesterday at the Tulsa School of Dev. Say we need to copy some data from one object to another. For this example my objects are a Book and an AlternateBook. The class are shown below:

public class Book
{
    public Book()
    {
    }

    public Book(string title, int yearPublished)
    {
        Title = title;
        YearPublished = yearPublished;
    }

    public string Title { get; set; }
    public int YearPublished { get; set; }
    public string AuthorFirstName { get; set; }
    public string AuthorLastName { get; set; }
}

public class AlternateBook
{
    public string AuthorFullName { get; set; }
    public string Title { get; set; }
}

I could have a mapper that worked like this:

Mapper mapper = new Mapper(book, alternateBook);
mapper.AddMap("Title", "Title");
mapper.AddMap("AuthorFirstName", "AuthorFullName");

That approach would have some problems, though:

  • The properties are mapped using string literals, which means we don’t benefit from strong-typing or IntelliSense (bugs could easily arise because of typos or properties that don’t exist on the objects);
  • The properties have a 1:1 mapping, which in the sample above wouldn’t really work, since I had to map "AuthorFullName" directly to "AuthorFirstName", as oppose AuthorFirstName and AutorLastName.
  • The mapper would have to use Reflection to look up the properties on the given objects and then assign their values (not the most optimized way of doing it…).

The approach using Lambda expression would look like this:

var mapper = new Mapper<Book, AlternateBook>(); mapper.AddMapping((b, ab) => ab.Title = b.Title); mapper.AddMapping((b, ab) => ab.AuthorFullName = b.AuthorFirstName + " " +
b.AuthorLastName);

Points to note:

  • As I instantiate the Mapper I provide the type of objects as Generic parameters;
  • The AddMapping method takes in lambda expressions that receive two parameters (b and ab, for book and alternate book, respectively), which I then use to set the mapping. That gives me both strong-typing and IntelliSense support.
  • The lambda expression also opens up control over how things are pushed into the target object. In the sample below, I can format how the alternate book’s AuthorFullName property gets populated;
  • Since the lambda expression is compiled down to a delegate, there’s no Reflection involved, so the code should run faster when executed.

Now, in case you’re not familiar with lambdas yet: the expression does NOT get executed when we are passing it as a parameter to the AddMapping method. At that point we are only registering the m apping; the actual execution occurs whenever we want it to. I would probably, say, at the application start, register all mappings I’m likely to use. Eventually, when I need to actually process the mapping (copying some data from one object to another), I can just call the ApplyMapping, passing in the objects involved with the mapping. For example:

Book book = new Book { AuthorFirstName = "Claudio", AuthorLastName = "Lassala", Title = "Some book" }; AlternateBook alternateBook = new AlternateBook();
mapper.ApplyMappingsTo(book, alternateBook); Console.WriteLine( "Book:"+ "\r\n Title: "+ book.Title + "\r\n AuthorFirstName: " + book.AuthorFirstName + "\r\n AuthorLastName: " + book.AuthorLastName); Console.WriteLine( "\r\nAlternateBook:" + "\r\n Title: " + alternateBook.Title + "\r\n AuthorFullName: " + alternateBook.AuthorFullName);

The code above should produce the following results:

lambda

So, how’s the Mapper class implemented? Here we go:

public class Mapper<TSource, TTarget>
{
    private Collection<Action<TSource, TTarget>> m_Mappings = 
                    new Collection<Action<TSource, TTarget>>();
    public Collection<Action<TSource, TTarget>> Mappings
    {
        get { return m_Mappings; }
    }

    public void AddMapping(Action<TSource, TTarget> action)
    {
        Mappings.Add(action);
    }

    public void ApplyMappingsTo(TSource source, TTarget target)
    {
        foreach (var mapping in Mappings)
        {
            mapping(source, target);
        }
    }
}

The class keeps a list of all the mappings within its Mappings collection, which is a collection of Action<TSource, TTarget> delegates. Both TSource and TTarget are Generic types provided to the class. The AddMaping method takes in the delegate (which we pass in as a lambda) and adds it to the Mappings collection. The ApplyMappingsTo method  takes in instances for the source and target object, iterates through the Mappings collection, and invokes each mapping (don’t forget it, the mapping is a delegate). 

That’s it. Again, this is just a simple implementation. I’m pretty sure there’s a lot of improvements to be done on top of this, and there’s definitely something some people wrote out there around the same lines; I’ll be looking for that myself in order to see how I can improve this.

For instance, one thing that pops up is: what if the developer messed up and specified the mapping going from the target to the source instead? Like so:

mapper.AddMapping((b, ab) => b.Title = ab.Title);

We certainly don’t want to copy the title from the Alternate Book to the Book object. I don’t know of way yet to have the compiler catch that problem (any ideas?). During runtime, though, I know I could have the AddMapping method analyze the Expression Tree for the lambda it was given, and make sure that the target appears at the left side of the = sign, and the target appears at the right side.

Another improvement that could be made is some optimization. For instance, itake the following code snippet:

mapper.AddMapping((b, ab) => ab.AuthorFullName = b.AuthorFirstName + " " +
b.AuthorLastName);

There, the developer has used string concatenation to build up the value to be assigned to the property. The AddMapping method could have some logic that analyzes the expression tree, and if it finds something like string concatenation, it could replace that with using a StringBuilder. Of course, such optimization should only be done if we can clearly see that as being something that we really needed to squeeze better performance out of.

Also, it’d be convenient to have the mapper just figure out what properties to copy from the source to the target by looking for properties that map 1:1 (that is, property name and type exist on both objects), and then let the developer only supply special mappings for properties that require transformation.

I’ll be doing some more research on better ways to do this kind of stuff.

4 Comments

The Temple of the King

After some pretty tough weeks with a lot of work and little sleep, I spent an afternoon with my friends Milton and Lucas having some fun making some home-recording of Rainbow’s Temple of the King. Nothing like having some fun to relieve some of the stress.  🙂

Check it out:

http://www.youtube.com/watch?v=BefV66tSyEI

Leave a comment

The Twitter thing is not so bad after all

A few weeks ago I mentioned I had broken down and signed up for Twitter. Even though I have not yet fell for it yet, I do some of its value. It does have an obvious problem: if you’re not careful, you can quickly get carried away and become and totally unproductive person. What I’ve been doing is to have a Twitter client open in one of my computers, and every once in a while I check out what my peers are up to (usually on short breaks or when my computer is taking to long to process something).

Here are the things I’ve either experienced or seen that I kind of dig:

  • This morning I had to go from the hotel to the campus where the Tulsa School of Dev was going to take place. I was supposed to start my keynote at 9am, so I came down to the hotel’s lobby at around 7:50am, thinking I’d get a shuttle and be at the campus at around 8:15am (I had no idea how far the place was). Somebody from the hotel tells me they wouldn’t drive to the place. Somebody else says they would. Twenty minutes later a "definitive" word comes that they really would not take me there. Ok, no problem, but they could have me told that before, right? Anyway, I asked whether they could just call for a cab, which they said they would. Another 40 minutes go by, and nothing. They called the cab company 4 or 5 times, and on the last time, the company says they don’t have a cab in the area (it’s not like I was in the middle of nowhere; I was in downtown…). Well, I start barking at people, and eventually one of the hotel’s drivers says he’s taking me to the place, and that it didn’t take 10 minutes for us to get there (while we’re on our way, the hotel calls the guy to say he’s in trouble because he wasn’t supposed to take me). What the hell? It’s 10 minutes away from the hotel!! What’s their problem? Anyway, around 8:30am, I had twittered expressing my frustrating with my wait for a cab. When I got to the auditorium, at 9:01am, Dave said he had seen my twitter, so he knew I should be getting there anytime soon. I didn’t have Dave’s cell phone number, so I was just hoping he’d be checking on Twitter. So I was actually glad I used Twitter for that (yes, I did give the hotel’s manager an earful when I got back there this evening).
  • Every once in a while I see somebody twittering links to articles or blog post on things that I’m personally interested in.
  • I end up connecting with people in ways I would not have even imagined before. For instance, in my list of friends and followers, I have a mix of people who are personal friends, and people who knows me from the software development community (people who follows this blog, sees me presenting, or reads my articles). The other day I twittered to say I had finished watching the first season of Heroes, and all of a sudden I start getting replies from people who also enjoyed the show, and I’ve ended up learning about some other things related to the series that I didn’t know about (such as the fact that there are short graphic novels on the series’ website that extend episodes and provide more details about characters and things like that). I really like this social aspect where a person ends up relating to another by means of other things they like but wouldn’t know they shared it because they usually meet in the realm of, say, software development.
  • You learn some fun tidbits, like when Rick smashed his bass sometime in the 80’s and had to borrow one from Testament.  🙂
  • Some people (like Kevin Miller) are already finding ways to use Twitter to produce some business value.

One other thing I’m not liking about it right now is the fact that I don’t own an *i*Phone, I just own *a* phone; a regular Sony phone that has a tiny little web browser, which makes it *really* hard to type anything on it and post to Twitter, or to follow posts. And an iPhone isn’t getting in my budget plans for the foreseen feature (there’s just too many other things taking higher priority).

Note: I was typing this post offline; when I got online, I saw a tweet from somebody pointing out to a post by Rick that’s very similar to this one of mine.  🙂

Leave a comment

Tulsa’s School of Dev

This Saturday I’ll be speaking at the Tulsa’s School of Dev. I’ve had a great time last year, so I’m looking forward to going there again, do some presentations, attend others, and meet some old buddies there.

Here  are the talks I’ll be delivering:

  • Keynote: Using the New Features in C# 3.0
  • Design Patterns in .NET
  • Intro to LINQ

The full agenda can be found here.

That’s a free event, so make sure to check it out!

Leave a comment