views:

46

answers:

6

I have code something like this in an IRepository implementation in Linq to Sql:

var newlist = from h in list where h.StringProp1 == "1"
                      select new MyBusinessBO{
                          firstProp = h.StringProp1,
                          secondProp = h.StringProp2
                      };

The projection into MyBusinessBO is not difificult but when the Business Object has many properties the projection code becomes very lengthy. Also, as the projection can occur in several places in the Repository we break the DRY principle.

Is there any way to abstract out the projection or replace it with a delegate?

I.e. replace the code

                          firstProp = h.StringProp1,
                          secondProp = h.StringProp2

with something reusable?

A: 

Take a look at AutoMapper and similar tools

UserControl
yes, actually already using automapper extensively, in this case it wasn't the best approach
Redeemed1
A: 

Perhaps use regular non-default constructors, rather than object initializers. Or, if you can start using C# 4.0, try to add optional/default parameters to the mix.

Brent Arias
A: 

You can put that code in a method:

public static IEnumerable<MyBusinessBO> ConvertToMyBusinessBO(
        IEnumerable<Original> collection)
{
    return
        from item in collection
        select new MyBusinessBO{
            firstProp = h.StringProp1,
            secondProp = h.StringProp2
        };
}

And call it like this:

var q = from h in list where h.StringProp1 == "1" select h;
var list = ConvertToMyBusinessBO(q);

And you can do the same when using IQueryable<T>:

public static IQueryable<MyBusinessBO> ConvertToMyBusinessBO(
        IQueryable<Original> collection)
{
    return
        from item in collection
        select new MyBusinessBO{
            firstProp = h.StringProp1,
            secondProp = h.StringProp2
        };
}

This way you can still use the power of a LINQ provider that will execute your code against the database.

Steven
+4  A: 

You could solve this by using the dot syntax rather than the LINQ-style syntax.

Your current:

list
    .Where(h => h.StringProp1 == "1")
    .Select(h => new MyBusinessBO
    {
        firstProp = h.StringProp1,
        secondProp = h.StringProp2
    });

Potential solution:

Func<MyType, MyBusinessBO> selector = h => new MyBusinessBO
{
    firstProp = h.StringProp1,
    secondProp = h.StringProp2
};
list
    .Where(h => h.StringProp1 == "1")
    .Select(selector);

And you could pass in the selector somewhere or generate it on-the-fly or something along those lines.

ckknight
Nice answer. I would even go one step further and define that `Func` as a real method. The OP wants to reuse that projection logic.
Steven
You beat me to it. +1
Khnle
A: 

I'd create an extension method on the type itself, that performed the projection for you. i.e.

static MyBusinessBO AsBusinessBO(this MyObject obj)
{
    return new MyBusinessBO()
    {
        firstProp = obj.StringProp1,
        secondProp = obj.StringProp2
    }
}

Then you can write your original query as:

var newlist = from h in list where h.StringProp1 == "1"
              select h.AsBusinessBO()
tzaman
this is an interesting idea and although the logic would be good against the BO I want to keep the logic in this case closer to the repository. Even so, the repository may be swapped out and as such it would be better out of the repository and back on the BO but as we are in development with some less experienced developers the repository location might be more understandable for them
Redeemed1
A: 

Queryable.Select requires an Expression<Func<T, U>>. You can write a method that returns this and use that method everywhere you do the transformation.

public Expression<Func<DataObj, BusiObj>> GetExpr()
{
  return h => new BusiObj()
  {
    firstProp = h.StringProp1,
    secondProp = h.StringProp2
  };
}


 //get a local variable holding the expression.
Expression<Func<DataObj, BusiObj>> toBusiObj = GetExpr();

//use it thusly
var newList = (from h in list where h.StringProp1 == "1" select h)
  .Select(toBusiObj)
  .ToList();

//or
List<BusiObj> newList = list
  .Where(h => h.StringProp1 == "1")
  .Select(toBusiObj)
  .ToList();
David B
A very similar approach to ckknight above, however this looks like it may be better as a means for creating clearer code. It has the projection in a separate re-usable function which can just be passed around. ckknight had the answer but David B, you worked it through in a manner that gets me to where I want to be. I have upvoted ckknight answer and accepted this one.
Redeemed1