This post has been migrated from www.experimentsincode.com, we apologise if some of the images or content is missing

When using data bound controls it can sometimes be a pain to ship data across to web control you want to bind the data to. For example lets take a repeater control. Our application is quite simple, we have a person object containing name and date of birth, our application then takes this information and calculates which year they turned 18. The code for the Person class looks like this:
public class Person{
    public string Name{get;set;}
    public DateTime DoB{get;set;}
}
And the bit doing the calculation like so:
        List people = GetPeople();
        people.ForEach(x =>
        {
            int year = GetYearTurned18(x);
        });
At the moment very simple but I am not doing anything with the calculated value except assign it to a variable. What I want to do is pass all this to a repeater control so that I can then render it nicely on screen. The result should look like this:
Mike 23/03/1976 1994
Fred 21/07/1980 1998
So I want to bind it to a repeater a control by passing it to the DataSource property and then calling DataBind. I can't just pass both the person object and the calculated year into the DataSource because it only takes a single object, so I need to work out some other method. The first thing might be to turn the calculation into a method on the person class like so, this will allow be to pass a list of Person objects to the DataSource.
public class Person{
    public string Name{get;set;}
    public DateTime DoB{get;set;}
    private int YearTurned18
    {
        get
        {
            return this.DoB.Year + 18;
        }
    }
}
However what if we wanted to calculate the year they turned 16 or 24 or 65? We would have to write additional properties. It is not a good solution because of all the additional methods. The second solution is to create a class to contain the person and year, something like:
    public class PersonContainer
    {
        public Person Person { get; set; }
        public int Year { get; set; }
    }
This again is still not great, what if at some time in the future I want to pass the person object over with another value that isn't an int. I would have to either add another property to this class or create another class. Adding properties leaves me with a class where only a few peoperties are used or I have lots of container classes in my code. A better solution is to use anonymous types. These types allow you to create classes with read only properties which you assign when the object is created. The code for our solution then becomes:
            List<Person> people = GetPeople();
            List<object> peopleData = new List<object>();

            people.ForEach(x =>
            {
                int year = GetYearTurned18(x);
                peopleData.Add(new { Person = x, Year = year });
            });

            _peopleRepeater.DataSource = peopleData;
            _peopleRepeater.DataBind();
This is quite nice. I can create this anonymous type and then in my repeater control get access to the data using the following:
                        <%# DataBinder.Eval(Container.DataItem, "Person.Name") %>
                        <%# DataBinder.Eval(Container.DataItem, "Person.DoB", "dd/MM/yyyy") %>
                        <%# DataBinder.Eval(Container.DataItem, "Year")%>
I can also add as many other properties as I like to my anonymous type, allowing for easy expansion later on. However the only real problem is that the list is not strongly typed. I could add anything to the list because it accepts an object. So I want to make this more strongly typed.  However there is a way around this, read the post at the end of Microsoft's Anonymous Types page for a way to do this.
comments powered by Disqus