HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Looping through columns in Entity Framework

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
loopingcolumnsthroughframeworkentity

Problem

Is there a cleaner/more efficient way to loop through the columns in EF implicitly than the way written below?

static void Main(string[] args) {
    using (var db = new someDbContext()) {
        var query = from p in db.someTable
                    select new {
                        column1 = p.column1
                        column2 = p.column2
                    };

        var columnAccessors = CreateAccessors(query.FirstOrDefault());

        foreach (var row in query) {
            foreach (var col in columnAccessors) {
                var val = col(row);
                //Do something with val here.
            }
        }
    }
}

static Func[] CreateAccessors(T source = default(T)) {
    var propertyAccessors = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public)
                .Where(p => p.CanRead)
                .Select((p, i) => new {
                    Index = i,
                    Property = p,
                    Accessor = CreatePropertyAccessor(p)
                })
                .ToArray();

    return propertyAccessors.Select(p => p.Accessor).ToArray();
}

static Func CreatePropertyAccessor(PropertyInfo prop) {
    var param = Expression.Parameter(typeof(T), "input");
    var propertyAccess = Expression.Property(param, prop.GetGetMethod());
    var castAsObject = Expression.TypeAs(propertyAccess, typeof(object));
    var lambda = Expression.Lambda>(castAsObject, param);
    return lambda.Compile();
}


I've tried simply doing a foreach (var col in row) before but I know that it won't work because row doesn't contain a definition for GetEnumerator and I'm not sure how one would go about implementing a solution that would do so generically for something like this.

I was working on implementing a DataReader and came across this page: http://www.developerfusion.com/article/122498/using-sqlbulkcopy-for-high-performance-inserts/. The thought occurred to me that I could modify the CreatePropertyAccessors segments to

Solution

So I had a look and made a few modifications, see below with comments.

// Made the method generic, the constaint is required by DbSet
static void LoopThroughColumns(Func> getTable)
    where T : class
{
    using (var db = new someDbContext())
    {
        // Selecting columns exlicitly was unnecessary
        var query = getTable(db);
        var columnAccessors = CreateAccessors();

        foreach (var row in query)
        {
            foreach (var col in columnAccessors)
            {
                var val = col(row);
            }
        }
    }
}

// Parameter is unnecessary as you never used it
static Func[] CreateAccessors()
{
    // Index and Property values weren't being used
    // ToArray() was an unnecessary conversion
    var propertyAccessors = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public)
                .Where(p => p.CanRead)
                .Select((p, i) =>  CreatePropertyAccessor(p));

    return propertyAccessors.Select(e => e).ToArray();
}

static Func CreatePropertyAccessor(PropertyInfo prop)
{
    var param = Expression.Parameter(typeof(T), "input");
    var propertyAccess = Expression.Property(param, prop.GetGetMethod());
    var castAsObject = Expression.TypeAs(propertyAccess, typeof(object));
    var lambda = Expression.Lambda>(castAsObject, param);
    return lambda.Compile();
}


Usage is a little different now, you would call it like this:

LoopThroughColumns(e => e.someTable);


To be extra-super cool (ie. generic) you could even pass in an Action and have that define what is done with val, then derive different methods on that one. Below I'm creating a PrintColumnValues method which calls LoopThroughColumns with an specific Action to perform.

static void LoopThroughColumns(Func> getTable, Action actionOnObject)
    where T : class
{
    using (var db = new someDbContext())
    {
        var query = getTable(db);
        var columnAccessors = CreateAccessors();

        foreach (var row in query)
        {
            foreach (var col in columnAccessors)
            {
                actionOnObject(col(row));
            }
        }
    }
}

static void PrintColumnValues(Func> getTable)
    where T : class
{
    LoopThroughColumns(getTable, 
        new Action(e => 
        {
            Console.WriteLine(e);
        }));
}


Usage

PrintColumnValues(e => e.someTable);

Code Snippets

// Made the method generic, the constaint is required by DbSet
static void LoopThroughColumns<T>(Func<someDbContext, DbSet<T>> getTable)
    where T : class
{
    using (var db = new someDbContext())
    {
        // Selecting columns exlicitly was unnecessary
        var query = getTable(db);
        var columnAccessors = CreateAccessors<T>();

        foreach (var row in query)
        {
            foreach (var col in columnAccessors)
            {
                var val = col(row);
            }
        }
    }
}

// Parameter is unnecessary as you never used it
static Func<T, object>[] CreateAccessors<T>()
{
    // Index and Property values weren't being used
    // ToArray() was an unnecessary conversion
    var propertyAccessors = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public)
                .Where(p => p.CanRead)
                .Select((p, i) =>  CreatePropertyAccessor<T>(p));

    return propertyAccessors.Select(e => e).ToArray();
}

static Func<T, object> CreatePropertyAccessor<T>(PropertyInfo prop)
{
    var param = Expression.Parameter(typeof(T), "input");
    var propertyAccess = Expression.Property(param, prop.GetGetMethod());
    var castAsObject = Expression.TypeAs(propertyAccess, typeof(object));
    var lambda = Expression.Lambda<Func<T, object>>(castAsObject, param);
    return lambda.Compile();
}
LoopThroughColumns(e => e.someTable);
static void LoopThroughColumns<T>(Func<someDbContext, DbSet<T>> getTable, Action<object> actionOnObject)
    where T : class
{
    using (var db = new someDbContext())
    {
        var query = getTable(db);
        var columnAccessors = CreateAccessors<T>();

        foreach (var row in query)
        {
            foreach (var col in columnAccessors)
            {
                actionOnObject(col(row));
            }
        }
    }
}

static void PrintColumnValues<T>(Func<someDbContext, DbSet<T>> getTable)
    where T : class
{
    LoopThroughColumns(getTable, 
        new Action<object>(e => 
        {
            Console.WriteLine(e);
        }));
}
PrintColumnValues(e => e.someTable);

Context

StackExchange Code Review Q#21634, answer score: 3

Revisions (0)

No revisions yet.