public async Task <ImportResult> ImportAsync(string csvPath, ImportMethod method, CancellationToken?token, IProgress <int> progress) { IEnumerable <TModel> original = (LoadAsync == null) ? Enumerable.Empty <TModel>() : await LoadAsync(); if (token?.IsCancellationRequested ?? false) { return(null); } progress?.Report(20); // 進捗:取込対象データ読込完了 var unitOfWork = new UnitOfWorkWithReference <TModel, TIdentity>( original, createIdentity); var worker = new ImportWorker <TModel>(RowDef); worker.LoginUserId = UserId; worker.LoginUserCode = UserCode; worker.LoginCompanyId = CompanyId; worker.LoginCompanyCode = CompanyCode; await InitializeWorker?.Invoke(worker); var valid = true; var csv = Parser.Parse(csvPath); RowDef.SetupFields(); var fieldDefs = RowDef.Fields .Where(f => !f.Ignored) .OrderBy(f => f.FieldNumber) .ToArray(); foreach (var fields in csv.Skip(RowDef.StartLineNumber)) { worker.NewRecord(fields); if (fields.Length < fieldDefs.Length) { foreach (var field in fieldDefs.Skip(fields.Length)) { Errors.Add(new WorkingReport { LineNo = worker.RecordCount, FieldNo = field.FieldIndex, FieldName = field.FieldName, Value = string.Empty, Message = $"{field.FieldName}がありません。", }); } } valid &= RowDef.Do(worker); // 書式チェック&エラー情報収集 if (worker.Reports.Any()) { Errors.AddRange(worker.Reports); worker.Reports.Clear(); } } progress?.Report(40); // 進捗:CSVファイル読込完了 // DB関連チェック var validate = new CodeToIdWorker <TModel>(worker); // importの結果を引き渡す valid &= RowDef.Do(validate); // DBチェック&エラー情報収集 Errors.AddRange(validate.Reports); await AdditionalWorker?.Invoke(worker); var additionalValidate = new AdditionalValidationWorker <TModel>(worker) { ImportMethod = method, }; valid &= RowDef.Do(additionalValidate); // DBチェック&エラー情報収集 Errors.AddRange(additionalValidate.Reports); foreach (var lineNo in Errors.Where(x => x.LineNo.HasValue).Select(x => x.LineNo.Value).Distinct()) { worker.Models.Remove(lineNo); } // DB重複チェック if (!method.ValidateDuplicated(unitOfWork, worker)) { worker.Reports.ForEach(r => worker.Models.Remove(r.LineNo.Value)); Errors.AddRange(worker.Reports); worker.Reports.Clear(); } // ファイル内キー重複チェック var uniqueKeys = new Dictionary <TIdentity, int>(); var duplicatedLines = new List <int>(); foreach (var pair in validate.Models) { // 必須項目が空欄の時は、Nullまたはゼロになっている。 // キー項目が部分的にでも空欄の場合、重複チェックをパスする。 var identity = createIdentity(pair.Value); if (ContainsNull(identity)) { identity = default(TIdentity); } if (identity == null || identity.Equals(default(TIdentity))) { continue; } if (uniqueKeys.ContainsKey(identity)) { switch (RowDef.DuplicateAdoption) { case DuplicateAdoption.BothAreErrors: var duplicated = uniqueKeys[identity]; if (!duplicatedLines.Contains(duplicated)) { duplicatedLines.Add(duplicated); } duplicatedLines.Add(pair.Key); break; case DuplicateAdoption.First: duplicatedLines.Add(pair.Key); break; } } else { uniqueKeys.Add(identity, pair.Key); } if (token.HasValue && token.Value.IsCancellationRequested) { return(null); } } progress?.Report(60); // 進捗:データベース関連チェック完了 switch (RowDef.TreatDuplicateAs) { case TreatDuplicateAs.Error: duplicatedLines.ForEach(lineNo => { Errors.Add(new WorkingReport() // キー重複 { LineNo = lineNo, FieldName = RowDef.KeyFields?.FirstOrDefault().FieldName ?? string.Empty, Message = "重複しているため、インポートできません。", Value = createIdentity(validate.Models[lineNo]).ToString(), }); }); break; case TreatDuplicateAs.Ignore: // エラーにはせず、ここで取込対象から取り除く。 duplicatedLines.ForEach(lineNo => worker.Ignore(lineNo)); break; } if (token.HasValue && token.Value.IsCancellationRequested) { return(null); } // エラーデータを更新対象から取り除く var errorLines = Errors .GroupBy(report => report.LineNo) .Select(g => g.Key) .ToList(); if (errorLines.Any(lineNo => !lineNo.HasValue)) { validate.Models.Clear(); } else { errorLines.ForEach(lineNo => validate.Models.Remove(lineNo.Value)); } if (token.HasValue && token.Value.IsCancellationRequested) { return(null); } progress?.Report(80); // 進捗:すべてのデータチェック完了 ImportResult result = null; if (method.Import(unitOfWork, worker) && RegisterAsync != null) { result = await RegisterAsync(unitOfWork); PostImportHanlder?.Invoke(worker, result); Debug.Assert(result != null, $"{nameof(RegisterAsync)}()が{nameof(ImportResult)}を返しませんでした。"); } else // 登録処理をしなかった、または取込可能件数がゼロ { result = new ImportResult { ProcessResult = new ProcessResult { Result = true }, InsertCount = 0, UpdateCount = 0, DeleteCount = 0, }; } Errors.AddRange(worker.Reports); if (Errors.Any() && !string.IsNullOrWhiteSpace(ErrorLogPath)) { OutputErrorLog(ErrorLogPath, Errors, csvPath); } result.ValidItemCount = worker.Models.Count; result.InvalidItemCount = worker.RecordCount - result.ValidItemCount; UnitOfWork = unitOfWork; progress?.Report(100); // 進捗:取込完了 return(result); }
public async Task <int> ExportAsync(string csvPath, List <TModel> original, CancellationToken cancel, IProgress <int> progress, Action <StringBuilder> specialHeaderHandler = null) { if (cancel.IsCancellationRequested) { return(0); } progress.Report(20); // 進捗:取込対象データ読込完了 var export = new ExportWorker <TModel>(RowDef.DataExpression); export.LoginUserId = UserId; export.LoginUserCode = UserCode; export.LoginCompanyId = CompanyId; export.LoginCompanyCode = CompanyCode; RowDef.SetupFields(); var sb = new StringBuilder(); specialHeaderHandler?.Invoke(sb); // header var header = RowDef.GetHeaderAry(); if (header != null) { sb.AppendLine(string.Join(RowDef.Delimiter, header)); } await Task.Run(() => { foreach (TModel m in original) { export.NewRecord(m); RowDef.Do(export); } }, cancel); // DB関連チェック var validate = new IdToCodeWorker <TModel>(export); // Exportの結果を引き渡す RowDef.Do(validate); // DBチェック&エラー情報収集 // TODO: 文字列項目は強制でダブルクォート という要件 await Task.Run(() => { foreach (var record in validate.Records.Values) { for (int i = 0; i < record.Count; i++) { if (record[i] == null) { continue; } if (record[i].IndexOf("\"") >= 0 || record[i].IndexOf(RowDef.Delimiter) >= 0 || record[i].IndexOf("\r") >= 0 || record[i].IndexOf("\n") >= 0) { record[i] = "\"" + record[i].Replace("\"", "\"\"") + "\""; } } sb.AppendLine(string.Join(RowDef.Delimiter, record.ToArray())); } }); try { File.WriteAllText(csvPath, sb.ToString(), CsvEncoding); } catch (Exception e) { Exception = e; return(0); } return(validate.Records.Count); }