我已经知道我们如何使用 NHibernate 调用存储过程并填充 DTO(如此处所回答)。
但是,我想知道是否有可能在 NHibernate 中使用相同的 ResultTransformer 概念以某种方式填充 DataTable,或者是否有其他推荐的方法?
我更喜欢在 NHibernate 中使用 ResultTransformer 的概念。但是,我不知道它是否可能以及如何。有没有其他人试过这个?
我已经知道我们如何使用 NHibernate 调用存储过程并填充 DTO(如此处所回答)。
但是,我想知道是否有可能在 NHibernate 中使用相同的 ResultTransformer 概念以某种方式填充 DataTable,或者是否有其他推荐的方法?
我更喜欢在 NHibernate 中使用 ResultTransformer 的概念。但是,我不知道它是否可能以及如何。有没有其他人试过这个?
我使用此处描述的 ResultTransformer 的概念提出了以下解决方案:
public class DataTableResultTransformer : IResultTransformer
{
private DataTable dataTable;
public IList TransformList(IList collection)
{
var rows = collection.Cast<DataRow>().ToList();
rows.ForEach(dataRow => dataTable.Rows.Add(dataRow));
return new List<DataTable> { dataTable };
}
public object TransformTuple(object[] tuple, string[] aliases)
{
//Create the table schema based on aliases if its not already done
CreateDataTable(aliases);
//Create and Fill DataRow
return FillDataRow(tuple, aliases);
}
private DataRow FillDataRow(object[] tuple, string[] aliases)
{
DataRow dataRow = dataTable.NewRow();
aliases.ToList().ForEach(alias =>
{
dataRow[alias] = tuple[Array.FindIndex(aliases, colName => colName == alias)];
});
return dataRow;
}
private void CreateDataTable(IEnumerable<string> aliases)
{
if (dataTable == null)
{
dataTable = new DataTable();
aliases.ToList().ForEach(alias => dataTable.Columns.Add(alias));
}
}
}
并按如下方式使用:
using (ISession session = sessionFactory.OpenSession())
{
var sqlQuery = session.CreateSQLQuery("SELECT ID, NAME, ADDRESS FROM CUSTOMER");
var transformedQuery = sqlQuery.SetResultTransformer(new DataTableResultTransformer());
return transformedQuery.List().Single();
}
我刚刚创建了一个自定义 ResultTransformer,并在我的 sql 查询中使用它来根据我在 DataTableResultTransformer 中的逻辑来转换查询的结果。
为结果集中的每个项目调用 TransformTupple 方法。元组包含数据,而别名包含数据的名称。因此,我们几乎可以构建和填充 DataTable。一旦结果集中的所有项目都已由 TransformTupple 方法转换,则最后调用 TransformList 方法。集合参数包含我们在 TransformTupple 方法中转换为 DataRow 的所有项目。所以,在这里我们可以很容易地用 DataRows 填充我们的 DataTable 并返回。
希望它对其他处理相同情况的人有所帮助。
更新了将列数据类型考虑在内的示例
[SuppressMessage("Design", "CA1001")]
public class DataTableResultTransformer : IResultTransformer
{
readonly Type?[] m_DataTypeOverrides = Array.Empty<Type?>();
readonly DataTable m_DataTable = new DataTable();
/// <summary>
/// Initializes a new instance of the <see cref="DataTableResultTransformer"/> class.
///
/// Only use this constructor if none of the columns are nullable.
/// </summary>
/// <remarks>Warning: If a field is NULL in the first row, that entire column will be cast as a String.</remarks>
public DataTableResultTransformer()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="DataTableResultTransformer"/> class.
///
/// 1. If dataTypeOverrides for a given column is not null, it is used.
/// 2. If the field is not null for the first row, then that field's data type if used.
/// 3. If both the dataTypeOverride and the field in the first row are null, the column's data type is String.
/// </summary>
/// <param name="dataTypeOverrides">The expected data types.</param>
public DataTableResultTransformer(params Type?[] dataTypeOverrides)
{
m_DataTypeOverrides = dataTypeOverrides;
}
public IList TransformList(IList collection)
{
return new List<DataTable> { m_DataTable };
}
public object TransformTuple(object[] tuple, string[] aliases)
{
if (tuple == null || tuple.Length == 0)
throw new ArgumentException($"{nameof(tuple)} is null or empty.", nameof(tuple));
if (aliases == null || aliases.Length == 0)
throw new ArgumentException($"{nameof(aliases)} is null or empty.", nameof(aliases));
if (m_DataTable.Columns.Count == 0)
{
//Create the DataTable if this is the first row
for (var i = 0; i < aliases.Length; i++)
{
var col = m_DataTable.Columns.Add(aliases[i]);
if (i < m_DataTypeOverrides.Length && m_DataTypeOverrides[i] != null)
col.DataType = m_DataTypeOverrides[i];
else if (tuple[i] != null && tuple[i] != DBNull.Value)
col.DataType = tuple[i].GetType();
}
}
return m_DataTable.Rows.Add(tuple);
}
}