WCF项目的架构设计
本文将介绍以WCF开发项目为主的架构设计,主要从类库的分类和代码的结构。
下面将以一个WCF实例做具体的介绍。此项目底层是一个Windows Service,WCF服务Hosted在此Windows Service上,WCF向外暴露REST接口供第三方调用,仅为单向通讯。
项目的目录结构如下图:
项目介绍:
***CentralService:
Windows service 项目,它是一个windows service的壳,同时定义关于此服务的权限、介绍、名称等内容,里面会调用OnStart和OnStop事件,分别关联Windows服务的启动和停止事件。具体的工作在下面的***StartEngine项目中,之所以分离开有以下两方面原因:
1.为方便调试,除在Windows服务中可以启动整个程序,一般会新建一个Console程序启动整个程序以便调试,所以启动程序最好是一个操作单元(unit,即***StartEngine项目)
2.OnStart和OnStop默认有一个超时时间(timeout),所以在启动服务时为了避免不超时一般采用另起一个新线程启动整个程序,单独做一个操作单元会很方便新线程调用
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Diagnostics; using System.Linq; using System.ServiceProcess; using System.Text; using System.Threading; using ***.***.Remote; using log4net; namespace ***CentralService { public partial class ***CentralService : ServiceBase { public ***CentralService() { InitializeComponent(); if(!System.Diagnostics.EventLog.SourceExists("***CentralServiceSource")) { System.Diagnostics.EventLog.CreateEventSource("***CentralServiceSource", "***CentralServiceLog"); } eventLog1.Source = "***CentralServiceSource"; eventLog1.Log = "***CentralServiceLog"; } private ILog _log = LogManager.GetLogger(typeof (***CentralService)); protected override void OnStart(string[] args) { eventLog1.WriteEntry("In OnStart ***CentralService."); _log.Info("In OnStart ***CentralService."); Thread thread = new Thread(delegate() { CentralServiceEngine.StartService(); }); thread.Start(); } protected override void OnStop() { eventLog1.WriteEntry("In onStop ***CentralService."); _log.Info("In onStop ***CentralService."); Thread thread = new Thread(delegate() { CentralServiceEngine.StopService(); }); thread.Start(); } } }
***StartEngine:
整个项目的启动单元
using System; using System.Collections.Generic; using System.Configuration; using System.Linq; using System.ServiceModel; using System.ServiceModel.Activation; using System.ServiceModel.Description; using System.ServiceModel.Web; using System.Text; using log4net; namespace ***。***.Remote { public static class CentralServiceEngine { private static WebServiceHost host_ParameterValidateService; private static WebServiceHost host_DomainOperateService; private static WebServiceHost host_RemoteInstallService; //private static Logger _logger = LogManager.CreateLogger(); private static ILog _log = LogManager.GetLogger(typeof (CentralServiceEngine)); public static void StartService() { _log.Info("In OnStart ***CentralService."); try { int port = 8889; if (!int.TryParse(CommonStaticValues.RestServicePort, out port)) { throw new Exception( "The configured RestServicePort value must be int type, please try http://localhost:8889 to check if the service started."); } string restServiceURL = "http://localhost:" + port; host_ParameterValidateService = new WebServiceHost(typeof (ParameterValidateService), new Uri(restServiceURL + "/validate")); ServiceEndpoint ep = host_ParameterValidateService.AddServiceEndpoint( typeof (IParameterValidateService), new WebHttpBinding(), ""); host_DomainOperateService = new WebServiceHost(typeof (DomainOperateService), new Uri(restServiceURL + "/domain")); ServiceEndpoint ep1 = host_DomainOperateService.AddServiceEndpoint(typeof (IDomainOperateService), new WebHttpBinding(), ""); host_RemoteInstallService = new WebServiceHost(typeof (RemoteInstallService), new Uri(restServiceURL + "/remoteinstall")); ServiceEndpoint ep2 = host_RemoteInstallService.AddServiceEndpoint(typeof (IRemoteInstallService), new WebHttpBinding(), ""); ServiceDebugBehavior stp = host_ParameterValidateService.Description.Behaviors.Find<ServiceDebugBehavior>(); stp.HttpHelpPageEnabled = false; ServiceDebugBehavior stp1 = host_DomainOperateService.Description.Behaviors.Find<ServiceDebugBehavior>(); stp1.HttpHelpPageEnabled = false; ServiceDebugBehavior stp2 = host_RemoteInstallService.Description.Behaviors.Find<ServiceDebugBehavior>(); stp2.HttpHelpPageEnabled = false; host_ParameterValidateService.Open(); host_DomainOperateService.Open(); host_RemoteInstallService.Open(); _log.Info("WCF Rest Service is up and running"); } catch (Exception ex) { _log.Error("***CentralService occur exception.", ex); } Console.Read(); } public static void StopService() { if (host_ParameterValidateService != null) { host_ParameterValidateService.Close(); host_DomainOperateService.Close(); host_RemoteInstallService.Close(); //_logger.Log("Validate Service is down and closed"); _log.Info("All Services is down and closed"); for (int i = 0; i < CommonStaticValues.All***BackgroundThreads.Length; i++) { var th = CommonStaticValues.All***BackgroundThreads[i]; if (th != null) { th.Abort(); th = null; } } } //_logger.Log("In onStop ***CentralService."); _log.Info("In onStop ***CentralService."); } } }
***WCFRestContract:
WCF服务的接口项目,包含如下内容以方便其他项目调用本项目的主要功能是:
1.定义WCF服务的接口
2.整个程序中用到的公共实体(Entity)
3.整个程序中用到的公共方法
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.ServiceModel; using System.ServiceModel.Web; using System.Text; using ***.***.Remote; namespace ***.***.Remote { [DataContractFormat] [ServiceContract] public interface IRemoteInstallService { //this is an example: http://localhost:8899/remoteinstall/task/6e4e5792-f654-4b0d-a97a-c84cb0d04e3b [WebGet(UriTemplate = "Task/{id}", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)] string GetInstallationState(string id); //this is a test url [WebGet(UriTemplate = "Task/Test", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)] string TestRemoteInstallationService(); //this is an example: http://localhost:8899/remoteinstall/Invoke?targetmachine=10.175.28.126&installerlocation="C:\Work\***exes\TroubleShooting\MA-QC48471\Clients\MachineAccountingSetup.exe"&command="C:\Work\IPSexes\TroubleShooting\MA-QC48471\Clients\MachineAccountingSetup.exe" /s /instance=-1 /v" /qn " /v"/L*v \"c:\IPS_RD\MA-Client-Installer-9b66ba27-bcb5-4909-aca8-2925a5b10e94.log\""&installertype=0 //the url must be url encode, so the true url is http://localhost:8899/remoteinstall/Invoke?targetmachine=10.175.28.126&installerlocation=%22C%3A%5CWork%5CIPSexes%5CTroubleShooting%5CMA-QC48471%5CClients%5CMachineAccountingSetup.exe%22&command=%22C%3A%5CWork%5CIPSexes%5CTroubleShooting%5CMA-QC48471%5CClients%5CMachineAccountingSetup.exe%22++%2Fs+%2Finstance%3D-1+%2Fv%5C%22+%2Fqn+%22+%2Fv%22%2FL*v+%22c%3A%5C***_RD%5CMA-Client-Installer-9b66ba27-bcb5-4909-aca8-2925a5b10e94.log%22%0D%0A&installertype=0 [WebGet(UriTemplate = "Invoke?targetmachine={targetIPorMachineName}&installerlocation={installerLocation}&command={commandLine}&installertype={installerType}" , RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)] string ExecuteInstallationForTestOnHttpGetMethod(string targetIPorMachineName, string installerLocation, string commandLine, InstallerTypeEnum installerType); //below is an example for Json data //{ // "targetmachine": "10.175.28.126", // "installerlocation": "\"C:\\Work\\IPSexes\\TroubleShooting\\MA-QC48471\\Clients\\MachineAccountingSetup.exe\"", // "command": "\"C:\\Work\\IPSexes\\TroubleShooting\\MA-QC48471\\Clients\\MachineAccountingSetup.exe\" \/s \/instance=-1 \/v\" \/qn \" \/v\"\/L*v \\\"c:\\IPS_RD\\MA-Client-Installer-9b66ba27-bcb5-4909-aca8-2925a5b10e94.log\\\"\"", // "installertype": 0 //} [WebInvoke(Method = "POST", UriTemplate = "ExecuteInstallation", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)] string ExecuteInstallation(Stream stream); [WebGet(UriTemplate = "Task/ALL", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)] string GetAllTask(); [WebGet(UriTemplate = "Task/Running", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)] string GetAllRunningTask(); [WebGet(UriTemplate = "Task/Successed", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)] string GetAllSuccessedTask(); [WebGet(UriTemplate = "Task/Failed", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)] string GetAllFailedTask(); } }
***WcfRestService:
WCF服务的具体实现,真正服务的实现和处理都在这个项目中。
本项目的所有方法一般只是个壳,具体的业务实现需调用***CentralServiceCore,目的是保证整个项目的稳定。
为了项目的稳定(windows service或client程序),在这里的每个方法都要做try catch,捕获所有从底层服务或本方法抛出的异常。
一般底层服务不再需要try catch异常,因为会一直抛到最上层被这个项目给抓住,多线程调用的底层方法需自己捕获并处理异常。
主要功能如下:
1.catch住整个程序的所有异常
2.记录异常等日志信息
3.记录方法调用日志信息
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Web; using Newtonsoft.Json; using log4net; namespace ***.***.Remote { public class RemoteInstallService : IRemoteInstallService { private ILog _log = LogManager.GetLogger(typeof (RemoteInstallService)); public string GetInstallationState(string id) { _log.Info(string.Format("Entry Method GetInstallationState({0})",id)); ExecuteResult executeResult = new ExecuteResult(); try { executeResult = new RemoteInstall().GetInstallationState(id); } catch (Exception exception) { _log.Error(exception); executeResult.ExecuteTaskId = id; executeResult.State = ExecuteStatusEnum.Unknow; executeResult.Message = exception.Message; } string result = JsonConvert.SerializeObject(executeResult); _log.Info(string.Format("Entry then Out Method GetInstallationState({0}), Result:{1}", id,result)); return result; } public string ExecuteInstallationForTestOnHttpGetMethod(string targetIPorMachineName, string installerLocation, string commandLine, InstallerTypeEnum installerType) { _log.Info(string.Format("Entry Method ExecuteInstallationForTestOnHttpGetMethod({0},{1},{2},{3})", targetIPorMachineName, installerLocation, commandLine, installerType)); ExecuteResult executeResult = new ExecuteResult(); executeResult.ExecuteTaskId = Guid.NewGuid().ToString(); try { executeResult = new RemoteInstall().ExecuteInstallationSync(targetIPorMachineName, installerLocation, commandLine, executeResult.ExecuteTaskId, installerType); } catch (Exception exception) { _log.Error(exception); executeResult.State = ExecuteStatusEnum.Unknow; executeResult.Message = exception.Message; } string result = JsonConvert.SerializeObject(executeResult); _log.Info(string.Format("Entry then Out Method ExecuteInstallationForTestOnHttpGetMethod({0},{1},{2},{3}), Result:{4}", targetIPorMachineName, installerLocation, commandLine, installerType, result)); return result; } public string ExecuteInstallation(System.IO.Stream stream) { _log.Info(string.Format("Entry Method ExecuteInstallation(),{0}", "stream type, see follow data")); ExecuteResult executeResult = new ExecuteResult(); executeResult.ExecuteTaskId = Guid.NewGuid().ToString(); try { StreamReader reader = new StreamReader(stream); string data = reader.ReadToEnd(); _log.Info(string.Format("Entry Method ExecuteInstallation({0})", data)); RemoteInstallationEntity remoteInstallationEntity; try { remoteInstallationEntity = JsonConvert.DeserializeObject<RemoteInstallationEntity>(data); } catch (Exception ex) { _log.Error(ex); executeResult.State = ExecuteStatusEnum.Failed; executeResult.Message = string.Format("The Post data -{0}- is not valide. ERROR: {1}", data, ex); return JsonConvert.SerializeObject(executeResult); } executeResult = new RemoteInstall().ExecuteInstallationASync(remoteInstallationEntity.TargetMachine, remoteInstallationEntity.InstallerLocation, remoteInstallationEntity.Command, executeResult.ExecuteTaskId, (InstallerTypeEnum) remoteInstallationEntity.InstallerType); } catch (Exception exception) { _log.Error(exception); executeResult.State = ExecuteStatusEnum.Unknow; executeResult.Message = exception.Message; } string result = JsonConvert.SerializeObject(executeResult); _log.Info(string.Format("Entry then Out Method ExecuteInstallation(), Result:{0}", result)); return result; } public string GetAllTask() { List<ExecuteResult> list=new List<ExecuteResult>(); try { list = CommonStaticValues.AllTaskResultList; } catch (Exception ex) { _log.Error(ex); } return JsonConvert.SerializeObject(list); } public string GetAllRunningTask() { List<ExecuteResult> list = new List<ExecuteResult>(); try { list = CommonStaticValues.AllTaskResultList.FindAll(x=>x.State==ExecuteStatusEnum.Running); } catch (Exception ex) { _log.Error(ex); } return JsonConvert.SerializeObject(list); } public string GetAllSuccessedTask() { List<ExecuteResult> list = new List<ExecuteResult>(); try { list = CommonStaticValues.AllTaskResultList.FindAll(x => x.State == ExecuteStatusEnum.Successed); } catch (Exception ex) { _log.Error(ex); } return JsonConvert.SerializeObject(list); } public string GetAllFailedTask() { List<ExecuteResult> list = new List<ExecuteResult>(); try { list = CommonStaticValues.AllTaskResultList.FindAll(x => x.State == ExecuteStatusEnum.Failed); } catch (Exception ex) { _log.Error(ex); } return JsonConvert.SerializeObject(list); } public string TestRemoteInstallationService() { _log.Info(string.Format("Entry Method TestRemoteInstallationService()")); return "Test Successed!"; } } }
***CentralServiceCore:
这个项目是具体的业务实现,***WcfRestService中的服务一般调用***CentralServiceCore里的方法,以方便处理异常。本项目的内容一般不需捕获异常,出了问题直接抛给了***WcfRestService,复杂的逻辑一般在这里处理。多线程的调用需要在这里做异常处理。
using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; using Castor.UniversalInstaller; using System.Net; using Castor.UniversalInstaller.Util; using tinyClient; using log4net; using System.Threading; namespace ***.***.Remote { public class RemoteInstall { private ILog _log = LogManager.GetLogger(typeof (RemoteInstall)); public ExecuteResult GetInstallationState(string taskid) { ExecuteResult executeResult= CommonStaticValues.AllTaskResultList.Find(x => x.ExecuteTaskId == taskid); if(executeResult==null) { executeResult=new ExecuteResult(); executeResult.ExecuteTaskId = taskid; executeResult.State=ExecuteStatusEnum.NotFound; executeResult.Message = string.Format("Not Found this Taskid:{0} in *** Service", taskid); } return executeResult; } public ExecuteResult ExecuteInstallationSync(string targetIPorMachineName, string installerLocation, string commandLine, string taskId, InstallerTypeEnum installerType) { ExecuteResult executeResult = new ExecuteResult(); executeResult.ExecuteTaskId = taskId; executeResult.State=ExecuteStatusEnum.Running; executeResult.BeginTime = DateTime.Now; CommonStaticValues.AllTaskResultList.Add(executeResult); //Step 1 TODO: Run pre or post script string machineName, ip, errMessage; Helper.TryParseComputerNameIP(targetIPorMachineName, out machineName, out ip, out errMessage); int exitCode = 0; if (machineName.ToLower().Trim() == GetCurrentMachineName().ToLower().Trim())//Local Installation { //executeResult = InstalledToLocalMachine(targetIPorMachineName, installerLocation, commandLine, taskId, executeResult, machineName); ProcessStartInfo processStartInfo = new ProcessStartInfo(); if (commandLine.StartsWith("msiexec.exe", StringComparison.CurrentCultureIgnoreCase)) { processStartInfo.FileName = "msiexec.exe"; processStartInfo.Arguments = commandLine.Substring(processStartInfo.FileName.Length + 1); } else // fix local install error { processStartInfo.FileName = installerLocation; processStartInfo.Arguments = Regex.Replace(commandLine, string.Format("[\\\"]?{0}[ \\\"]?", installerLocation.Trim().Replace("\\", "\\\\")), "", RegexOptions.IgnoreCase); } processStartInfo.UseShellExecute = false; processStartInfo.CreateNoWindow = true; var process = Process.Start(processStartInfo); process.WaitForExit(CommonStaticValues.InstallationTimeOutMinute); exitCode = process.ExitCode; } else//Remote Installation. Tiny command please refer to "psexec", please search "psexec" via google for more help. { string remoteLocationToDelete=string.Empty; string command_c = string.Empty;//-c: Copy the specified program to the remote system for execution. If you omit this option the application must be in the system path on the remote system. if (!commandLine.StartsWith("msiexec.exe", StringComparison.CurrentCultureIgnoreCase)) { command_c = " -c"; } else if (!installerLocation.ToLower().Contains("sbx") || installerType != InstallerTypeEnum.DatabaseInstall) // for sbX DB products, it contains other service installers, not copy to remote machines { remoteLocationToDelete = Helper.CopyFileToComputer(installerLocation, targetIPorMachineName); if (!string.IsNullOrEmpty(remoteLocationToDelete)) { commandLine = commandLine.Replace(installerLocation, remoteLocationToDelete); } } string remoteCommandLine=string.Empty; remoteCommandLine += command_c; remoteCommandLine += string.IsNullOrEmpty(CommonStaticValues.DomainAdmin) ? "" : string.Format(" -u {0} -p {1}", CommonStaticValues.DomainAdmin, CommonStaticValues.DomainPassword); remoteCommandLine = string.Format(@"\\{0} execute {1} {2}", targetIPorMachineName, remoteCommandLine, commandLine); var tinyHelper = new TinyHelper(); var args = tinyHelper.SplitCommandLineArgument(remoteCommandLine); TinyParameter tp = tinyHelper.CommandLineParser(args); TinyResponse rsp = new TinyResponse(); try { tinyHelper.StartRemoteService(tp); rsp = tinyHelper.ExecuteCommand(tp); } finally { try { // Ignore error in stopping service error: tinyHelper.StopService(tp); } catch (Exception ex) { _log.Error(string.Format( "Failed on install targetIPorMachineName: {0}, installerLocation: {1}, commandLine: {2}, taskId: {3}, ErrorMessage: {4}", targetIPorMachineName, installerLocation, commandLine, taskId, ex.Message)); } } exitCode = rsp.CmdRetCode == 0 ? rsp.TinyRetCode : rsp.CmdRetCode; // delete the temperary installer if it exists try { if (File.Exists(remoteLocationToDelete)) File.Delete(remoteLocationToDelete); } catch { } } if (ExitCodeHelper.IsSucess(exitCode)) { executeResult.State = ExecuteStatusEnum.Successed; } else { executeResult.State = ExecuteStatusEnum.Failed; executeResult.Message = string.Format( "Failed on install targetIPorMachineName: {0}, installerLocation: {1}, commandLine: {2}, taskId: {3}, Exit code: {4}", targetIPorMachineName, installerLocation, commandLine, taskId, ExitCodeHelper.GetExitCodeFormatedMessage(exitCode)); _log.Error(executeResult.Message); } return executeResult; } public ExecuteResult ExecuteInstallationASync(string targetIPorMachineName, string installerLocation, string commandLine, string taskId, InstallerTypeEnum installerType) { ExecuteResult executeResult = new ExecuteResult(); int currentAvaliableThreadIndex = CommonStaticValues.GetAvaliableThreadIndex(); if (currentAvaliableThreadIndex == int.MaxValue) { executeResult.State = ExecuteStatusEnum.Failed; executeResult.Message = string.Format( "There are more than {0} tasks being work now, please wait 10 minutes to retry your task, or change the MaxInvokeInstallationThreadCount in config file, or contact the administrator for help.", CommonStaticValues.AllIPSBackgroundThreads.Length); return executeResult; } executeResult.ExcuteThreadIndex = currentAvaliableThreadIndex; executeResult.ExecuteTaskId = taskId; executeResult.State = ExecuteStatusEnum.Running; executeResult.BeginTime = DateTime.Now; CommonStaticValues.AllTaskResultList.Add(executeResult); //Step 1 TODO: Run pre or post script string machineName, ip, errMessage; Helper.TryParseComputerNameIP(targetIPorMachineName, out machineName, out ip, out errMessage); if (machineName.ToLower().Trim() == GetCurrentMachineName().ToLower().Trim()) //Local Installation { CommonStaticValues.AllIPSBackgroundThreads[currentAvaliableThreadIndex] = new Thread(delegate() { try { ExecuteInstallationOnLocalMachine(executeResult, machineName, targetIPorMachineName, installerLocation, commandLine); } catch (Exception exception) { executeResult.State = ExecuteStatusEnum.Failed; executeResult.Message = string.Format( "Failed on install targetIPorMachineName: {0}, installerLocation: {1}, commandLine: {2}, taskId: {3}, ERROR MESSAGE: {4}", targetIPorMachineName, installerLocation, commandLine, executeResult.ExecuteTaskId, exception.Message); _log.Error( "ExecuteInstallationASync-ExecuteInstallationOnLocalMachine ERROR:", exception); } executeResult.EndTime = DateTime.Now; }); CommonStaticValues.AllIPSBackgroundThreads[currentAvaliableThreadIndex].Start(); } else //Remote Installation. Tiny command please refer to "psexec", please search "psexec" via google for more help. { CommonStaticValues.AllIPSBackgroundThreads[currentAvaliableThreadIndex] = new Thread(delegate() { try { ExecuteInstallationOnRemoteMachine(executeResult, machineName, targetIPorMachineName, installerLocation, commandLine, installerType); } catch (Exception exception) { executeResult.State = ExecuteStatusEnum.Failed; executeResult.Message = string.Format( "Failed on install targetIPorMachineName: {0}, installerLocation: {1}, commandLine: {2}, taskId: {3}, ERROR MESSAGE: {4}", targetIPorMachineName, installerLocation, commandLine, executeResult.ExecuteTaskId, exception.Message); _log.Error("ExecuteInstallationASync-ExecuteInstallationOnRemoteMachine ERROR:", exception); } executeResult.EndTime = DateTime.Now; }); CommonStaticValues.AllIPSBackgroundThreads[currentAvaliableThreadIndex].Start(); } return executeResult; } private void ExecuteInstallationOnLocalMachine(ExecuteResult executeResult, string machineName, string deliveredIPorMachineName, string installerLocation, string commandLine) { ProcessStartInfo processStartInfo = new ProcessStartInfo(); if (commandLine.StartsWith("msiexec.exe", StringComparison.CurrentCultureIgnoreCase)) { processStartInfo.FileName = "msiexec.exe"; processStartInfo.Arguments = commandLine.Substring(processStartInfo.FileName.Length + 1); } else // fix local install error { processStartInfo.FileName = installerLocation; processStartInfo.Arguments = Regex.Replace(commandLine, string.Format("[\\\"]?{0}[ \\\"]?", installerLocation.Trim().Replace("\\", "\\\\")), "", RegexOptions.IgnoreCase); } processStartInfo.UseShellExecute = false; processStartInfo.CreateNoWindow = true; _log.Debug(string.Format("Run Method ExecuteInstallationOnLocalMachine, processStartInfo.FileName:{0}, processStartInfo.Arguments:{1}", processStartInfo.FileName, processStartInfo.Arguments)); var process = Process.Start(processStartInfo); process.WaitForExit(CommonStaticValues.InstallationTimeOutMinute); int exitCode = process.ExitCode; if (ExitCodeHelper.IsSucess(exitCode)) { executeResult.State = ExecuteStatusEnum.Successed; } else { executeResult.State = ExecuteStatusEnum.Failed; executeResult.Message = string.Format( "Failed on install targetIPorMachineName: {0}, installerLocation: {1}, commandLine: {2}, taskId: {3}, Exit code: {4}", deliveredIPorMachineName, installerLocation, commandLine, executeResult.ExecuteTaskId, ExitCodeHelper.GetExitCodeFormatedMessage(exitCode)); _log.Error(executeResult.Message); } } private void ExecuteInstallationOnRemoteMachine(ExecuteResult executeResult, string machineName, string deliveredIPorMachineName, string installerLocation, string commandLine, InstallerTypeEnum installerType) { string remoteLocationToDelete = string.Empty; string command_c = string.Empty;//-c: Copy the specified program to the remote system for execution. If you omit this option the application must be in the system path on the remote system. if (!commandLine.StartsWith("msiexec.exe", StringComparison.CurrentCultureIgnoreCase)) { command_c = " -c"; } else if (!installerLocation.ToLower().Contains("sbx") || installerType != InstallerTypeEnum.DatabaseInstall) // for sbX DB products, it contains other service installers, not copy to remote machines { remoteLocationToDelete = Helper.CopyFileToComputer(installerLocation, machineName); if (!string.IsNullOrEmpty(remoteLocationToDelete)) { commandLine = commandLine.Replace(installerLocation, remoteLocationToDelete); } } string remoteCommandLine = string.Empty; remoteCommandLine += command_c; remoteCommandLine += string.IsNullOrEmpty(CommonStaticValues.DomainAdmin) ? "" : string.Format(" -u {0} -p {1}", CommonStaticValues.DomainAdmin, CommonStaticValues.DomainPassword); remoteCommandLine = string.Format(@"\\{0} execute {1} {2}", machineName, remoteCommandLine, commandLine); var tinyHelper = new TinyHelper(); var args = tinyHelper.SplitCommandLineArgument(remoteCommandLine); TinyParameter tp = tinyHelper.CommandLineParser(args); TinyResponse rsp = new TinyResponse(); try { tinyHelper.StartRemoteService(tp); rsp = tinyHelper.ExecuteCommand(tp); } finally { try { // Ignore error in stopping service error: tinyHelper.StopService(tp); } catch (Exception ex) { _log.Error(string.Format( "Failed on install targetIPorMachineName: {0}, installerLocation: {1}, commandLine: {2}, taskId: {3}, ErrorMessage: {4}", deliveredIPorMachineName, installerLocation, commandLine, executeResult.ExecuteTaskId, ex.Message)); } } int exitCode = rsp.CmdRetCode == 0 ? rsp.TinyRetCode : rsp.CmdRetCode; // delete the temperary installer if it exists try { if (File.Exists(remoteLocationToDelete)) File.Delete(remoteLocationToDelete); } catch { } if (ExitCodeHelper.IsSucess(exitCode)) { executeResult.State = ExecuteStatusEnum.Successed; } else { executeResult.State = ExecuteStatusEnum.Failed; executeResult.Message = string.Format( "Failed on install targetIPorMachineName: {0}, installerLocation: {1}, commandLine: {2}, taskId: {3}, Exit code: {4}", deliveredIPorMachineName, installerLocation, commandLine, executeResult.ExecuteTaskId, ExitCodeHelper.GetExitCodeFormatedMessage(exitCode)); _log.Error(executeResult.Message); } } private string GetCurrentMachineName() { string currentName = Dns.GetHostName(); return currentName.Contains(".") ? currentName.Substring(0, currentName.IndexOf(".")) : currentName; } } }