2013-03-26 2 views
-1

다른 레코드와 관련된 일련의 9 가지 워크 플로를 제공하는 회사의 문서 관리 시스템을 작성하라는 요청을 받았습니다. 워크 플로우 중에는 "파일"또는 레코드에 문서를 추가하고 비즈니스 규칙에 따라 공개 웹 사이트에 이러한 문서의 하위 집합을 게시하는 작업이 있습니다.파일 실행 시간이 오래 걸리고 실행 시간이 초과 됨

문서는 거의 예외없이 PDF 형식으로 제공되며, 일반적으로 한 문서에 대해 20 개 미만의 문서가 한 번에 처리됩니다.

웹 응용 프로그램으로 구축하려는 주된 이유는 원격 사이트에서 잠재적으로 느린 연결 속도를 통해 위치간에 위아래로 복사를 시도하는 대신 데이터 센터 및 고속 스위치에 파일을 보관하는 것이 었습니다.

시스템은 문서의 큰 시리즈까지 완벽하게 일했다

(114 개 PDF 문서를 완전히 크기 3백29메가바이트가)의 방식 약 95 %를 초과되었습니다.

코드는 (IncomingDocuments 유형 목록 <의에서는 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 Studio 2010과 함께 .NET 4를 사용하고 있습니다. 따라서 async를 사용하여 타사 구현을 사용해야하고 AsyncBridge (https://nuget.org/packages/AsyncBridge)를 기다려야합니다. 문서를 소싱하고 진행을 업데이트하려면 ajax를 사용하십시오. . Microsoft가 제공 한 비동기 타겟팅 팩을 사용하기 위해 Visual Studio 2012 또는 XP보다 최신 버전의 Windows에 액세스 할 수 없습니다.

이러한 제약 조건을 고려할 때 제한 시간을 피하기 위해 이러한 문서를 위/일괄 처리하고 각 일괄 처리가 추가 될 때 사용자에게 피드백을 제공하는 방법이 있습니까? 구현하기가 간단하다면 F #을 탐험 할 수 있습니다. 또는 Visual Studio 2012에 대해이 케이스를 탄원해야합니까?

+0

궁금한 점이 있습니다. 조직에서 'SharePoint'를 사용하거나 구현할 수 없습니까? 당신이 'Wheel'을 재발 명하도록 요청받은 것처럼 들린다. – MethodMan

+2

질문을 downvoting하는 사람들은 왜 설명 할 수 있습니까? 나는 약간의 추론과 함께 그것을 턱에 가져갈 것보다 더 기쁩니다. – pwdst

+1

타사 제품과 통합해야하므로 Sharepoint는이 시나리오에서 옵션이 아닙니다. – pwdst

답변

9

다른 언어로 옮기거나 IDE 툴링을 업그레이드 할 필요가 없습니다. 당면 문제는 아닙니다. 빠른 응답을 위해 근본적으로 설계된 시스템 (웹 응용 프로그램)이 장기 실행 프로세스에 사용되는 것이 문제입니다.

웹 응용 프로그램에서 잠시 이상 걸리는 것은 비동기 적으로 수행되어야합니다. HTTP의 요청/응답 모델에서 요청을하는 클라이언트에 신속하게 응답하는 것이 여러 가지 이유에서 가장 좋습니다.

장기 실행 프로세스의 경우 "비동기"로 AJAX를 사용하는 것은 아닙니다. 다른 AJAX와 마찬가지로 요청/응답이기 때문에 AJAX를 사용하는 것이 아닙니다.

"비동기식"은 CPU 집약적 인 작업을 처리하는 별도의 서버 측 프로세스를 원한다는 것을 의미하며 웹 응용 프로그램은 실행을위한 작업 대기열 및 상태 확인 이외의 작업을 수행하지 않습니다. 사람들이 그것을 찾을 때의 과제. 그런 다음 작업이 완료된 후 작업 결과를보고 할 수 있습니다.

그래서 아키텍처의 기본 개요는 다음과 같이 될 것이다 : ". 작업을 시작"

  • 웹 응용 프로그램에서 사용자가 버튼을 클릭
  • 웹 응용 프로그램은 작업 (어쩌면 대기중인 사람의 사용자 ID, 타임 스탬프, 당신이 알아야 할 무엇으로) 대기되었음을 나타내는 데이터베이스 테이블에 레코드를 삽입합니다.
  • 별도의 예약 된 응용 프로그램 (콘솔 응용 프로그램 또는 Windows 서비스)이 영구적으로 실행됩니다. (항상 실행중인 Windows 서비스에서 타이머를 사용하거나 몇 분 간격으로 반복해서 실행하도록 예약 된 콘솔 응용 프로그램으로 예약합니다.)이 응용 프로그램은 데이터베이스 테이블에서 대기중인 새 작업을 확인합니다. 응용 프로그램이 작업을 볼 때 데이터베이스 (응용 프로그램 때문에 후속 실행이 병렬로 같은 작업을 실행하려고하지 않는다)의 "시작"과 실행을 시작으로
  • 은, 그것을 표시합니다.
  • 웹 응용 프로그램은 데이터베이스 테이블에서 작업의 상태를보고 요청한 사용자에게 표시하여 사용자가 계속 실행 중인지 확인할 수 있습니다.
  • 작업이 완료되면 데이터베이스 테이블의 작업 레코드가 업데이트되고 결과가 어딘가에 저장됩니다. (결과가 무엇인지에 따라 다릅니다. 데이터를? 데이터베이스에. 어떤 종류의 보고서 파일을? 어딘가에 파일로 저장합니다. 그것은 당신에게 모든이야.) 완료로
  • 이 웹 응용 프로그램은 작업의 상태를 볼 수 있으며, 기록 된 다른 정보 및 사용자는 작업 출력을 보도록 요청할 수 있습니다.

여기서 기억해야 할 중요한 것은 두 응용 프로그램에 책임을 파괴하는 것입니다. 웹 응용 프로그램은 사용자 인터페이스를 제공하기위한 것입니다. 웹 응용 프로그램은 장기 실행 백그라운드 작업에 적합하지 않습니다. 따라서 책임은 그 목적에 더 적합한 별도의 응용 프로그램으로 옮겨집니다. 두 응용 프로그램은 공유 데이터베이스를 통해 조정됩니다. 그래서

, 당신은 당신의 질문의 끝에서 알 수 있듯이, 할 수 있습니다 (그리고해야) 사용자에게 표시하는 큐를 관리 맞게 단순히 "큐"응용 프로그램과의 작업, 그리고 다양한 방법으로한다.

관련 문제