public static bool ValidateDuplicated <TModel, TIdentity>(this ImportMethod method, UnitOfWorkWithReference <TModel, TIdentity> unitOfWork, ImportWorker <TModel> worker) where TModel : class, new() { if (method != ImportMethod.InsertOnly) { return(true); } var valid = true; foreach (var pair in worker.Models) { TModel product = pair.Value; TIdentity importedUnique = unitOfWork.GetIdentity(product); if (unitOfWork.All.ContainsKey(importedUnique)) { valid = false; worker.AddKeyDuplicatedError(pair.Key); } } return(valid); }
private static bool InsertAndUpdate <TModel, TIdentity>( UnitOfWorkWithReference <TModel, TIdentity> unitOfWork, ImportWorker <TModel> worker) where TModel : class, new() { foreach (var pair in worker.Models) { TModel product = pair.Value; TIdentity importedUnique = unitOfWork.GetIdentity(product); TModel target = null; if (unitOfWork.All.TryGetValue(importedUnique, out target)) { Model.CopyTo(product, target, false); unitOfWork.RegisterDirty(target); } else { unitOfWork.RegisterNew(product); } } Model.SetUpdateBy(unitOfWork.New.Concat(unitOfWork.Dirty), worker.LoginUserId); return(worker.Models.Any()); }
public static bool Import <TModel, TIdentity>(this ImportMethod method, UnitOfWorkWithReference <TModel, TIdentity> unitOfWork, ImportWorker <TModel> worker) where TModel : class, new() { switch (method) { case ImportMethod.Replace: return(Replace(unitOfWork, worker)); case ImportMethod.InsertOnly: return(InsertOnly(unitOfWork, worker)); case ImportMethod.InsertAndUpdate: return(InsertAndUpdate(unitOfWork, worker)); default: throw new InvalidOperationException(); } }
private static bool Replace <TModel, TIdentity>( UnitOfWorkWithReference <TModel, TIdentity> unitOfWork, ImportWorker <TModel> worker) where TModel : class, new() { foreach (var pair in worker.Models) { TModel product = pair.Value; TIdentity importedUnique = unitOfWork.GetIdentity(product); TModel target = null; if (unitOfWork.All.TryGetValue(importedUnique, out target)) { Model.CopyTo(product, target, false); unitOfWork.RegisterDirty(target); } else { unitOfWork.RegisterNew(product); } } IEnumerable <TModel> newAndDirty = unitOfWork.Dirty.Concat(unitOfWork.New); Model.SetUpdateBy(newAndDirty, worker.LoginUserId); foreach (TModel model in unitOfWork.All.Values.Except(newAndDirty)) { unitOfWork.RegisterRemoved(model); } if (worker.RecordCount != worker.Models.Count) { worker.Models.Clear(); // 全件取込不可 } return(worker.Models.Any()); }
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); }