如何更快地评估 DataColumn 表达式?

How to evaluate a DataColumn expression faster?

我创建了一个 class,它负责在我需要时评估该行的表达式值,但问题是当我的方法在大数据表中运行时非常慢。

public static class RowExpressionEvaluator
{

    public static object EvaluateValue(DataRow row, string expression, Type outputType)
    {
        if (row == null) throw new ArgumentNullException(nameof(row));
        return EvaluateValue(row.Table, row, expression, outputType);
    }


    private static object EvaluateValue(DataTable table, DataRow row, string expression, Type outputType)
    {
        if (table == null) throw new ArgumentNullException(nameof(table));
        if (row == null) throw new ArgumentNullException(nameof(row));
        if (string.IsNullOrEmpty(expression)) throw new ArgumentException("Expression cannot be null or empty.", nameof(expression));

        if (row.RowState.Equals(DataRowState.Detached)) throw new ArgumentException(@"The rowState is detached.");



        object result = null;

        using (var tempColumn = new DataColumn("Exp", outputType))
        {
            table.Columns.Add(tempColumn);

            tempColumn.Expression = expression;

            if (!row[tempColumn.ColumnName].IsDbNull())
                result = row[tempColumn.ColumnName];

            table.Columns.Remove(tempColumn);
        }

        return result;
    }
}

此代码工作正常,但当数据表包含大数据时速度较慢

我试图改进这段代码,所以当我有很多表达式要计算时 然后当我完成后我处理它它工作得更好但仍然需要改进因为我认为当我添加带有表达式的列时数据表对所有行评估它但我只需要评估传递的行的值。

喜欢这个:

public sealed class BetterRowExpressionEvaluator :IDisposable
{
    private readonly DataRow _row;

    private readonly DataColumn _expressionColumn;


    public BetterRowExpressionEvaluator(DataRow row)
    {
        _row = row ?? throw new ArgumentNullException(nameof(row));
        if (row.RowState.Equals(DataRowState.Detached)) throw new ArgumentException(@"The rowState is detached.");

        _expressionColumn = new DataColumn("Expression",typeof(object));


        DataTable table = _row.Table;

        table.Columns.Add(_expressionColumn);


    }


    public object Evaluate(string expression)
    {
        if (string.IsNullOrEmpty(expression)) throw new ArgumentException("Value cannot be null or empty.", nameof(expression));

        _expressionColumn.Expression = expression;

        return !_row[_expressionColumn.ColumnName].IsDbNull() ? _row[_expressionColumn.ColumnName] : null;
    }


    public void Dispose()
    {
        if (_expressionColumn == null) return;

        DataTable table = _row.Table;

        table.Columns.Remove(_expressionColumn);

        _expressionColumn?.Dispose();

    }
}

我做了类似的事情来克隆行及其父子关系,所以当我评估表达式时它变得更快,这就是我所做的:

    private DataSet CloneTableWithRelations(DataRow row)
    {
        var dataset = new DataSet("EvaluationDataSet") {Locale = CultureInfo.InvariantCulture};

        dataset.Tables.Add(row.Table.Clone());
        dataset.Tables[row.Table.TableName].ImportRow(row);

        foreach (DataRelation parentRelation in row.Table.ParentRelations)
        {
            string relationName = parentRelation.RelationName;

            DataTable parentTable = parentRelation.ParentTable;

            // clone the parent table
            dataset.Tables.Add(parentTable.Clone());

            // copy the parent rows related only to the passed row
            DataRow parentRow= row.GetParentRow(relationName);
            dataset.Tables[parentTable.TableName].ImportRow(parentRow);

            DataColumn parentColumn=parentRelation.ParentColumns[0];

            DataColumn childColumn=parentRelation.ChildColumns[0];

            dataset.Relations.Add(relationName, parentColumn, childColumn,false);

        }

        foreach (DataRelation dataRelation in row.Table.ChildRelations)
        {
            DataTable childTable = dataRelation.ChildTable;

            // clone the parent table
            dataset.Tables.Add(childTable.Clone());

            // copy the parent rows related only to the passed row
            foreach (DataRow childRow in row.GetChildRows(dataRelation.RelationName))
            {
                dataset.Tables[childTable.TableName].ImportRow(childRow);
            }

            DataColumn parentColumn=dataRelation.ParentColumns[0];

            DataColumn childColumn=dataRelation.ChildColumns[0];

            dataset.Relations.Add(dataRelation.RelationName, parentColumn, childColumn,false);
        }

        return dataset;
    }

有没有更好更靠谱的方法?

终于好了很多

当我克隆该行及其父数据和子数据时

