using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.ComponentModel;
using System.Reflection;
using Csla.Properties;
namespace Csla.Data
{
///
/// An ObjectAdapter is used to convert data in an object
/// or collection into a DataTable.
///
public class ObjectAdapter
{
///
/// Fills the DataSet with data from an object or collection.
///
///
/// The name of the DataTable being filled is will be the class name of
/// the object acting as the data source. The
/// DataTable will be inserted if it doesn't already exist in the DataSet.
///
/// A reference to the DataSet to be filled.
/// A reference to the object or collection acting as a data source.
public void Fill(DataSet ds, object source)
{
string className = source.GetType().Name;
Fill(ds, className, source);
}
///
/// Fills the DataSet with data from an object or collection.
///
///
/// The name of the DataTable being filled is specified as a parameter. The
/// DataTable will be inserted if it doesn't already exist in the DataSet.
///
/// A reference to the DataSet to be filled.
///
/// A reference to the object or collection acting as a data source.
public void Fill(DataSet ds, string tableName, object source)
{
DataTable dt;
bool exists;
dt = ds.Tables[tableName];
exists = (dt != null);
if (!exists)
dt = new DataTable(tableName);
Fill(dt, source);
if (!exists)
ds.Tables.Add(dt);
}
///
/// Fills a DataTable with data values from an object or collection.
///
/// A reference to the DataTable to be filled.
/// A reference to the object or collection acting as a data source.
public void Fill(DataTable dt, object source)
{
if (source == null)
throw new ArgumentException(Resources.NothingNotValid);
// get the list of columns from the source
List columns = GetColumns(source);
if (columns.Count < 1) return;
// create columns in DataTable if needed
foreach (string column in columns)
if (!dt.Columns.Contains(column))
dt.Columns.Add(column);
// get an IList and copy the data
CopyData(dt, GetIList(source), columns);
}
#region DataCopyIList
private IList GetIList(object source)
{
if (source is IListSource)
return ((IListSource)source).GetList();
else if (source is IList)
return source as IList;
else
{
// this is a regular object - create a list
ArrayList col = new ArrayList();
col.Add(source);
return col;
}
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
private void CopyData(
DataTable dt, IList ds, List columns)
{
// load the data into the DataTable
dt.BeginLoadData();
for (int index = 0; index < ds.Count; index++)
{
DataRow dr = dt.NewRow();
foreach (string column in columns)
{
try
{
dr[column] = GetField(ds[index], column);
}
catch (Exception ex)
{
dr[column] = ex.Message;
}
}
dt.Rows.Add(dr);
}
dt.EndLoadData();
}
#endregion
#region GetColumns
private List GetColumns(object source)
{
List result;
// first handle DataSet/DataTable
object innerSource;
IListSource iListSource = source as IListSource;
if (iListSource != null)
innerSource = iListSource.GetList();
else
innerSource = source;
DataView dataView = innerSource as DataView;
if (dataView != null)
result = ScanDataView(dataView);
else
{
// now handle lists/arrays/collections
IEnumerable iEnumerable = innerSource as IEnumerable;
if (iEnumerable != null)
{
Type childType = Utilities.GetChildItemType(
innerSource.GetType());
result = ScanObject(childType);
}
else
{
// the source is a regular object
result = ScanObject(innerSource.GetType());
}
}
return result;
}
private List ScanDataView(DataView ds)
{
List result = new List();
for (int field = 0; field < ds.Table.Columns.Count; field++)
result.Add(ds.Table.Columns[field].ColumnName);
return result;
}
private List ScanObject(Type sourceType)
{
List result = new List();
if (sourceType != null)
{
// retrieve a list of all public properties
PropertyInfo[] props = sourceType.GetProperties();
if (props.Length >= 0)
for (int column = 0; column < props.Length; column++)
if (props[column].CanRead)
result.Add(props[column].Name);
// retrieve a list of all public fields
FieldInfo[] fields = sourceType.GetFields();
if (fields.Length >= 0)
for (int column = 0; column < fields.Length; column++)
result.Add(fields[column].Name);
}
return result;
}
#endregion
#region GetField
private static string GetField(object obj, string fieldName)
{
string result;
DataRowView dataRowView = obj as DataRowView;
if (dataRowView != null)
{
// this is a DataRowView from a DataView
result = dataRowView[fieldName].ToString();
}
else if (obj is ValueType && obj.GetType().IsPrimitive)
{
// this is a primitive value type
result = obj.ToString();
}
else
{
string tmp = obj as string;
if (tmp != null)
{
// this is a simple string
result = (string)obj;
}
else
{
// this is an object or Structure
try
{
Type sourceType = obj.GetType();
// see if the field is a property
PropertyInfo prop = sourceType.GetProperty(fieldName);
if ((prop == null) || (!prop.CanRead))
{
// no readable property of that name exists -
// check for a field
FieldInfo field = sourceType.GetField(fieldName);
if (field == null)
{
// no field exists either, throw an exception
throw new DataException(
Resources.NoSuchValueExistsException +
" " + fieldName);
}
else
{
// got a field, return its value
result = field.GetValue(obj).ToString();
}
}
else
{
// found a property, return its value
result = prop.GetValue(obj, null).ToString();
}
}
catch (Exception ex)
{
throw new DataException(
Resources.ErrorReadingValueException +
" " + fieldName, ex);
}
}
}
return result;
}
#endregion
}
}