// // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. // using System; using System.Collections; using System.Collections.Generic; using System.Data; using System.Globalization; using Microsoft.SqlServer.Management.Common; using Microsoft.SqlServer.Management.Sdk.Sfc; using Microsoft.SqlServer.Management.Smo; using Microsoft.SqlServer.Management.Smo.Agent; using Microsoft.SqlTools.ServiceLayer.Agent.Contracts; using Microsoft.SqlTools.ServiceLayer.Management; using SMO = Microsoft.SqlServer.Management.Smo; namespace Microsoft.SqlTools.ServiceLayer.Agent { [Flags] internal enum UserRoles { NotSet = -1, None = 0, AgentUser = 1, AgentReader = 2, AgentOperator = 4, SysAdmin = 8 }; /// /// Summary description for JobPropertiesPrototype. /// internal class JobData { #region constants /// /// Mode the dialog has been launched /// internal enum ActionMode { Create, Edit, Unknown }; /// /// If the job has been pushed from an MSX then it's category ID will always be 1 /// public readonly int JobFromMsxId = 1; #endregion #region fields /* * fields that are always populated */ /// /// Urn of the Job object we were launched against. This will be null if we are creating a new job. /// private Urn urn = null; /// /// Datacontainer that represents everything we need to know about the server. /// private CDataContainer context; /// /// Mode we are working in. /// private ActionMode mode; /* * fields that map to the SMO Job object */ /// /// original name of the job /// private string originalName = null; /// /// Job GUID stored as string /// private string jobIdString = null; #region general page bool generalInfoLoaded = false; /// /// current name of the job /// private string currentName = null; /// /// job owner /// private string owner = null; /// /// category for this job /// private LocalizableCategory category = null; /// /// job description /// private string description = null; /// /// is the job enabled /// private bool enabled; /// /// source server for this job /// private string source = null; /// /// originating server /// private string originatingServer = null; /// /// Date and time this job was created /// private DateTime created; /// /// when was the job last modified /// private DateTime lastModified; /// /// when was the job last executed /// private DateTime lastExecution; #endregion #region notifications bool notificationsLoaded = false; /// /// operator that will be emailed /// private string operatorToEmail = null; /// /// when the operator will be emailed /// private CompletionAction emailLevel; /// /// operator will be paged /// private string operatorToPage = null; /// /// when they will be paged /// private CompletionAction pageLevel; /// /// when will an entry be written to the event log /// private CompletionAction eventLogLevel; /// /// when will the job be deleted /// private CompletionAction deleteLevel; #endregion // msx / tsx information private bool msaInformationLoaded = false; private bool targetLocalServer = true; private bool originallyTargetLocalServer = true; private MsaJobTargetServer[] targetServers; // cached server information private UserRoles userRole = UserRoles.NotSet; private string trueLogin = null; private string[] owners = null; private LocalizableCategory[] smoCategories = null; private LocalizableCategory[] displayableCategories = null; private string[] operators = null; private Version version = null; // cached proxy objects private JobStepsData jobSteps = null; private JobSchedulesData jobSchedules = null; private JobAlertsData jobAlerts = null; // other information private string script = null; private string scriptName = null; private int startStepID = -1; #endregion #region public properties public Version Version { get { if (this.version == null) { LoadVersion(); } return this.version; } } public UserRoles UserRole { get { if (this.userRole == UserRoles.NotSet) { LoadUserRoles(); } return this.userRole; } } public CDataContainer OriginalContext { get { return this.context; } } public ActionMode Mode { get { return this.mode; } } public string Name { get { CheckAndLoadGeneralData(); return this.currentName; } set { CheckAndLoadGeneralData(); this.currentName = value.Trim(); } } public string Owner { get { CheckAndLoadOwner(); return this.owner; } set { this.owner = value; } } public string[] Owners { get { if (this.owners == null) { LoadLogins(); } return this.owners; } } public LocalizableCategory Category { get { CheckAndLoadCategory(); return this.category; } set { this.category = value; } } public LocalizableCategory[] Categories { get { CheckAndLoadDisplayableCategories(); return this.displayableCategories; } } private LocalizableCategory[] SmoCategories { get { CheckAndLoadSmoCategories(); return this.smoCategories; } } public string Description { get { CheckAndLoadGeneralData(); return this.description; } set { CheckAndLoadGeneralData(); this.description = value; } } public bool Enabled { get { CheckAndLoadGeneralData(); return this.enabled; } set { CheckAndLoadGeneralData(); this.enabled = value; } } public string Source { get { CheckAndLoadGeneralData(); return this.source; } set { throw new NotImplementedException(); } } public DateTime DateCreated { get { CheckAndLoadGeneralData(); return this.created; } } public DateTime DateLastModified { get { CheckAndLoadGeneralData(); return this.lastModified; } } public DateTime LastRunDate { get { CheckAndLoadGeneralData(); return this.lastExecution; } } public string OperatorToEmail { get { CheckAndLoadNotifications(); return this.operatorToEmail; } set { CheckAndLoadNotifications(); this.operatorToEmail = value; } } public CompletionAction EmailLevel { get { CheckAndLoadNotifications(); return this.emailLevel; } set { CheckAndLoadNotifications(); this.emailLevel = value; } } public string OperatorToPage { get { CheckAndLoadNotifications(); return this.operatorToPage; } set { CheckAndLoadNotifications(); this.operatorToPage = value; } } public CompletionAction PageLevel { get { CheckAndLoadNotifications(); return this.pageLevel; } set { CheckAndLoadNotifications(); this.pageLevel = value; } } public CompletionAction EventLogLevel { get { CheckAndLoadNotifications(); return this.eventLogLevel; } set { CheckAndLoadNotifications(); this.eventLogLevel = value; } } public CompletionAction DeleteLevel { get { CheckAndLoadNotifications(); return this.deleteLevel; } set { CheckAndLoadNotifications(); this.deleteLevel = value; } } public string[] Operators { get { if (this.operators == null) { LoadOperators(); } return this.operators; } } public JobStepsData JobSteps { get { CheckAndLoadJobSteps(); return this.jobSteps; } } public JobSchedulesData JobSchedules { get { CheckAndLoadJobSchedules(); return this.jobSchedules; } } public JobAlertsData JobAlerts { get { CheckAndLoadJobAlerts(); return this.jobAlerts; } } public bool IsMsx { get { return this.AvailableTargetServers.Length > 0; } } public MsaJobTargetServer[] AvailableTargetServers { get { if (this.targetServers == null) { LoadTargetServers(); } return this.targetServers; } } public bool IsLocalJob { get { return this.targetLocalServer; } } public bool IsRemotelyOriginated { get { return (this.category == null) ? false : this.Category.SmoCategory.ID == JobFromMsxId; } } public string OriginatingServer { get { return this.originatingServer; } } public bool IsUserAgentAdmin { get { return (UserRole & UserRoles.SysAdmin) > 0; } } public bool OriginallyTargetLocalServer { get { CheckAndLoadMsaInformation(); return this.originallyTargetLocalServer; } set { CheckAndLoadMsaInformation(); this.originallyTargetLocalServer = value; } } public bool TargetLocalServer { get { CheckAndLoadMsaInformation(); return this.targetLocalServer; } set { CheckAndLoadMsaInformation(); // If a change in the targetLocalServer was detected, then fire the OnCategoriesChanged // event so that the categories drop down list is properly populated. if (this.targetLocalServer != value) { this.targetLocalServer = value; this.displayableCategories = null; CheckAndLoadDisplayableCategories(); OnCategoriesChanged(); this.owners = null; OnOwnersChanged(); } } } /// If we're looking at an existing Job and this.Job is not /// null, then check if the job's category is local. We do /// this for the case where the job exists but has no targets, /// and therefore this.targetLocalServer is false even though /// we're in a local job category. public bool JobCategoryIsLocal { get { Job job = this.Job; if (job == null) { return false; } return (CategoryType.LocalJob == (SMO.Agent.CategoryType)(job.CategoryType)); } } public bool IsReadOnly { get { return IsRemotelyOriginated || (this.mode == ActionMode.Edit && !IsUserAgentAdmin && this.TrueLogin != this.Owner); } } public bool AllowEnableDisable { get { // Enable/disable for non-read only jobs should always be true // for remotely originate jobs it will always be false // if the user is an agent operator and they do not own the job then it will be true return IsReadOnly ? (!IsRemotelyOriginated && ((UserRole & UserRoles.AgentOperator) > 0)) : true; } } private void OnCategoriesChanged() { // Fire the categories changed event. if (this.CategoriesChanged != null) { this.CategoriesChanged(this, EventArgs.Empty); } } private void OnOwnersChanged() { // Fire the categories changed event. if (this.OwnersChanged != null) { this.OwnersChanged(this, EventArgs.Empty); } } #endregion #region events public event EventHandler CategoriesChanged; public event EventHandler OwnersChanged; #endregion #region construction public JobData(CDataContainer data, AgentJobInfo jobInfo = null, ConfigAction configAction = ConfigAction.Create) { this.context = data; // get the job information STParameters parameters = new STParameters(this.context.Document); parameters.GetParam("job", ref this.originalName); parameters.GetParam("jobid", ref this.jobIdString); parameters.GetParam("script", ref this.script); parameters.GetParam("scriptName", ref this.scriptName); // get the Urn string urn = string.Empty; parameters.GetParam("urn", ref urn); if (urn != null && urn.Length > 0) { this.urn = new Urn(urn); } bool isMsxJob = false; parameters.GetParam("msxjob", ref isMsxJob); //If this is an MSX, initially set TargetLocalServers to false; if (isMsxJob) { this.targetLocalServer = false; } // we are in properties mode. if (configAction == ConfigAction.Update) { this.mode = ActionMode.Edit; CheckAndLoadGeneralData(); } else if (this.script.Length > 0) { // we are creating a new job, but prepopulating // one step with the script passed to us this.mode = ActionMode.Create; this.Name = this.scriptName; SetDefaults(); this.jobSteps = new JobStepsData(context, script, this); } // creating a new job else { this.mode = ActionMode.Create; // set defaults that do not involve going to the server to retrieve SetDefaults(); } // load AgentJobInfo data if (jobInfo != null) { this.currentName = jobInfo.Name; this.Owner = jobInfo.Owner; this.Description = jobInfo.Description; this.Enabled = jobInfo.Enabled; this.startStepID = jobInfo.StartStepId; this.Category = ConvertStringToCategory(jobInfo.Category); } } #endregion #region properties - non public internal Job Job { get { Job job = null; // If JobID is passed in look up by jobID if (!string.IsNullOrEmpty(this.jobIdString)) { job = this.context.Server.JobServer.Jobs.ItemById(Guid.Parse(this.jobIdString)); // Job name might not be passed in context, let us fix job name and urn this.originalName = job.Name; this.urn = job.Urn; } else { // or use urn path to query job job = this.context.Server.GetSmoObject(this.urn) as Job; } return job; } } internal Urn Urn { get { return this.urn; } } internal string TrueLogin { get { if (this.trueLogin == null) { LoadTrueLogin(); } return trueLogin; } } #endregion #region data loading / initialization private void CheckAndLoadMsaInformation() { if (msaInformationLoaded || this.mode == ActionMode.Create) { return; } Job job = this.Job; msaInformationLoaded = true; DataTable table = job.EnumTargetServers(); string targetName; if (table.Rows.Count == 0) { originallyTargetLocalServer = this.TargetLocalServer = false; } // if the server is an msx then see if this job is targetted at local // servers else if (this.IsMsx) { for (int i = 0; i < table.Rows.Count; ++i) { targetName = table.Rows[i]["ServerName"].ToString().ToLowerInvariant(); for (int ii = 0; ii < this.AvailableTargetServers.Length; ++ii) { if (targetName == this.AvailableTargetServers[ii].Name.ToLowerInvariant()) { AvailableTargetServers[ii].IsJobAppliedToTarget = AvailableTargetServers[ii].WillJobBeAppliedToTarget = true; originallyTargetLocalServer = this.TargetLocalServer = false; } } } } } private void CheckAndLoadGeneralData() { if (this.generalInfoLoaded || this.mode == ActionMode.Create) { return; } Job job = this.Job; this.currentName = job.Name; this.description = job.Description; this.enabled = job.IsEnabled; // this.source = job.Source; this.created = job.DateCreated; this.lastModified = job.DateLastModified; this.lastExecution = job.LastRunDate; this.originatingServer = job.OriginatingServer; this.generalInfoLoaded = true; CheckAndLoadMsaInformation(); } private void CheckAndLoadOwner() { if (this.owner != null) { return; } if (this.Mode == ActionMode.Edit) { this.owner = this.Job.OwnerLoginName; } else { this.owner = this.context.ConnectionInfo.UserName.TrimEnd(); } } private void CheckAndLoadJobSteps() { if (this.jobSteps != null) { return; } this.jobSteps = new JobStepsData(context, this); } private void CheckAndLoadJobSchedules() { if (this.jobSchedules != null) { return; } // load schedules this.jobSchedules = new JobSchedulesData(context, this); } private void CheckAndLoadJobAlerts() { if (this.jobAlerts != null || (UserRole & UserRoles.SysAdmin) == 0) { return; } this.jobAlerts = new JobAlertsData(context, this); } // loads default values for a new job. private void SetDefaults() { this.description = string.Empty; this.enabled = true; this.source = null; // notifications this.operatorToEmail = string.Empty; this.emailLevel = CompletionAction.Never; this.operatorToPage = string.Empty; this.pageLevel = CompletionAction.Never; this.eventLogLevel = CompletionAction.Never; this.deleteLevel = CompletionAction.Never; this.generalInfoLoaded = true; this.notificationsLoaded = true; this.msaInformationLoaded = true; this.originatingServer = string.Empty; } private void CheckAndLoadNotifications() { if (this.notificationsLoaded || this.Mode == ActionMode.Create) { return; } Job job = this.Job; if (this.Version.Major >= 9 || ((UserRole & UserRoles.SysAdmin) > 0)) { // notifications this.operatorToEmail = job.OperatorToEmail; this.emailLevel = job.EmailLevel; this.operatorToPage = job.OperatorToPage; this.pageLevel = job.PageLevel; } else { this.operatorToEmail = String.Empty; this.emailLevel = CompletionAction.Never; this.operatorToPage = String.Empty; this.pageLevel = CompletionAction.Never; } this.eventLogLevel = job.EventLogLevel; this.deleteLevel = job.DeleteLevel; this.notificationsLoaded = true; } private void CheckAndLoadCategory() { if (this.category != null) { return; } if (this.Mode == ActionMode.Edit) { this.category = ConvertStringToCategory(this.Job.Category); } else { this.category = Categories[0]; } } /// /// Load all SMO categories. /// private void CheckAndLoadSmoCategories() { if (this.smoCategories != null) { return; } // force a preload of all of the category information. We cache this, // and if we are not careful SMO will end up executing a batch per category this.context.Server.SetDefaultInitFields(typeof(JobCategory), true); JobServer jobServer = this.context.Server.JobServer; List smoCategories = new List(); for (int i = 0; i < jobServer.JobCategories.Count; ++i) { smoCategories.Add(new LocalizableCategory(jobServer.JobCategories[i])); } this.smoCategories = smoCategories.ToArray(); } /// /// Load only those categories that will be displayed in the categories drop-down based on /// TSX and MSX job status. /// private void CheckAndLoadDisplayableCategories() { if (this.displayableCategories != null) { return; } this.displayableCategories = null; LocalizableCategory[] allCategories = this.SmoCategories; List displayableCategories = new List(); bool targetsLocalServer = this.targetLocalServer || this.JobCategoryIsLocal; // get all applicable categories for (int i = 0; i < allCategories.Length; ++i) { bool validCategoryForThisContext = false; CategoryType currentJobCategoryType = allCategories[i].SmoCategory.CategoryType; if (targetsLocalServer) { // Only local jobs are valid, regardless of whether they are originated remotely or locally // (Besides, it doesn't matter for TSX jobs as their category cannot be changed by TSX anyway) validCategoryForThisContext = currentJobCategoryType == CategoryType.LocalJob; } else { if (this.IsRemotelyOriginated) { if (!this.IsMsx) { validCategoryForThisContext = (currentJobCategoryType == CategoryType.LocalJob); } } else { if (this.IsMsx) { validCategoryForThisContext = (currentJobCategoryType == CategoryType.MultiServerJob); } } } ///See if this category can be added. if (validCategoryForThisContext) { displayableCategories.Add(allCategories[i]); } } this.displayableCategories = displayableCategories.ToArray(); } private void LoadLogins() { // figure out what rights the user has. SqlServer.Management.Smo.Server server = this.context.Server; // see if the user is a sysadmin. At the moment sysadmins can assign // job ownership to any user. Non sysadmins cannot. Operators can see jobs owned by anyone if ((this.UserRole & UserRoles.SysAdmin) > 0 || (this.UserRole & UserRoles.AgentOperator) > 0) { System.Collections.Specialized.StringCollection validLoginNames = new System.Collections.Specialized.StringCollection(); foreach (SMO.Login login in server.Logins) { if (SMO.LoginType.WindowsGroup != login.LoginType) { //For Msx jobs, only add logins that are members of the sysadmin role. if (!this.targetLocalServer) { if (login.IsMember("sysadmin")) { validLoginNames.Add(login.Name); } } else { //Otherwise, if this is NOT an Msx jobs, just add it. validLoginNames.Add(login.Name); } } } //validLoginNames will not include the current connection's trusted user therefore //add it to the owners string array. This will allow the value to be seen (and selected) in //the job properties drop down. //Only add the name if it doesn't already exist in the collection if (!validLoginNames.Contains(TrueLogin)) { validLoginNames.Add(TrueLogin); } this.owners = new string[validLoginNames.Count]; validLoginNames.CopyTo(this.owners, 0); } else { // the user is the only person allowed to own the job this.owners = new string[1] { server.ConnectionContext.TrueLogin }; } } private void LoadTargetServers() { if ((UserRole & UserRoles.SysAdmin) > 0 || (UserRole & UserRoles.AgentOperator) > 0) { // load any target servers if this is an msx this.targetServers = new MsaJobTargetServer[this.context.Server.JobServer.TargetServers.Count]; if (this.targetServers.Length > 0) { for (int i = 0; i < this.targetServers.Length; ++i) { this.targetServers[i] = new MsaJobTargetServer(this.context.Server.JobServer.TargetServers[i].Name); } } } else { this.targetServers = new MsaJobTargetServer[0]; } } private void LoadOperators() { if (this.Version.Major >= 9 || ((UserRole & UserRoles.SysAdmin) > 0)) { // load operators int operatorCount = this.context.Server.JobServer.Operators.Count; this.operators = new string[operatorCount]; for (int i = 0; i < operatorCount; i++) { this.operators[i] = this.context.Server.JobServer.Operators[i].Name; } } else { this.operators = new string[0]; } } private void LoadTrueLogin() { this.trueLogin = this.context.Server.ConnectionContext.TrueLogin; } private void LoadVersion() { this.version = this.context.Server.Information.Version; } private void LoadUserRoles() { SqlServer.Management.Smo.Server server = this.context.Server; this.userRole = UserRoles.None; if ((server.ConnectionContext.UserProfile & ServerUserProfiles.SALogin) > 0) { this.userRole |= UserRoles.SysAdmin; } if (this.Version.Major >= 9) { Database msdb = server.Databases["msdb"]; if (msdb != null) { if (msdb.IsMember("SQLAgentOperatorRole")) { this.userRole |= UserRoles.AgentOperator; } if (msdb.IsMember("SQLAgentReaderRole")) { this.userRole |= UserRoles.AgentReader; } } } this.userRole |= UserRoles.AgentUser; } #endregion #region saving public void ApplyChanges(bool creating) { Job job = null; bool scripting = this.context.Server.ConnectionContext.SqlExecutionModes == SqlExecutionModes.CaptureSql; bool targetServerSelected = false; this.mode = creating ? ActionMode.Create : ActionMode.Edit; // Before any job posting if donem make sure that if this is an MSX job that the user has selected at // least one Target Server. if (!this.targetLocalServer) { for (int i = 0; i < this.AvailableTargetServers.Length; ++i) { if (this.AvailableTargetServers[i].WillJobBeAppliedToTarget) { targetServerSelected = true; break; } } if (!targetServerSelected) { // Not target servers selected. Throw error. throw new ApplicationException(SR.TargetServerNotSelected); } } if (creating) { job = new Job(this.context.Server.JobServer, this.Name, this.Category.SmoCategory.ID); } else { // just lookup the original object job = this.Job; } if (!this.IsReadOnly) { if (creating || job.OwnerLoginName != this.owner) { job.OwnerLoginName = this.owner; } if (creating || (this.category != null && this.category.SmoCategory.Name != job.Category)) { job.Category = this.category.SmoCategory.Name; } if (!string.IsNullOrWhiteSpace(this.description) && (creating || this.description != job.Description)) { job.Description = this.description; } SaveNotifications(job, creating); } if (this.AllowEnableDisable) { if (creating || this.enabled != job.IsEnabled) { job.IsEnabled = this.enabled; } } // do the actual creation / alter if (creating) { ///Check to see if the job already exists JobExists(job.Name); job.Create(); if (!scripting) { this.urn = job.Urn; } } else { job.StartStepID = this.startStepID != -1 ? this.startStepID : job.StartStepID; job.Alter(); } if (!this.IsReadOnly && !scripting) { if (this.targetLocalServer) { if (!OriginallyTargetLocalServer || creating) { foreach (MsaJobTargetServer targetServer in this.AvailableTargetServers) { if (targetServer.IsJobAppliedToTarget) { job.RemoveFromTargetServer(targetServer.Name.ToUpperInvariant()); targetServer.IsJobAppliedToTarget = false; } } OriginallyTargetLocalServer = true; job.ApplyToTargetServer(this.context.Server.ConnectionContext.TrueName.ToUpperInvariant()); } } else if (this.IsMsx) { if (!creating && OriginallyTargetLocalServer) { // Remove from target server only if actually does target the local server string thisServerName = this.context.Server.ConnectionContext.TrueName.ToUpperInvariant(); DataTable targetServers = job.EnumTargetServers(); foreach (DataRow row in targetServers.Rows) { string targetServerName = row["ServerName"] as string; if (String.Compare(targetServerName, thisServerName, StringComparison.OrdinalIgnoreCase) == 0) { job.RemoveFromTargetServer(thisServerName); break; } } OriginallyTargetLocalServer = false; } // add and remove target servers for (int i = 0; i < this.AvailableTargetServers.Length; ++i) { if (this.AvailableTargetServers[i].WillJobBeAppliedToTarget && !this.AvailableTargetServers[i].IsJobAppliedToTarget) { job.ApplyToTargetServer(this.AvailableTargetServers[i].Name.ToUpperInvariant()); this.AvailableTargetServers[i].IsJobAppliedToTarget = true; } else if (!this.AvailableTargetServers[i].WillJobBeAppliedToTarget && this.AvailableTargetServers[i].IsJobAppliedToTarget) { job.RemoveFromTargetServer(this.AvailableTargetServers[i].Name); this.AvailableTargetServers[i].IsJobAppliedToTarget = false; } } } } // Because some of the SMO methods above can update Job's CategoryID affecting the Urn this.urn = job.Urn; bool stepsChanged = false; // save steps,schedules etc if (this.jobSteps != null) { stepsChanged = this.jobSteps.ApplyChanges(job); } bool schedulesChanged = false; if (this.jobSchedules != null) { schedulesChanged = this.jobSchedules.ApplyChanges(job); } if ((stepsChanged || schedulesChanged) && !this.TargetLocalServer && !creating) { // TODO: this seems wrong. Why do it here and not in SMO? this.context.Server.ConnectionContext.ExecuteNonQuery( string.Format(CultureInfo.InvariantCulture, "EXECUTE msdb.dbo.sp_post_msx_operation N'INSERT', N'JOB', @job_id = '{0}'" , job.JobID)); } // Do not attempt to save the job alert if we are in scripting mode, since the job id does not // yet exists. if (jobAlerts != null && !scripting) { this.jobAlerts.ApplyChanges(job); } // check the name if we are not creating if (!creating && this.Name != job.Name ) { // new name = rename job.Rename(this.Name); // get the new urn if we aren't scripting if (!scripting) { this.urn = job.Urn; } } } private void SaveNotifications(Job job, bool creating) { if (job == null) { throw new ArgumentNullException("job"); } // nothing to do if this information has not been loaded if (this.notificationsLoaded == false) { return; } // notifications panel if (this.emailLevel == CompletionAction.Never) { this.emailLevel = CompletionAction.OnFailure; this.operatorToEmail = String.Empty; } if (creating || this.emailLevel != job.EmailLevel) { job.EmailLevel = this.emailLevel; } if (creating || this.operatorToEmail != job.OperatorToEmail) { job.OperatorToEmail = this.operatorToEmail; } if (this.pageLevel == CompletionAction.Never) { this.pageLevel = CompletionAction.OnFailure; this.operatorToPage = String.Empty; } if (creating || this.pageLevel != job.PageLevel) { job.PageLevel = this.pageLevel; } if (creating || this.operatorToPage != job.OperatorToPage) { job.OperatorToPage = this.operatorToPage; } if (creating || this.eventLogLevel != job.EventLogLevel) { job.EventLogLevel = this.eventLogLevel; } if (creating || this.deleteLevel != job.DeleteLevel) { job.DeleteLevel = this.deleteLevel; } } #endregion #region implementation /// /// Convert a string into a Localizable job category /// /// /// internal LocalizableCategory ConvertStringToCategory(string source) { if (this.SmoCategories == null || this.SmoCategories.Length == 0) { throw new InvalidOperationException(); } LocalizableCategory category = null; for (int i = 0; i < this.SmoCategories.Length; ++i) { if (source == this.SmoCategories[i].SmoCategory.Name) { category = this.SmoCategories[i]; break; } } return category; } /// /// Check SMO to see if job already exists. /// /// private void JobExists(string jobName) { Microsoft.SqlServer.Management.Smo.Agent.JobCollection smoJobCollection = this.context.Server.JobServer.Jobs; try { if (smoJobCollection.Contains(jobName)) { throw new ApplicationException(SR.JobAlreadyExists(jobName)); } } finally { smoJobCollection = null; } } #endregion } }