views:

65

answers:

2

In working with Linq to Sql I create a seperate class to ferry data to a web page. To simplify creating these ferry objects I either use a specialized constructor or an explicit conversion operator. I have two questions.

First which approach is better from a readibility perspective?

Second while the clr code that is generated appeared to be the same to me, are there situations where one would be treated different than the other by the compiler (in lambda's or such).

Example code (DatabaseFoo uses specialized constructor and BusinessFoo uses explicit operator):

public class DatabaseFoo
{
    private static int idCounter; // just to help with generating data
    public int Id { get; set; }
    public string Name { get; set; }

    public DatabaseFoo()
    {
        Id = idCounter++;
        Name = string.Format("Test{0}", Id);
    }
    public DatabaseFoo(BusinessFoo foo)
    {
        this.Id = foo.Id;
        this.Name = foo.Name;
    }
}

public class BusinessFoo
{
    public int Id { get; set; }
    public string Name { get; set; }

    public static explicit operator BusinessFoo(DatabaseFoo foo)
    {
        return FromDatabaseFoo(foo);
    }


    public static BusinessFoo FromDatabaseFoo(DatabaseFoo foo)
    {
        return new BusinessFoo {Id = foo.Id, Name = foo.Name};
    }
}

public class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Creating the initial list of DatabaseFoo");
        IEnumerable<DatabaseFoo> dafoos = new List<DatabaseFoo>() { new DatabaseFoo(), new DatabaseFoo(), new DatabaseFoo(), new DatabaseFoo(), new DatabaseFoo(), new DatabaseFoo()};

        foreach(DatabaseFoo dafoo in dafoos)
            Console.WriteLine(string.Format("{0}\t{1}", dafoo.Id, dafoo.Name));

        Console.WriteLine("Casting the list of DatabaseFoo to a list of BusinessFoo");
        IEnumerable<BusinessFoo> bufoos = from x in dafoos
                                          select (BusinessFoo) x;

        foreach (BusinessFoo bufoo in bufoos)
            Console.WriteLine(string.Format("{0}\t{1}", bufoo.Id, bufoo.Name));

        Console.WriteLine("Creating a new list of DatabaseFoo by calling the constructor taking BusinessFoo");
        IEnumerable<DatabaseFoo> fufoos = from x in bufoos
                                         select new DatabaseFoo(x);

        foreach(DatabaseFoo fufoo in fufoos)
            Console.WriteLine(string.Format("{0}\t{1}", fufoo.Id, fufoo.Name));
    }
}
+2  A: 

I would recommend you looking at AutoMapper. It will make your code more clear and separate the mapping between those objects which will render them independent and more reusable.

Darin Dimitrov
Thanks for the link it is very interesting, I will definitely play around with AutoMapper. There many cases when such would be handy. Sometimes adding abstractions like AutoMapper seems like walking a mile out of your way to avoid crossing a busy street. If you just have to cross one busy street, the extra mile seems not worth it, but if you have to cross several nasty streets then longer (which will end up shorter) and cleaner path pays off.
Felan
+1  A: 

I'm not a big fan of conversions for the most part - whether explicit or implicit. The same syntax: (TypeName) expression is used for various different kinds of conversion, and it can get a bit confusing to know which type the compiler is applying.

A static factory method like FromDatabaseFoo is good - and you might also want to have an instance method of ToBusinessFoo on DatabaseFoo. Both of these are clearer than user-defined conversions in my view.

(That's not to say that custom conversions are always a bad idea, mind you. I'm just wary of them in general.)

Jon Skeet
I would probably go with a static ToBusinessFoo and a static FromBusinessFoo in the DatabaseFoo class and avoid putting any in BusinessFoo as the point is to minimize what BusinessFoo knows about the data.Conversions don't feel right to me either. So you prefer the static method to using a constructor like: DatabaseFoo(businessFoo)?
Felan
@Felan: You could always make it an *extension* method on DatabaseFoo... so that DatabaseFoo itself doesn't know about it, but it looks like it does. Constructor or static method doesn't bother me much, to be honest. Static methods have some advantages, such as being able to cache or return null, and having names to disambiguate parameters. Constructors are more typical.
Jon Skeet