私が働いている組織のために、さまざまなレコードに関連する一連の9つの異なるワークフローを提供するドキュメント管理システムを作成するように依頼されました。ワークフローの中には、「ファイル」またはレコードにドキュメントを追加し、ビジネスルールに基づいてこれらのドキュメントのサブセットを公開Webサイトに公開することが含まれます。
文書はほとんど例外なくPDF形式であり、通常、1つのレコードに対して一度に処理されるのは20未満です。
これをWebアプリケーションとして構築する主な理由は、リモートサイトで接続速度が遅くなる可能性がある場所間でファイルをコピーしたり、コピーしたりするのではなく、データセンターと高速スイッチにファイルを保持することでした。
システムは、より大きな一連のドキュメント(合計で329MBのサイズの114のPDFドキュメント)が途中で約95%タイムアウトするまで、問題なく機能していました。
コードは次のとおりです(IncomingDocumentsのタイプはList <FileInfo>です)-
List<string> filesSuccessfullyAdded = new List<string>();
foreach (FileInfo incomingFile in IncomingDocuments)
{
FileOperations.AddDocument(incomingFile, false, ApplicationCode, (targetDirectoryPath.EndsWith(@"\") ? targetDirectoryPath : targetDirectoryPath + @"\"));
FileInfo copiedDocument = new FileInfo(Path.Combine(targetDirectoryPath, incomingFile.Name));
if (copiedDocument.Exists && copiedDocument.Length == incomingFile.Length && copiedDocument.LastWriteTime == incomingFile.LastWriteTime)
{
filesSuccessfullyAdded.Add(copiedDocument.Name);
}
}
if (filesSuccessfullyAdded.Any())
{
SetupConfirmationLiteral.Text += "<p class='info'>The following files have been successfully added to the application file-</p>";
XDocument successfullyAddedList = new XDocument(
new XElement("ul", new XAttribute("class", "documentList")));
foreach (string successfulFile in filesSuccessfullyAdded)
{
successfullyAddedList.Root.Add(new XElement("li", successfulFile));
}
SetupConfirmationLiteral.Text += successfullyAddedList.ToString();
}
var notSuccessfullyAdded = from FileInfo incomingDocument in IncomingDocuments
where !filesSuccessfullyAdded.Contains(incomingDocument.Name)
orderby incomingDocument.Name ascending
select incomingDocument.Name;
if (notSuccessfullyAdded.Any())
{
SetupConfirmationLiteral.Text += "<p class='alert'>The following files have <strong>not</strong> been successfully added to the application file-</p>";
XDocument notAddedList = new XDocument(
new XElement("ul", new XAttribute("class", "documentList")));
foreach (string notAdded in notSuccessfullyAdded)
{
notAddedList.Root.Add(new XElement("li", notAdded));
}
SetupConfirmationLiteral.Text += notAddedList.ToString();
SetupConfirmationLiteral.Text += "<p>A file of the same name may already exist in the target location.</p>";
}
の効用法で-
public static void AddDocument(FileInfo sourceFile, bool appendName, string applicationCode, string targetPath)
{
try
{
DirectoryInfo targetDirectory = new DirectoryInfo(targetPath);
if (targetDirectory.Exists)
{
string targetFileName = (appendName ? sourceFile.Name.Insert(sourceFile.Name.IndexOf(sourceFile.Extension, StringComparison.Ordinal), " UPDATED") : sourceFile.Name);
if (targetDirectory.GetFiles(targetFileName).Any())
{
//Do not throw an exception if the file already exists. Silently return. If the file exists and matches both last modified and size it won't be reported, and can be archived as normal,
//otherwise it should be reported to user in the calling method.
return;
}
string targetFileUnc = Path.Combine(targetPath, targetFileName);
sourceFile.CopyTo(targetFileUnc, overwrite: false);
Logging.FileLogEntry(username: (HttpContext.Current.User.Identity.IsAuthenticated ? HttpContext.Current.User.Identity.Name : "Unknown User"), eventType: LogEventType.AddedDocument,
applicationCode: applicationCode, document: sourceFile.Name, uncPath: targetFileUnc);
}
else
{
throw new PdmsException("Target directory does not exist");
}
}
catch (UnauthorizedAccessException ex)
{
throw new PdmsException("Access was denied to the target directory. Contact the Service Desk.", ex);
}
catch (PathTooLongException)
{
throw new PdmsException(string.Format("Cannot add document {0} to the Site File directory for Application {1} - the combined path is too long. Use the Add Documents workflow to re-add documents to this Site File after renaming {0} to a shorter name.", sourceFile.Name, applicationCode ));
}
catch (FileNotFoundException ex)
{
throw new PdmsException("The incoming file was not found. It may have already been added to the application file.", ex);
}
catch (DirectoryNotFoundException ex)
{
throw new PdmsException("The source or the target directory were not found. The document(s) may have already been added to the application file.", ex);
}
catch (IOException ex)
{
throw new PdmsException("Error adding files - file(s) may be locked or there may be server or network problem preventing the copy. Contact the Service Desk.", ex);
}
}
実際のコピーと監査を行うため。PdmsExceptionは、ユーザーに役立つエラーメッセージを表示するために使用する特定の例外クラスであり、ユーザーが可能な場合は独自の問題を解決できるようにするか、少なくとも失敗の理解可能な理由を示します。
ExecutionTimeoutプロパティ(http://msdn.microsoft.com/en-us/library/system.web.configuration.httpruntimesection.executiontimeout.aspx)をデフォルトの110秒(最大300秒)を超えて増やすことができることはわかっています。 -これはおそらく、この場合はタイムアウトが発生しなくなることを意味しますが、ユーザーが何千ものドキュメントを追加または公開しようとするとどうなりますか。このソリューションは適切に拡張できず、問題を解決するのではなく、単に延期します。
Visual Studio2010で.NET4を使用しているため、私が知る限り、非同期のサードパーティ実装を使用し、AsyncBridge(https://nuget.org/packages/AsyncBridge)のように待機する必要があります。ドキュメントを作成し、ajaxを使用して進捗状況を更新します。Microsoftが提供する非同期ターゲティングパックを使用するために、VisualStudio2012またはXPよりも新しいWindowsにアクセスできません。
これらの制約がある場合、タイムアウトを回避し、(理想的には)各バッチが追加されるときにユーザーにフィードバックを提供するために、これらのドキュメントを分割/バッチ処理する方法はありますか?実装が簡単であれば、F#を検討することもできます。または、これについてVisual Studio 2012のケースを主張する必要がありますか?