【问题标题】:Efficient Converting DataTable to List in C#在 C# 中高效地将 DataTable 转换为 List
【发布时间】:2021-10-14 06:58:21
【问题描述】:

我想将 DataTable 序列化为 C# 中的列表。我的代码大约需要 10 分钟才能获取 100K 数据并将其转换为列表。以前尝试使用顺序方式,但处理时间增加了三倍。所以,我尝试了 Parallel,它节省了 1/3 的时间。但是,还是太慢了。

如果有人可以帮助加快速度。任何帮助表示赞赏。

public async Task<List<DataPointModel>> GetDetails(HashParameterModel parameterModel)
{
    List<DataPointModel> result = new List<DataPointModel>();
    try
    {
        var query = "USP_GetDetailsFromMetaData";
        using (var sqlConnection = new SqlConnection(connectionString))
        {
            using (var sqlCommand = new SqlCommand(query, sqlConnection))
            {
                sqlCommand.CommandType = CommandType.StoredProcedure;
                sqlCommand.Parameters.AddWithValue("@GroupID", parameterModel.GroupID);
                sqlCommand.CommandTimeout = 0;
                sqlConnection.Open();
                DataTable dataTable = new DataTable();
                DataSet dataSet = new DataSet();
                SqlDataAdapter da = new SqlDataAdapter
                {
                    SelectCommand = sqlCommand
                };
                da.Fill(dataSet);

                dataTable = dataSet.Tables[0];
                DataTable dataTable1 = dataSet.Tables[1];
                var questionList = dataTable1.AsEnumerable();
                if (dataTable.Rows.Count > 0)
                {
                    Parallel.ForEach(dataTable.AsEnumerable(), new ParallelOptions { MaxDegreeOfParallelism = 5 }, row =>
                    {
                        DataPointModel model = new DataPointModel();
                        model.ID = Convert.ToInt32(row["ID"]);
                        model.GroupID = Convert.ToInt32(row["GroupID"]);
                        model.ProviderID = Convert.ToInt32(row["SalvageProviderID"]);
                        model.ClaimNumber = row["ClaimNumber"].ToString();
                        model.PolicyNumber = row["PolicyNumber"].ToString();
                        model.DataPoint1 = row["DataPoint1"].ToString();
                        model.DataPoint2 = row["DataPoint2"].ToString();
                        model.DataPoint3 = row["DataPoint3"].ToString();
                        model.DataPoint4 = row["DataPoint4"].ToString();
                        model.FirstName = row["FirstName"].ToString();
                        model.LastName = row["LastName"].ToString();
                        model.PrimaryDamage = row["PrimaryDamage"].ToString();
                        model.Type = row["TypeCode"].ToString();
                        model.LossDate = row["LossDate"].ToString();
                        model.QuestionList = (from p in questionList
                                              where p.Field<int>("ID") == model.ID
                                              select new QuestionResponseModel()
                                              {
                                                  QuestionID = p.Field<int>("QuestionID").ToString(),
                                                  Response = p.Field<string>("ResponseValue")
                                              }).ToList();
                        result.Add(model);
                    });
                }
            }
        }
    }
    catch (Exception ex)
    {
        throw ex;
    }

    return result;
}

DataSet有两个DataTable

DataTable dataTable = dataSet.Tables[0]; // Details
DataTable dataTable1 = dataSet.Tables[1]; // QUestionList

我认为它在为 QuestionList 循环时会消耗时间,它可能有大约 120K 行。任何建议

【问题讨论】:

  • 跳过 DataTable 并直接从 DataReader 创建对象。
  • 或者使用 dapper 从查询结果中直接实现你的列表
  • 不要将 120K 数据行读入内存。
  • 你最终将如何处理这些数据?
  • 检查存储过程是否真的只查询您需要的字段并将它们直接映射到您的模型中。从数据库中获取数据是否会比填充对象花费更多时间?

标签: c# .net asp.net-mvc serialization datatable


【解决方案1】:

实现性能改进的一个简单快速的方法是从您的 questionList 构建一个查找表,并访问它来获取问题,而不是执行这段代码

model.QuestionList = (from p in questionList
                      where p.Field<int>("ID") == model.ID
                      select new QuestionResponseModel()
                      {
                          QuestionID = p.Field<int>("QuestionID").ToString(),
                          Response = p.Field<string>("ResponseValue")
                      }).ToList();

所以添加以下内容

var questionList = dataTable1.AsEnumerable();
//maybe add .AsParallel() - questionList.AsParallel().ToLookUp(...)
var questionLookUp = questionList.ToLookUp(x => x.Field<int>("ID"), x => new QuestionResponseModel() { QuestionID = x.Field<int>("QuestionID"), Response = p.Field<string>("ResponseValue") });

而不是像这样使用它

model.QuestionList = questionLookUp[model.ID].ToList();

https://docs.microsoft.com/en-us/dotnet/api/system.linq.lookup-2

https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.tolookup

代码未经测试,希望我没有犯很多错误。

【讨论】:

    【解决方案2】:

    详细信息表中是否存在重复的模型 ID?如果是这样,这可能有助于避免多次运行问题列表查询:

                            model.QuestionList = getQuestions(model.ID);
    

    方法:

        public Dictionary<int, List<QuestionResponseModel>> questionBuffer = new Dictionary<int, List<QuestionResponseModel>>();
    
        public List<QuestionResponseModel> getQuestions(int ID)
        {
            if (questionBuffer.ContainsKey(ID)) return questionBuffer[ID];
    
            List<QuestionResponseModel> questions =  (from p in questionList
                                  where p.Field<int>("ID") == model.ID
                                  select new QuestionResponseModel()
                                  {
                                      QuestionID = p.Field<int>("QuestionID").ToString(),
                                      Response = p.Field<string>("ResponseValue")
                                  }).ToList();
            questionBuffer.Add(ID, questions);
            return questions;
        }
    

    【讨论】:

    • 所以一个带有额外步骤的查找表:)
    • 哈,我猜。当我开始打字时,你的帖子不在那里。但是,如果详细信息表只有少量模型 id,这将避免将整个 120k 行拉入内存
    • 它们已经在内存中,在数据表中。您的解决方案的缺点是针对每个唯一的ID 多次枚举questionList。如果ID 被多次传递,它只会提高性能,因为您正在缓存每个ID 的结果,尽管从名称ID 来看,情况会令人惊讶,ID 应该是独一无二的。 ToLookUp 仅枚举列表一次并在进行时构建查找。当然构建一次查找需要一些时间,但它应该比多次枚举 120k 执行得更好。
    猜你喜欢
    • 2013-10-30
    • 2013-10-14
    • 2011-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-10
    相关资源
    最近更新 更多