为数据库定长字段添加数据

需求:使用C#做一个工具,把csv中的数据追加到DB中。
描述:表PharmacyMemo中存在一个定长600的字段memo,现在有一个csv文件记录了患者的memo数据,根据患者ID+日期,如果数据库中存在该患者数据,那么就把csv的memo内容添加到既存数据中,如果不存在那么就插入一条新的数据。
已知csv文件中的memo最长不会超过600。

因为觉得还可以优化,先记录下简单实现,后面有时间再看看怎么优化。

// 把文件转为DTO
List<MemoCsvDto> csvDtoList = ConvertCsvFileToDto(memoFile);
// 根据患者号分组
Dictionary<int, List<MemoCsvDto>> benkjnoDic = csvDtoList.GroupBy(s => s.NextKjno)
                .ToDictionary(g => g.Key, g => g.ToList());
// 然后根据患者号排序
var sortedCsvDic = (from entry in benkjnoDic
                        orderby entry.Key
                    select entry);

// 获得数据库中所有PharmacyMemo数据(因为库中数据不是太多,因此直接查全表,这里可以根据情况改进)
// C# 版本较低时不支持 out int intValue的形式,必须提前声明
int maxKey = 0;
List<PharmacyMemoDto> existPharmacyMemoList = GetAllKanjaPharmacyMemos(out maxKey);
// 根据患者号加日期分组
Dictionary<string, List<PharmacyMemoDto>> existPharmacyMemoDic =
        existPharmacyMemoList.GroupBy(s => GetPharmacyMemoGroupKey(s))
        .ToDictionary(g => g.Key, g => g.ToList());

// 准备好要插入的数据
List<PharmacyMemoDto> insertList = new List<PharmacyMemoDto>();
List<PharmacyMemoDto> updateList = new List<PharmacyMemoDto>();
// 字符串拼接的stringBuilder
StringBuilder sb = new StringBuilder();

foreach (var kjnoPair in sortedCsvDic)
{
    int nextKjno = kjnoPair.Key;
    // 判断非法数据,如果患者表中没有这个患者号就不继续
    if (!existNextKjnoSet.Contains(nextKjno))
    {
        // C#版本较低时不支持$""的形式
        Log.Info(string.Format("NEXTの患者テーブルにKJNO:{0}はありません。", nextKjno));
        continue;
    }

    List<MemoCsvDto> oneKanjaCsvList = kjnoPair.Value;
    // 根据日期分组
    // 因为C#的dictionary的键不能为null,因此使用一个自定义类来作为Key
    Dictionary<NullableKey<DateTime?>, List<MemoCsvDto>> groupByEditDayDic = 
            oneKanjaCsvList.GroupBy(s => new NullableKey<DateTime?>(s.EditDay))
                    .ToDictionary(g => g.Key, g => g.ToList());

    foreach (var editDayPair in groupByEditDayDic)
    {
        sb.Clear();
        DateTime? editday = editDayPair.Key.Value;
        List<MemoCsvDto> tobeSaveList = editDayPair.Value.OrderBy(s => s.RowNumber).ToList();

        string csvGroupKey = GetPharmacyMemoGroupKey(nextKjno, editday);

        PharmacyMemoDto dbDto;
        if (existPharmacyMemoDic.ContainsKey(csvGroupKey))
        {
            // 找到DB主键最大的数据
            dbDto = existPharmacyMemoDic[csvGroupKey].OrderBy(dto => dto.MemoCode).Reverse().First();
            sb.Append(dbDto.MemoText);
        }
        else
        {
            // 库中不存在时就虚构一个数据
            maxKey++;
            dbDto = new PharmacyMemoDto();
            dbDto.MemoCode = maxKey;
            dbDto.OperatorCode = nextKjno;
            dbDto.OperatorFlag = 1;
            dbDto.MemoText = string.Empty;
            dbDto.EditDay = editday ?? DEFAULT_EDITDAY;
            // 自定义追加的字段,非DB字段
            dbDto.IsDbData = false;

        }

        PharmacyMemoDto currentDto = dbDto;
        // 向insertList或者updateList插入的时机就是长度大于600或者最后一个
        {
            for (int i = 0; i < tobeSaveList.Count; i++)
            {
                MemoCsvDto csvDto = tobeSaveList[i];

                // 长度大于600时
                if ((Encoding.Default.GetByteCount(sb.ToString())
                        + Encoding.Default.GetByteCount(csvDto.Memo)
                        + Encoding.Default.GetByteCount(Environment.NewLine)) > 600)
                {
                    // 保存当前dto
                    currentDto.MemoText = sb.ToString();
                    if (currentDto.IsDbData)
                    {
                        updateList.Add(currentDto);
                    }
                    else
                    {
                        insertList.Add(currentDto);
                    }

                    sb.Clear();
                    sb.Append(csvDto.Memo);

                    // 创建新的dto
                    maxKey++;
                    currentDto = new PharmacyMemoDto();
                    currentDto.MemoCode = maxKey;
                    currentDto.OperatorCode = nextKjno;
                    currentDto.OperatorFlag = 1;
                    currentDto.MemoText = sb.ToString();
                    currentDto.EditDay = editday ?? DEFAULT_EDITDAY;
                    currentDto.IsDbData = false;
                }
                else
                {
                    // 小于600时只需要拼接memo字段即可
                    if (sb.ToString() == "")
                    {
                        sb.Append(csvDto.Memo);
                    }
                    else
                    {
                        sb.AppendLine().Append(csvDto.Memo);
                    }
                }

                if (i == tobeSaveList.Count - 1)
                {
                    currentDto.MemoText = sb.ToString();
                    if (currentDto.IsDbData)
                    {
                        updateList.Add(currentDto);
                    }
                    else
                    {
                        insertList.Add(currentDto);
                    }
                }
            }
        }
    }
}


/// <summary>
/// use this class to solve dictionary's key can't be null
/// </summary>
/// <typeparam name="T"></typeparam>
class NullableKey<T>
{
    private readonly T _value;

    public NullableKey(T value)
    {
        _value = value;
    }

    public T Value
    {
        get { return _value; }
    }

    public override bool Equals(object obj)
    {
        var key = obj as NullableKey<T>;
        return key != null &&
                EqualityComparer<T>.Default.Equals(Value, key.Value);
    }

    public override int GetHashCode()
    {
        return -1937169414 + EqualityComparer<T>.Default.GetHashCode(Value);
    }
}

题外话:
如果再复杂一点,可以根据DFA画出状态变化图,然后在进行编码。

标签: C#

添加新评论