Skip to content

Instantly share code, notes, and snippets.

@atifaziz
Created January 15, 2010 00:39
Show Gist options
  • Save atifaziz/277661 to your computer and use it in GitHub Desktop.
Save atifaziz/277661 to your computer and use it in GitHub Desktop.
ToDataTable.cs
// Copyright (c) 2010, Atif Aziz. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Data;
using System.Linq.Expressions;
using System.Reflection;
// See also:
// http://groups.google.com/group/morelinq-dev/t/10b777ca7703f0e6
namespace MoreLinq
{
public static partial class MoreEnumerable
{
/// <summary>
/// Converts a sequence to a <see cref="DataTable"/> object.
/// </summary>
/// <typeparam name="T">The type of the elements of <paramref name="source"/>.</typeparam>
/// <param name="source">The source.</param>
/// <returns>
/// A <see cref="DataTable"/> representing the source.
/// </returns>
public static DataTable ToDataTable<T>(this IEnumerable<T> source)
{
return ToDataTable(source, new DataTable());
}
/// <summary>
/// Appends elements in the sequence as rows of a given <see cref="DataTable"/> object.
/// </summary>
/// <typeparam name="T">The type of the elements of <paramref name="source"/>.</typeparam>
/// <typeparam name="TTable"></typeparam>
/// <param name="source">The source.</param>
/// <param name="table"></param>
/// <returns>
/// A <see cref="DataTable"/> or subclass representing the source.
/// </returns>
public static TTable ToDataTable<T, TTable>(this IEnumerable<T> source, TTable table)
where TTable : DataTable
{
return ToDataTable(source, table, null);
}
/// <summary>
/// Appends elements in the sequence as rows of a given <see cref="DataTable"/>
/// object with a set of lambda expressions specifying which members (property
/// or field) of each element in the sequence will supply the column values.
/// </summary>
/// <typeparam name="T">The type of the elements of <paramref name="source"/>.</typeparam>
/// <param name="source">The source.</param>
/// <param name="expressions">Expressions providing access to element members.</param>
/// <returns>
/// A <see cref="DataTable"/> representing the source.
/// </returns>
public static DataTable ToDataTable<T>(this IEnumerable<T> source, params Expression<Func<T, object>>[] expressions)
{
return ToDataTable(source, new DataTable(), expressions);
}
/// <summary>
/// Appends elements in the sequence as rows of a given <see cref="DataTable"/>
/// object with a set of lambda expressions specifying which members (property
/// or field) of each element in the sequence will supply the column values.
/// </summary>
/// <typeparam name="T">The type of the elements of <paramref name="source"/>.</typeparam>
/// <typeparam name="TTable">The type of the input and resulting <see cref="DataTable"/> object.</typeparam>
/// <param name="source">The source.</param>
/// <param name="table">The <see cref="DataTable"/> type of object where to add rows</param>
/// <param name="expressions">Expressions providing access to element members.</param>
/// <returns>
/// A <see cref="DataTable"/> or subclass representing the source.
/// </returns>
public static TTable ToDataTable<T, TTable>(this IEnumerable<T> source, TTable table, params Expression<Func<T, object>>[] expressions)
where TTable : DataTable
{
if (source == null) throw new ArgumentNullException("source");
if (table == null) throw new ArgumentNullException("table");
//
// If no lambda expressions supplied then reflect them off
// the source element type.
//
if (expressions == null || expressions.Length == 0)
{
var parameter = Expression.Parameter(typeof(T), "e");
var expressionz = from m in typeof(T).GetMembers()
where m.MemberType == MemberTypes.Field
|| m.MemberType == MemberTypes.Property
select CreateMemberAccessor<T>(parameter, m);
expressions = expressionz.ToArray();
}
else
{
//
// Ensure none of the expressions is null.
//
if (expressions.Any(e => e == null))
throw new ArgumentException(null, "expressions");
}
//
// Compile the expressions while also digging out the member
// information that may be needed to build the table schema.
//
var memberz = from e in expressions
let member = GetMemberExpression(e)
let type = member.Type
let nullable = type.IsGenericType
&& type.GetGenericTypeDefinition() == typeof(Nullable<>)
select new
{
Name = member.Member.Name,
Type = nullable ? type.GetGenericArguments()[0] : type,
Nullable = nullable,
GetValue = e.Compile(),
};
var members = memberz.ToArray();
//
// If the table has no columns then build the schema.
// If it has columns, then bind to each based on the
// member name pulled out of the expressions.
//
DataColumn[] columns;
if (table.Columns.Count == 0)
{
columns = members.Select(m => new DataColumn(m.Name, m.Type) { AllowDBNull = m.Nullable })
.ToArray();
table.Columns.AddRange(columns);
}
else
{
columns = members.Select(m => table.Columns[m.Name])
.ToArray();
for (var i = 0; i < members.Length; i++)
{
if (columns[i] == null)
throw new Exception(string.Format("Column named '{0}' is missing.", members[i].Name));
}
}
//
// Builds rows out of elements in the sequence and
// add them to the table.
//
table.BeginLoadData();
try
{
foreach (var element in source)
{
var row = table.NewRow();
for (var i = 0; i < members.Length; i++)
row[columns[i]] = members[i].GetValue(element) ?? DBNull.Value;
table.Rows.Add(row);
}
}
finally
{
table.EndLoadData();
}
return table;
}
private static Expression<Func<T, object>> CreateMemberAccessor<T>(ParameterExpression parameter, MemberInfo member)
{
var access = Expression.MakeMemberAccess(parameter, member);
var box = Expression.Convert(access, typeof(object));
return Expression.Lambda<Func<T, object>>(box, parameter);
}
private static MemberExpression GetMemberExpression<T>(Expression<Func<T, object>> e)
{
var unary = e.Body as UnaryExpression;
return (MemberExpression) (unary != null ? unary.Operand : e.Body);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment