views:

107

answers:

1

I have to work with multiple SQL Server tables that generally look like this:

int id_this, int id_that, ..., double Value1, double Value2, ..., double Value96

I know this sucks, but I can't change it. What I want to do now is to define some class like

public class Foo
{
    public int Id_This { get; set; }
    public int Id_That { get; set; }
    ...
    public double Value[];
}

The Value-Array being a property of course, but I think you get the idea.

The question is, how to get the 96 columns into the array as painlessly as possible.

I could do that with a plain SqlDataReader, since DataRow allows indexed access, but I wonder if I could declare some attributes or write some minimum amount of code to use the class directly with LINQ2SQL.

As a minimum, I would like to do

dataContext.ExecuteQuery<Foo>("SELECT * FROM Foo");
+2  A: 

Ooh, that is... nice? The DataContext methods always expect an entity type; there is no ExecuteReader, which is a pain (but understandable, as it wants to behave as an ORM). To be honest, I would be tempted to use ADO.NET for the guts against this table, but if you have mapped the wide table to the DataContext you should be able to use either regular C# or reflection.

Since the number doesn't change, unless you have multiple tables I'd just bite the bullet and write some ugly code:

Foo foo = new Foo { Id_This = obj.Id_This, Id_That = obj.Id_That,
    Values = new double[] {obj.Value1, obj.Value2, ... } };

If you have multiple tables... reflection, perhaps optimised via Expression:

using System;
using System.Collections.Generic;
using System.Linq.Expressions;
class FooUgly
{
    public int IdThis { get; set; }
    public int IdThat { get; set; }
    public double Value1 { get; set; }
    public double Value2 { get; set; }
    public double Value3 { get; set; }
}
class Foo
{
    public int IdThis { get; set; }
    public int IdThat { get; set; }
    public double[] Values { get; set; }
    public Foo() { }
    internal Foo(FooUgly ugly)
    {
        IdThis = ugly.IdThis;
        IdThat = ugly.IdThat;
        Values = extractor(ugly);
    }
    // re-use this!!!
    static readonly Func<FooUgly, double[]> extractor =
        ValueExtractor<FooUgly, double>.Create("Value", 1, 3);
}
static class Program
{
    static void Main()
    {
        FooUgly ugly = new FooUgly { IdThis = 1, IdThat = 2, Value1 = 3, Value2 = 4, Value3 = 5 };
        Foo foo = new Foo(ugly);
    }
}
static class ValueExtractor<TFrom,TValue>
{
    public static Func<TFrom, TValue[]> Create(string memberPrefix, int start, int end)
    {
        if(end < start) throw new ArgumentOutOfRangeException();
        ParameterExpression param = Expression.Parameter(typeof(TFrom), "source");
        List<Expression> vals = new List<Expression>();
        for(int i = start ; i <= end ; i++) {
            vals.Add(Expression.PropertyOrField(param, memberPrefix + i));
        }
        Expression arr = Expression.NewArrayInit(typeof(TValue), vals);
        return Expression.Lambda<Func<TFrom, TValue[]>>(arr, param).Compile();
    }
}
Marc Gravell
Hi Marc, thanks for the prompt answer. I especially like the idea with the reusable Expression.
TToni