/// <summary> /// 實際的 Schedule Job 執行序工作的方法 WorkThreadFunction. /// <param name="JobMethod">Job MethodInfo</param> /// <param name="TargetJobIns">要呼叫的主 Job 物件的執行個體</param> /// <param name="job">JobData</param> /// <param name="IsReTry">目前是否為 ReTry 的呼叫</param> /// <param name="writeLogAttr"></param> /// <param name="writeExAttr"></param> /// <param name="errorLevelAttr"></param> /// </summary> private Thread_Exec_Reault WorkerThreadFunction( MethodInfo JobMethod, object TargetJobIns, JobData job, bool IsReTry, LogAttributeData <WriteLogAttribute> writeLogAttr, LogAttributeData <WriteExceptionLogAttribute> writeExAttr, LogAttributeData <ErrorLevelAttribute> errorLevelAttr, Logger loggerInner, Logger loggerInnerErr) { Thread_Exec_Reault result = Thread_Exec_Reault.SUCCESS; #region NLog 相關物件 //Logger loggerInner = NLogHelper.GetFileLogConfig(LogLevel.Info); //Logger loggerInnerErr = NLogHelper.GetErrorFileLogConfig(LogLevel.Error); #endregion try { ////紀錄 LOG 工作開始 //NLogHelper.LoggerWriter(loggerInner, job.JobName, job.DLLName, "[ScheduleJob]", "[工作開始]", ""); //執行工作 JobMethod.Invoke(TargetJobIns, new object[] { job, writeLogAttr, writeExAttr, errorLevelAttr, loggerInner, loggerInnerErr }); //執行成功後,紀錄 [執行紀錄] 表示執行過此 Job,若發生錯則只在下方的 catch 中記錄 錯誤資訊. NLogHelper.LoggerWriter(loggerInner, job.JobName, job.DLLName, string.Format("[{0}]", job.JobName)); } catch (Exception ex) { result = Thread_Exec_Reault.FAIL; //如果是被呼叫的 DLL內發生錯誤,就將錯誤 LOG 起來 //throw ex; //當然,先決條件就是,DLL 必須存在,否則 TargetJobIns 會是 null. if (JobMethod != null && TargetJobIns != null) { string ErrMessage = ex.InnerException != null ? ex.InnerException.Message : ex.Message; if (IsReTry) { NLogHelper.LoggerExWriter(loggerInnerErr, job.JobName, job.DLLName, string.Format("[{0}]", job.JobName), string.Format(" [ReTry] {0}", ErrMessage)); } else { NLogHelper.LoggerExWriter(loggerInnerErr, job.JobName, job.DLLName, string.Format("[{0}]", job.JobName), ErrMessage); } } } finally { //不管執行序內是正常執行結束、或是發生錯誤後才結束,都將執行序設定為執行序結束 _EventStopThread.Set(); } return(result); }
/// <summary> /// 傳入 Assembly 物件,從 Assembly 指定的 NameSpace & ClassName 到 Assembly 中取得並回傳 MethodInfo. /// </summary> /// <param name="ass">Assembly for Job DLL</param> /// <param name="Job">JobData 物件</param> /// <returns>Tuple 物件 (包含:MethodIfo, new Job Instance)</returns> public static Tuple <MethodInfo, object, LogAttributeData <WriteLogAttribute>, LogAttributeData <WriteExceptionLogAttribute>, LogAttributeData <ErrorLevelAttribute> > Common( Assembly ass, JobData Job) { object jobClassObject = null; LogAttributeData <WriteLogAttribute> writeLogAttr = new LogAttributeData <WriteLogAttribute>(); LogAttributeData <WriteExceptionLogAttribute> writeExAttr = new LogAttributeData <WriteExceptionLogAttribute>(); LogAttributeData <ErrorLevelAttribute> errorLevelAttr = new LogAttributeData <ErrorLevelAttribute>(); MethodInfo jobMethod = null; MethodInfo BeforePrepareMethod = null; MethodInfo PrepareDataMethod = null; MethodInfo ProcessDataMethod = null; MethodInfo AfterProcessMethod = null; Type magicType = ass.GetType(string.Format("{0}.{1}", Job.NameSpace, Job.ClassName)); if (magicType != null) { //判別 Load 進來的 Class 型態是不是實作 IAction 的介面,若不是,則 throw Exception. if (magicType.GetInterfaces().AsEnumerable <Type>().Where(c => c == typeof(IAction)).FirstOrDefault() == null) { throw new IActionNotImplementException(string.Format("The {0} of class {1} is must be to inheritance by interface of IAtion.", Job.DLLName, Job.ClassName)) { Datetime = DateTime.Now, MethodInfo = string.Format("DLLPath={0}, DLLName={1}, NameSpace={2}, ClassName={3}", dllPath, Job.DLLName, Job.NameSpace, Job.ClassName) }; } ConstructorInfo jobConstructor = magicType.GetConstructor(Type.EmptyTypes); jobClassObject = jobConstructor.Invoke(null); jobMethod = magicType.GetMethod(JOB_METHOD_NAME); BeforePrepareMethod = magicType.GetMethod(BEFORE_PREPARE_METHOD_NAME, BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.NonPublic); PrepareDataMethod = magicType.GetMethod(PREPARE_DATA_METHOD_NAME, BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.NonPublic); ProcessDataMethod = magicType.GetMethod(PROCESS_DATA_METHOD_NAME, BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.NonPublic); AfterProcessMethod = magicType.GetMethod(AFTER_PROCESS_METHOD_NAME, BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.NonPublic); #region 包裝 WriteLogAttribute 容器 writeLogAttr.SetBeforePrepareMethod(BeforePrepareMethod); writeLogAttr.SetPrepareDataMethod(PrepareDataMethod); writeLogAttr.SetProcessDataMethod(ProcessDataMethod); writeLogAttr.SetAfterProcessMethod(AfterProcessMethod); #endregion #region 包裝 WriteLogExAttribute 容器 writeExAttr.SetBeforePrepareMethod(BeforePrepareMethod); writeExAttr.SetPrepareDataMethod(PrepareDataMethod); writeExAttr.SetProcessDataMethod(ProcessDataMethod); writeExAttr.SetAfterProcessMethod(AfterProcessMethod); #endregion #region 包裝 ErrorLevelAttribute 容器 errorLevelAttr.SetBeforePrepareMethod(BeforePrepareMethod); errorLevelAttr.SetPrepareDataMethod(PrepareDataMethod); errorLevelAttr.SetProcessDataMethod(ProcessDataMethod); errorLevelAttr.SetAfterProcessMethod(AfterProcessMethod); #endregion if (jobMethod == null) { throw new JobMethodNotFoundException("Please check the method is correct!") { Datetime = DateTime.Now, MethodInfo = string.Format("DLLPath={0}, DLLName={1}, NameSpace={2}, ClassName={3}", dllPath, Job.DLLName, Job.NameSpace, Job.ClassName) }; } } else { throw new JobMethodNotFoundException("Please check the namespace or class name is correct!") { Datetime = DateTime.Now, MethodInfo = string.Format("DLLPath={0}, DLLName={1}, NameSpace={2}, ClassName={3}", dllPath, Job.DLLName, Job.NameSpace, Job.ClassName) }; } return(Tuple.Create(jobMethod, jobClassObject, writeLogAttr, writeExAttr, errorLevelAttr)); }
/// <summary> /// 傳入 JobData 物件,從 JobData 指定的 NameSpace & ClassName 到 Assembly 中取得並回傳 MethodInfo. /// </summary> /// <param name="Job">JobData 物件</param> public static Tuple <MethodInfo, object, LogAttributeData <WriteLogAttribute>, LogAttributeData <WriteExceptionLogAttribute>, LogAttributeData <ErrorLevelAttribute> > GetExecuteJobMethod(JobData Job) { MethodInfo result = null; object jobClassObject = null; LogAttributeData <WriteLogAttribute> writeLogAttr = new LogAttributeData <WriteLogAttribute>(); LogAttributeData <WriteExceptionLogAttribute> writeExAttr = new LogAttributeData <WriteExceptionLogAttribute>(); LogAttributeData <ErrorLevelAttribute> errorLevelAttr = new LogAttributeData <ErrorLevelAttribute>(); string appPath = string.Empty; string nameSapce = string.Empty; try { Assembly ExecuteAssembly = Assembly.GetExecutingAssembly(); appPath = Path.GetDirectoryName(ExecuteAssembly.Location); dllPath = string.Format("{0}.dll", Job.DLLName); // 判斷上傳的實體路徑下是否有此文件,沒有提示錯誤信息 if (!System.IO.File.Exists(Path.Combine(appPath, dllPath))) { throw new JobDLLNotFoundException(string.Format("The {0} DLL is not exist!", dllPath)) { Datetime = DateTime.Now, MethodInfo = string.Format("DLLPath={0}, DLLName={1}, NameSpace={2}, ClassName={3}", dllPath, Job.DLLName, Job.NameSpace, Job.ClassName) }; } else { //要讀取的 DLL 路徑. string assPath = Path.Combine(appPath, dllPath); string dllConfigPath = string.Format("{0}.config", assPath); Assembly ass = null; //先判斷是否有這個 DLL 的 dll.config if (File.Exists(dllConfigPath)) { var resultValue = GetCustomConfig(assPath, dllConfigPath, "connectionStrings"); ass = resultValue.Item1; } else { //載入指定路徑中的 Assembly. ass = Assembly.LoadFile(assPath); } if (ass != null) { Type magicType = ass.GetType(string.Format(".", Job.NameSpace, Job.ClassName)); var resultValue = Common(ass, Job); result = resultValue.Item1; jobClassObject = resultValue.Item2; writeLogAttr = resultValue.Item3; writeExAttr = resultValue.Item4; errorLevelAttr = resultValue.Item5; } else { throw new JobDLLNotFoundException(string.Format("The {0} DLL is not exist!", dllPath)) { Datetime = DateTime.Now, MethodInfo = string.Format("DLLPath={0}, DLLName={1}, NameSpace={2}, ClassName={3}", dllPath, Job.DLLName, Job.NameSpace, Job.ClassName) }; } } } catch (Exception ex) { throw new JobDLLNotFoundException(ex.Message) { Datetime = DateTime.Now, MethodInfo = string.Format("DLLPath={0}, DLLName={1}, NameSpace={2}, ClassName={3}", dllPath, Job.DLLName, Job.NameSpace, Job.ClassName) }; } return(Tuple.Create(result, jobClassObject, writeLogAttr, writeExAttr, errorLevelAttr)); }
/// <summary> /// 使用 Thread 產生一個新的 Schedule Job 執行個體. /// <param name="job">Schedule Job 排程相關資料</param> /// </summary> public void StartNewScheduleJob(JobData job) { #region NLog 相關物件 Logger logger = NLogHelper.GetFileLogConfig(LogLevel.Info); Logger loggerErr = NLogHelper.GetErrorFileLogConfig(LogLevel.Error); #endregion LogAttributeData <WriteLogAttribute> writeLogAttr = null; //建立 傳遞 WriteLogAttribute 的容器 LogAttributeData <WriteExceptionLogAttribute> writeExAttr = null; //建立 傳遞 WriteExceptionLogAttribute 的容器 LogAttributeData <ErrorLevelAttribute> errorLevelAttr = null; //建立 傳遞 ErrorLevelAttribute 的容器 try { var resultValue = LoadAssemblyHelper.GetExecuteJobMethod(job); _jobMethod = resultValue.Item1; // Job DLL Instance of Method. _targetJobInstance = resultValue.Item2; // Job DLL Instance. writeLogAttr = resultValue.Item3; writeExAttr = resultValue.Item4; errorLevelAttr = resultValue.Item5; } catch (JobDLLNotFoundException dex) { NLogHelper.LoggerExWriter(loggerErr, job.JobName, job.DLLName, string.Format("[{0}]", job.JobName), dex.Message); } catch (JobMethodNotFoundException mex) { NLogHelper.LoggerExWriter(loggerErr, job.JobName, job.DLLName, string.Format("[{0}]", job.JobName), mex.Message); } catch (IActionNotImplementException aex) { NLogHelper.LoggerExWriter(loggerErr, job.JobName, job.DLLName, string.Format("[{0}]", job.JobName), aex.Message); } catch (Exception ex) { NLogHelper.LoggerExWriter(loggerErr, job.JobName, job.DLLName, string.Format("[{0}]", job.JobName), ex.Message); } try { //設定此工作目前執中. RunningTable.SetJobStatus(job.JobId, JobStatus.Running); //重設 Thread 事件狀態. _EventStopThread.Reset(); //m_EventThreadStopped.Reset(); //產生一個新的 WorkThreadFunction 的 Thread 執行個體. ThreadPool.QueueUserWorkItem((state) => { //紀錄 LOG 工作開始 logger = NLogHelper.GetFileLogConfig(LogLevel.Info); NLogHelper.LoggerWriter(logger, job.JobName, job.DLLName, string.Format("[{0}]", job.JobName), "[JobStart]", "[JobStart]"); //工作開始 Thread_Exec_Reault result = WorkerThreadFunction(_jobMethod, _targetJobInstance, job, false, writeLogAttr, writeExAttr, errorLevelAttr, logger, loggerErr); //如果前一個 WorkerThreadFunction 的執行是失敗的,就進行 ReTry 機制 if (result == Thread_Exec_Reault.FAIL) { int ReTrySec = 5000; //若未設定 config,則預設每 5 秒鐘進行 ReTry 機制 if (ConfigurationManager.AppSettings["ReTrySec"] != null) { try { ReTrySec = int.Parse(ConfigurationManager.AppSettings["ReTrySec"]) * 1000; } catch { loggerErr = NLogHelper.GetErrorFileLogConfig(LogLevel.Error); NLogHelper.LoggerExWriter(loggerErr, job.JobName, job.DLLName, "[ScheduleJob]", "app.config in the ReTrySec parameter setting is incorrect!"); } } if (job.ReTry > 0) { //Thread t = new Thread((state) => { WorkerThreadFunctionReTry(iReTry, _jobMethod, _targetJobInstance, job); }); //t.Start(); // 進行 ReTry 機制. // *** 變數說明 *** // iReTry:要進行 ReTry 的次數 // ExecCount:ReRey 的次數. // ReTrySec:ReRey 的秒數. int iReTry = job.ReTry; Task.Factory.StartNew(() => { int ExecCount = 1; do { Logger loggerRecord = NLogHelper.GetFileLogConfig(LogLevel.Info); NLogHelper.LoggerWriter(loggerRecord, job.JobName, job.DLLName, string.Format("[{0}]", job.JobName), "[ReTry]", string.Format("Execute repeat steps {0}.....", ExecCount)); Thread_Exec_Reault ReTryResult = WorkerThreadFunction(_jobMethod, _targetJobInstance, job, true, writeLogAttr, writeExAttr, errorLevelAttr, logger, loggerErr); if (ReTryResult == Thread_Exec_Reault.SUCCESS) { break; //若執行成功,隨即跳出迴圈 與 Currenthread. } Thread.Sleep(ReTrySec); iReTry--; ExecCount++; } while (iReTry > 0); } ); } } }, job); //設定最後執行時間. RunningJobs.SetLastExecuteDateTime(job); } catch (Exception ex) { //logger = NLogHelper.GetErrorFileLogConfig(LogLevel.Error); NLogHelper.LoggerExWriter(loggerErr, job.JobName, job.DLLName, string.Format("[{0}]", job.JobName), ex.Message); } finally { //等待執行序收到結束信號 _EventStopThread.WaitOne(); //設定服務為閒置狀態 RunningTable.SetJobStatus(job.JobId, JobStatus.Idle); } }
//protected static Logger LOG = NLog.LogManager.GetCurrentClassLogger(); /// <summary> /// 主要執行的 Method 入口 /// <param name="job">目前執行的 Job 相關資料.</param> /// <param name="writeLogAttr">WriteLog 的設定相關資料</param> /// <param name="writeExAttr">WriteExLog 的設定相關資料</param> /// </summary> public virtual void DoJob( JobData job, LogAttributeData <WriteLogAttribute> writeLogAttr, LogAttributeData <WriteExceptionLogAttribute> writeExAttr, LogAttributeData <ErrorLevelAttribute> errorLevelAttr, Logger loggerInner, Logger loggerInnerErr) { #region NLog 相關物件 //Logger loggerInner = NLogHelper.GetFileLogConfig(LogLevel.Info); //Logger loggerInnerErr = NLogHelper.GetErrorFileLogConfig(LogLevel.Error); #endregion WriteLogAttribute LogAttr = null; WriteExceptionLogAttribute LogExAttr = null; ErrorLevelAttribute ErrLevelAttr = null; System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); sw.Reset(); //碼表歸零 sw.Start(); //碼表開始計時 //loggerInner.Info("[DoJob] Start"); try { LogAttr = writeLogAttr.GetBeforePrepareAttr(); //取得當前的 BeforPrepare 方法是否有設定 WriteLogAttribute. if (LogAttr != null) { WriteInfo(LogAttr, job, "[BeforePrepare] Start..."); } BeforePrepare(); if (LogAttr != null) { loggerInner.Info(string.Format("[{0}].{1}.{2} Completed... total {3}", job.JobName, "[DotJob]", "[BeforePrepare]", sw.Elapsed.TotalMilliseconds.ToString())); } } catch (Exception ex) { ErrLevelAttr = errorLevelAttr.GetBeforePrepareAttr(); //取得當前的 BeforPrepare 方法是否有設定 ErrorLevelAttribute. LogExAttr = writeExAttr.GetBeforePrepareAttr(); //取得當前的 BeforPrepare 方法是否有設定 WriteExceptionLogAttribute. if (LogExAttr != null) { WriteExLog(LogExAttr, job, "[BeforePrepare]", ex, ErrLevelAttr); } throw ex; } try { LogAttr = writeLogAttr.GetPrepareDataMethodAttr(); if (LogAttr != null) { WriteInfo(LogAttr, job, "[PrepareData] Start..."); } PrepareData(); if (LogAttr != null) { loggerInner.Info(string.Format("[{0}].{1}.{2} Completed... total {3}", job.JobName, "[DotJob]", "[PrepareData]", sw.Elapsed.TotalMilliseconds.ToString())); } } catch (Exception ex) { ErrLevelAttr = errorLevelAttr.GetPrepareDataMethodAttr(); //取得當前的 PrepareData 方法是否有設定 ErrorLevelAttribute. LogExAttr = writeExAttr.GetPrepareDataMethodAttr(); //取得當前的 PrepareData 方法是否有設定 WriteExceptionLogAttribute. if (LogExAttr != null) { WriteExLog(LogExAttr, job, "[PrepareData]", ex, ErrLevelAttr); } throw ex; } try { LogAttr = writeLogAttr.GetProcessDataMethodAttr(); if (LogAttr != null) { WriteInfo(LogAttr, job, "[ProcessData] Start..."); } ProcessData(); if (LogAttr != null) { loggerInner.Info(string.Format("[{0}].{1}.{2} Completed... total {3}", job.JobName, "[DotJob]", "[ProcessData]", sw.Elapsed.TotalMilliseconds.ToString())); } } catch (Exception ex) { ErrLevelAttr = errorLevelAttr.GetProcessDataMethodAttr(); //取得當前的 ProcessData 方法是否有設定 ErrorLevelAttribute. LogExAttr = writeExAttr.GetProcessDataMethodAttr(); //取得當前的 ProcessData 方法是否有設定 WriteExceptionLogAttribute. if (LogExAttr != null) { WriteExLog(LogExAttr, job, "[ProcessData]", ex, ErrLevelAttr); } throw ex; } try { LogAttr = writeLogAttr.GetAfterProcessMethodAttr(); if (LogAttr != null) { WriteInfo(LogAttr, job, "[AfterProcess] Start..."); } AfterProcess(); if (LogAttr != null) { loggerInner.Info(string.Format("[{0}].{1}.{2} End... total {3}", job.JobName, "[DotJob]", "[AfterProcess]", sw.Elapsed.TotalMilliseconds.ToString())); } } catch (Exception ex) { ErrLevelAttr = errorLevelAttr.GetAfterProcessMethodAttr(); //取得當前的 AfterProcess 方法是否有設定 ErrorLevelAttribute. LogExAttr = writeExAttr.GetAfterProcessMethodAttr(); //取得當前的 AfterProcess 方法是否有設定 WriteExceptionLogAttribute. if (LogExAttr != null) { WriteExLog(LogExAttr, job, "[AfterProcess]", ex, ErrLevelAttr); } throw ex; } sw.Stop();//碼錶停止 }