使用这个 class 我创建了

  public class RowCloneHandler
  {

    private readonly DataRow _row;

    [System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization", "CA1303:Do not pass literals as localized parameters", Justification = "<Pending>")]
    public RowCloneHandler(DataRow row)
    {
        _row = row ?? throw new ArgumentNullException(nameof(row));

        if (row.RowState.Equals(DataRowState.Detached)) throw new ArgumentException("The rowState is detached.");


    }

    public DataSet CloneToDataSet()
    {
        var ClonedDataset = new DataSet { Locale = CultureInfo.InvariantCulture };

        DataTable clonedMainTable = _row.Table.Clone();

        ClonedDataset.Tables.Add(clonedMainTable);
        ClonedDataset.Tables[_row.Table.TableName].ImportRow(_row);


        CloneParentTablesToDataset(ClonedDataset, clonedMainTable);
        CloneChildTablesToDataSet(ClonedDataset, clonedMainTable);

        return ClonedDataset;
    }

    private  void CloneChildTablesToDataSet(DataSet clonedDataset,  DataTable clonedMainTable)
    {
        foreach (DataRelation dataRelation in _row.Table.ChildRelations)
        {
            DataTable childTable = dataRelation.ChildTable;

            // clone the parent table
            DataTable clonedChildTable = childTable.Clone();

            // copy the parent rows related only to the passed row

            foreach (DataRow childRow in _row.GetChildRows(dataRelation.RelationName))
            {
                clonedChildTable.ImportRow(childRow);
            }

            clonedDataset.Tables.Add(clonedChildTable);

            DataColumn parentColumn = clonedMainTable.Columns[dataRelation.ParentColumns[0].ColumnName];

            DataColumn childColumn = clonedChildTable.Columns[dataRelation.ChildColumns[0].ColumnName];

            clonedDataset.Relations.Add(dataRelation.RelationName, parentColumn, childColumn, false);
        }
    }

    private  void CloneParentTablesToDataset(DataSet clonedDataset,  DataTable clonedMainTable)
    {
        foreach (DataRelation parentRelation in _row.Table.ParentRelations)
        {

            DataTable parentTable = parentRelation.ParentTable;

            // clone the parent table
            DataTable clonedParentTable = parentTable.Clone();

            // copy the parent rows related only to the passed row
            DataRow parentRow = _row.GetParentRow(parentRelation.RelationName);

            clonedParentTable.ImportRow(parentRow);

            clonedDataset.Tables.Add(clonedParentTable);

            DataColumn parentColumn = clonedParentTable.Columns[parentRelation.ParentColumns[0].ColumnName];

            DataColumn childColumn = clonedMainTable.Columns[parentRelation.ChildColumns[0].ColumnName];

            clonedDataset.Relations.Add(parentRelation.RelationName, parentColumn, childColumn, false);

        }
    }



}

您需要为克隆的 DataTable 创建一个 clone of your DataTable, import DataRow,然后在克隆的 DataTable 中添加计算列。这里有一些扩展方法可以做到这一点。

更新: 我修改了代码以考虑与其他 table 的现有关系。代码变得更加复杂,因为现在克隆的 table 必须放在现有的 DataSet 中,并且现有的关系也必须被克隆,并临时重命名。克隆关系是在没有约束的情况下创建的,因此希望性能不会受到负面影响。

public static class DataRowExtensions
{
    public static object Compute(this DataRow dataRow, string expression)
    {
        using (var clonedDT = CloneDataTable(dataRow))
        {
            clonedDT.ImportRow(dataRow);
            var clonedRow = clonedDT.Rows[0];
            var dataColumn = clonedDT.Columns.Add(null, typeof(object), expression);
            return clonedRow[dataColumn];
        }
    }

    public static T Compute<T>(this DataRow dataRow, string expression)
    {
        using (var clonedDT = CloneDataTable(dataRow))
        {
            clonedDT.ImportRow(dataRow);
            var clonedRow = clonedDT.Rows[0];
            var dataColumn = clonedDT.Columns.Add(null, typeof(T), expression);
            return clonedRow.Field<T>(dataColumn);
        }
    }

    public static T? ComputeNullable<T>(this DataRow dataRow, string expression)
        where T : struct
    {
        using (var clonedDT = CloneDataTable(dataRow))
        {
            clonedDT.ImportRow(dataRow);
            var clonedRow = clonedDT.Rows[0];
            var dataColumn = clonedDT.Columns.Add(null, typeof(T), expression);
            return clonedRow.Field<T?>(dataColumn);
        }
    }

    private static DataTable CloneDataTable(DataRow dataRow)
    {
        var dataTable = dataRow.Table;
        var dataSet = dataRow.Table.DataSet;
        if (dataSet == null) return dataTable.Clone();
        var clonedDT = dataSet.Tables.Add();
        foreach (DataColumn column in dataTable.Columns)
        {
            clonedDT.Columns.Add(column.ColumnName, column.DataType);
        }
        var relationsAdded = new List<
            (DataRelation Cloned, DataRelation Original)>();
        foreach (var relation in dataTable.ParentRelations
            .Cast<DataRelation>().ToArray())
        {
            var relationName = relation.RelationName;
            relation.RelationName = Guid.NewGuid().ToString();
            var clonedColumns = relation.ChildColumns
                .Select(c => clonedDT.Columns[c.ColumnName]).ToArray();
            var clonedRelation = dataSet.Relations.Add(relationName,
                relation.ParentColumns, clonedColumns, createConstraints: false);
            relationsAdded.Add((clonedRelation, relation));
        }
        foreach (var relation in dataTable.ChildRelations
            .Cast<DataRelation>().ToArray())
        {
            var relationName = relation.RelationName;
            relation.RelationName = Guid.NewGuid().ToString();
            var clonedColumns = relation.ParentColumns
                .Select(c => clonedDT.Columns[c.ColumnName]).ToArray();
            var clonedRelation = dataSet.Relations.Add(relationName,
                clonedColumns, relation.ChildColumns, createConstraints: false);
            relationsAdded.Add((clonedRelation, relation));
        }
        clonedDT.Disposed += (s, e) => // Cleanup
        {
            clonedDT.Rows.Clear();
            foreach (var entry in relationsAdded)
            {
                dataSet.Relations.Remove(entry.Cloned);
                entry.Original.RelationName = entry.Cloned.RelationName;
            }
            clonedDT.Columns.Clear();
            dataSet.Tables.Remove(clonedDT);
        };
        return clonedDT;
    }
}

用法示例:

var dt = new DataTable();
dt.Columns.Add("Price", typeof(decimal));
dt.Rows.Add(10);
decimal doublePrice = dt.Rows[0].Compute<decimal>("Price * 2");
Console.WriteLine(doublePrice);

输出:

20