How to use ormlite with SQL Server and Mars? - c#

ServiceStack aficionados, hello!
We are legion (I hope so), so please help a brother out :)
I am trying to populate two collections with one SQL Server 2008 stored procedure call that return two resultsets.
I have "MultipleActiveResultSets=True" in my connection string but I am still getting this error:
'r.NextResult()' threw an exception of type
'System.InvalidOperationException'
Here is my code:
IList<ProjectMember> projectMembers = null;
IList<Project> projects = DbFactory.Run(dbCnx =>
{
using (var dbCmd = dbCnx.CreateCommand())
{
dbCmd.CommandType = CommandType.StoredProcedure;
dbCmd.CommandText = "mySchema.myStoredProc";
dbCmd.Parameters.Add(new SqlParameter("#categoryId", categoryId));
using (profiler.Step("ProjectService.myStoredProc"))
{
var r = dbCmd.ExecuteReader();
projectMembers = r.ConvertToList<ProjectMember>();
return r.NextResult() ? r.ConvertToList<Project>() : null;
}
}
});
Is this possible? If so, can someone please show me an example on how to do that?
Thanks,
Samir

I've found a way but I had to replace ormLite with Dapper:
using(var cnx = DbFactory.CreateConnection(Global.ConnectionString))
{
using (var multi = cnx.QueryMultiple("mySchema.myStoredProc", new { communityId, categoryId }, commandType: CommandType.StoredProcedure))
{
var projectMembers = multi.Read<ProjectMember>().ToList();
var projects = multi.Read<Project>().ToList();
BindProjectMembers(projects, projectMembers);
return projects;
}
}
It works perfectly and is twice as fast (from 40 ms to 20 ms) than before the MARS amelioration.

Related

Npgsql: prepared statement "_p1" does not exist

I am trying to load EOD stock data into a table using this method:
public async Task<long> BulkInsertEodData(IEnumerable<EodData> records, string symbol)
{
var recordsProcessed = 0L;
using (var conn = await OpenConnection())
using (var trans = conn.BeginTransaction())
using (var comm = _factory.CreateCommand())
{
try
{
comm.Connection = conn;
comm.Transaction = trans;
comm.CommandText = INSERT_EOD;
var ps = AddParametersToInsertEodQuery(comm);
foreach (var p in ps) comm.Parameters.Add(p);
comm.Prepare();
foreach (var record in records)
{
comm.Parameters["#date_id"].Value = record.DateId;
comm.Parameters["#symbol"].Value = symbol.ToUpper();
comm.Parameters["#eod_close"].Value = record.EodClose;
comm.Parameters["#eod_high"].Value = record.EodHigh;
comm.Parameters["#eod_low"].Value = record.EodLow;
comm.Parameters["#eod_volume"].Value = record.EodVolume;
comm.Parameters["#eod_open"].Value = record.EodOpen;
comm.Parameters["#eod_split"].Value = record.EodSplit;
comm.Parameters["#eod_dividend"].Value = record.EodDividend;
comm.Parameters["#last_modified"].Value = DateTime.UtcNow;
await comm.ExecuteNonQueryAsync();
recordsProcessed++;
}
trans.Commit();
}
catch (Exception ex)
{
_logger.LogError(ex, "BulkInsertEodData(IEnumerable<EodData>)");
trans.Rollback();
}
}
return recordsProcessed;
}
The query text is as follows:
INSERT INTO public.eod_datas(
date_id,
stock_id,
eod_open,
eod_close,
eod_low,
eod_high,
eod_volume,
eod_dividend,
eod_split,
last_modified_timestamp
)
values
#date_id,
(select s.id from stocks s where s.symbol = #symbol limit 1),
#eod_open,
#eod_clos,
#eod_low,
#eod_high,
#eod_volume,
#eod_dividend,
#eod_split,
current_timestamp
on conflict (date_id, stock_id)
do update set
eod_open = #eod_open,
eod_close = #eod_close,
eod_low = #eod_low,
eod_high = #eod_high,
eod_volume = #eod_volume,
eod_dividend = #eod_dividend,
eod_split = #eod_split,
last_modified_timestamp = current_timestamp;
It's not my first rodeo with prepared statements, but I am doing a couple things different this time around (.NET Core, using DbProviderFactory) and I'm getting odd results.
The first couple of times through this method, I get an error to the effect of Npgsql.PostgresException (0x80004005): 42601: syntax error at or near "$1" which is fairly mystifying in itself but the most mysterious of all is that the error actually goes away after a couple of method calls, and I start getting Npgsql.PostgresException (0x80004005): 26000: prepared statement "_p1" does not exist consistently afterwords.
Can someone explain this behavior? What am I doing wrong? Where do I get more details into what "$1" is all about?
You are missing brackets around values being inserted. Postgres sadly will not tell you that it is expecting bracket before $1.
Correct syntax:
values (
#date_id,
(select s.id from stocks s where s.symbol = #symbol limit 1),
#eod_open,
#eod_clos,
#eod_low,
#eod_high,
#eod_volume,
#eod_dividend,
#eod_split,
current_timestamp)
on conflict (date_id, stock_id)

Get list/multiset from informix to C#

I'm making a library for connecting an Informix database with a C# extension for use in Outsystems.
So, now I've hit a wall. I need to receive a list/multiset from the db. How can I do that? I'm using the IfxDataReader to receive the data. But I see no method that could work.
Here is how we can send a list/multiset input parameter. But I need to receive it from a result set.
EDIT: Seeing as this was frowned upon by someone I'll provide some code and my try at it to see if you think it's correct (can't test it right now as I don't yet have data on the database... I'll test it in the end):
ssBensAndFotos = new RLBensAndFotos_BemRecordList();
DatabaseConnection dc = new DatabaseConnection(ssDatabase, ssHost, ssServer, ssService, ssProtocol, ssUserID, ssPassword);
try
{
dc.Conn = new IfxConnection(dc.ConnectionString);
dc.Cmd = new IfxCommand("get_bens_and_fotos_by_id_diligencia", dc.Conn);
dc.Cmd.CommandType = CommandType.StoredProcedure;
dc.Cmd.Parameters.Add(new IfxParameter("pi_id_diligencia", IfxType.Integer) { Direction = ParameterDirection.Input, Value = sspi_id_diligencia });
dc.Conn.Open();
using (IfxDataReader reader = dc.Cmd.ExecuteReader())
if (reader.HasRows)
while (reader.Read())
{
var bensAndFotos = new STBensAndFotos_BemStructure()
{
ssid_bem = reader.GetInt32(0),
ssnumero = reader.GetInt32(1),
ssespecie = reader.GetString(2),
ssbem = reader.GetString(3),
ssvalor = reader.GetDecimal(4),
sscomum = reader.GetInt32(5),
ssvoice_record = AuxiliaryMethods.CreateByteArrayFromIfxBlob(reader.GetIfxBlob(6))
};
// Here I get the string, split it and check to see which members of the array are integers, since in this case I'll be getting a multiset of int's
var multisetString = reader.GetString(7).Split('\'');
int n;
foreach (var item in multisetString)
if (int.TryParse(item, out n))
bensAndFotos.ssfotos.Add(new STBensAndFotos_FotoStructure() { ssfoto = n });
ssBensAndFotos.Add(bensAndFotos);
}
dc.Conn.Close();
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
LIST, MULTISET and SET are mapped to String. You should be able to use IfxDataReader.GetString() to get the data.

'sqlConnection.InnerConnection.ServerVersionNormalized' Threw an Exception (Connection open, sqldatareader not working)

This seems to be a problem either with my computer or the azure sql servers. The problem I have was not a problem before but the code all of a sudden acting strange. Here is my code that is used to work. Connection opens successfully, my executenonquery works fine but reader acts strange.
public static List<ClientDto> GetClientInformation()
{
List<ClientDto> results = new List<ClientDto>();
try
{
using (var sqlConnection =
new SqlConnection(ConfigurationManager.ConnectionStrings["ClientData"].ConnectionString))
{
using (var command = new SqlCommand(null, sqlConnection))
{
sqlConnection.Open();
command.CommandType = CommandType.StoredProcedure;
//running the GetClientInformation stored procedure in DB
command.CommandText = Constants.GetClientInformation;
SqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
results.Add(new ClientDto()
{
Id = Convert.ToInt32(reader["Id"].ToString()),
DealerName = reader["DealerName"].ToString(),
GroupID = reader["GroupID"].ToString(),
DealerId = reader["DealerId"].ToString(),
DealerFolderName = reader["DealerFolder"].ToString(),
DMSType = reader["DMSType"].ToString(),
MAIsActive = Convert.ToBoolean(Convert.ToInt32(reader["MAIsActive"])),
SalesIsActive = Convert.ToBoolean(Convert.ToInt32(reader["SalesIsActive"])),
SalesSource = reader["SalesSource"].ToString(),
InventoryIsActive = Convert.ToBoolean(Convert.ToInt32(reader["InventoryIsActive"])),
InventorySource = reader["InventorySource"].ToString(),
AppointmentsIsActive =
Convert.ToBoolean(Convert.ToInt32(reader["AppointmentsIsActive"])),
AppointmentsSource = reader["AppointmentsSource"].ToString(),
LeadsIsActive = Convert.ToBoolean(Convert.ToInt32(reader["LeadsIsActive"])),
LeadsSource = reader["LeadsSource"].ToString(),
ServiceIsActive = Convert.ToBoolean(Convert.ToInt32(reader["ServiceIsActive"])),
ServiceSource = reader["ServiceSource"].ToString(),
SalesIsDelete = Convert.ToBoolean(Convert.ToInt32(reader["SalesIsDelete"])),
SalesDeleteRange = reader["SalesDeleteRange"].ToString(),
InventoryIsDelete = Convert.ToBoolean(Convert.ToInt32(reader["InventoryIsDelete"])),
InventoryDeleteRange = reader["InventoryDeleteRange"].ToString(),
AppointmentsIsDelete =
Convert.ToBoolean(Convert.ToInt32(reader["AppointmentsIsDelete"])),
AppointmentsDeleteRange = reader["AppointmentsDeleteRange"].ToString(),
LeadsIsDelete = Convert.ToBoolean(Convert.ToInt32(reader["LeadsIsDelete"])),
LeadsDeleteRange = reader["LeadsDeleteRange"].ToString(),
ServiceIsDelete = Convert.ToBoolean(Convert.ToInt32(reader["ServiceIsDelete"])),
ServiceDeleteRange = reader["ServiceDeleteRange"].ToString(),
IsActive = Convert.ToBoolean(Convert.ToInt32(reader["IsActive"])),
UserDefinedName = reader["UserDefinedName"].ToString()
});
}
}
}
return results;
}
catch (Exception ex)
{
//If an exception happens it will insert the error to errorlog table and to the log file
Logger.WriteError(new ErrorDto()
{
Source = "GetClientInformation",
Message = ex.Message,
StackTrace = ex.StackTrace
});
Console.WriteLine(string.Format("Error - {0} - {1} ", "GetClientInformation", ex.Message));
return results;
}
}
There is data in the sql, the executable of my application works fine on our virtual machine (Windows Server).
Only strange thing I see in the code is that SQLConnection have this innerexception (it is not stopping the code to run though)
ServerVersionNormalized = 'sqlConnection.InnerConnection.ServerVersionNormalized' threw an exception of type 'System.NotSupportedException'
I am not sure if this was an issue before or not.
This picture shows that Enamration yielded no result
This picture shows that reader has rows
I am reinstalling my visual studio now to see if it will fix the issue.
This issue is happening in both Visual Studio 2015 and 2017 for me.
This is my connection string format:
Server=.database.windows.net,1433;Database=; User ID=;Password=;Trusted_Connection=False; Encrypt=True;
It looks like it is working fine now. To me it sounds like since I had breakpoint in the code, it might have caused the issue because of the longer time between connecting and running the query.

How to trigger and find status of sql job through c#? [duplicate]

I need to create an application for monitoring SQL Server 2000 Agent Job status and info when Job occur same as show on Windows application event log. Now I connect to the database already via a connection string, but I don't know how to get the status and info from Job.
I need to show status and info on Textbox.
What do you suggestion how to do.
Developer tools :
MS SQL Sever 2000 SP4
MS Visual Studio 2008 (C#)
I am a rookie programmer.
i can do this already...
i select form table "Sysjobserver" in database "msdb" for read status, date, time of job that i want.
use this code
public void GetJobsAndStatus()
{
string sqlJobQuery = "select j.job_id, j.name, j.enabled, jh.run_status," +
" js.last_outcome_message, jh.run_date, jh.step_name, jh.run_time" +
" from sysjobs j left join sysjobhistory jh on (j.job_id = jh.job_id)" +
" left join sysjobservers js on (j.job_id = js.job_id)" +
" where jh.run_date = (select Max(run_date) from sysjobhistory)" +
" and jh.run_time = (select Max(run_time) from sysjobhistory)";
// create SQL connection and set up SQL Command for query
using (SqlConnection _con = new SqlConnection("server=10.15.13.70;database=msdb;user id=sa;pwd="))
using (SqlCommand _cmd = new SqlCommand(sqlJobQuery, _con))
{
try
{
// open connection
_con.Open();
SqlConnection.ClearPool(_con);
// create SQL Data Reader and grab data
using (SqlDataReader rdr = _cmd.ExecuteReader())
{
// as long as we get information from the reader
while (rdr.Read())
{
Guid jobID = rdr.GetGuid(0); // read Job_id
string jobName = rdr.GetString(1); // read Job name
byte jobEnabled = rdr.GetByte(2); // read Job enabled flag
int jobStatus = rdr.GetInt32(3); // read last_run_outcome from sysjobserver
string jobMessage = rdr.GetString(4); // read Message from sysjobserver
int jobRunDate = rdr.GetInt32(5); // read run_date from sysjobhistory
string jobStepName = rdr.GetString(6); // read StepName from sysjobhistory
int jobRunTime = rdr.GetInt32(7); // read run_time from sysjobhistory
String[] lviData = new String[] // ตัวแปรอะเรย์ชื่อ lviData
{
jobID.ToString(),
jobName.ToString(),
jobStepName.ToString(),
jobMessage.ToString(),
jobStatus.ToString(),
jobRunDate.ToString(),
jobRunTime.ToString(),
//jobEnabled.ToString(),
};
newData = lviData;
DisplayList(); // for display data on datagridview
}
rdr.Close();
}
}
thank you for everybody help very much. :-D
SQL stored procedures of queries don't give you any system data unless you have db_owner rights on the msdb system database, at lease in SQL Server 2008. Therefore mentioned methods normally don't work for applications where you want to show or manage jobs. However SMO namespace provides you with managed code solution for many SQL Server management features, including the SQL Server Agent functions that only require SQLServerAgent* permissions that you normally could get sorted for your application user. A good intro of using SMO classes to work with jobs is given here:
http://www.codeproject.com/Tips/367470/Manage-SQL-Server-Agent-Jobs-using-Csharp
I work on a similar task now and whilst SQL queries give me access denied, with C# code and Microsoft.SqlServer.Management.Smo.Agent namespace I just listed all jobs with this code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.Smo.Agent;
namespace SmoTest
{
class Program
{
static readonly string SqlServer = #"SQL01\SQL01";
static void Main(string[] args)
{
ServerConnection conn = new ServerConnection(SqlServer);
Server server = new Server(conn);
JobCollection jobs = server.JobServer.Jobs;
foreach (Job job in jobs)
{
Console.WriteLine(job.Name);
}
}
}
}
This should be a good starting point to find out how to find your SQL Agent jobs using T-SQL:
View (and disable) SQL Agent Jobs with TSQL
The script will list out all your jobs on your database, and when they will be run next and so forth.
Using the job_name, you should also be able to find out details about your jobs using the SQL Server Agent Stored Procedures in the msdb database on your server.
On SQL Server 2005 and above, you can use the system stored procedure msdb.dbo.sp_help_job to get information, including status, about SQL Server Agent Jobs. You can read more about sp_help_job at http://msdn.microsoft.com/en-us/library/ms186722(v=SQL.90).aspx.
Here is the sample code to do this from C#.
private Dictionary<int, string> ExecutionStatusDictionary = new Dictionary<int, string>()
{
{0, "Not idle or suspended"},
{1, "Executing"},
{2, "Waiting for thread"},
{3, "Between retries"},
{4, "Idle"},
{5, "Suspended"},
{7, "Performing completion actions"}
};
public string GetStatus()
{
SqlConnection msdbConnection = new SqlConnection("Data Source=SERVERNAME;Initial Catalog=msdb;Integrated Security=SSPI");
System.Text.StringBuilder resultBuilder = new System.Text.StringBuilder();
try
{
msdbConnection.Open();
SqlCommand jobStatusCommand = msdbConnection.CreateCommand();
jobStatusCommand.CommandType = CommandType.StoredProcedure;
jobStatusCommand.CommandText = "sp_help_job";
SqlParameter jobName = jobStatusCommand.Parameters.Add("#job_name", SqlDbType.VarChar);
jobName.Direction = ParameterDirection.Input;
jobName.Value = "LoadRegions";
SqlParameter jobAspect = jobStatusCommand.Parameters.Add("#job_aspect", SqlDbType.VarChar);
jobAspect.Direction = ParameterDirection.Input;
jobAspect.Value = "JOB";
SqlDataReader jobStatusReader = jobStatusCommand.ExecuteReader();
while (jobStatusReader.Read())
{
resultBuilder.Append(string.Format("{0} {1}",
jobStatusReader["name"].ToString(),
ExecutionStatusDictionary[(int)jobStatusReader["current_execution_status"]]
));
}
jobStatusReader.Close();
}
finally
{
msdbConnection.Close();
}
return resultBuilder.ToString();
}
You can get a list of all server jobs using this SELECT:
SELECT [name] FROM msdb.dbo.sysjobs
If you'd like to get a list of currently running jobs and their information, I would recommend writing a stored procedure in SQL which your application calls. There's a good demonstration here you could use...
http://feodorgeorgiev.com/blog/2010/03/how-to-query-currently-running-sql-server-agent-jobs/
Good luck!
For my use case, I specifically needed to know when the job was finished running, and whether or not it succeeded. Here is my code to do that:
using System;
using System.Data;
using System.Data.SqlClient;
namespace LaunchJobAndWaitTillDone
{
class Program
{
const string connectionString = "Data Source=YOURSERVERNAMEHERE;Initial Catalog=msdb;Integrated Security=SSPI";
const string jobName = "YOURJOBNAMEHERE";
static readonly TimeSpan waitFor = TimeSpan.FromSeconds(1.0);
enum JobExecutionResult
{
Succeeded,
FailedToStart,
FailedAfterStart,
Unknown
}
static void Main(string[] args)
{
var instance = new Program();
JobExecutionResult jobResult = instance.RunJob(jobName);
switch (jobResult)
{
case JobExecutionResult.Succeeded:
Console.WriteLine($"SQL Server Agent job, '{jobName}', ran successfully to completion.");
break;
case JobExecutionResult.FailedToStart:
Console.WriteLine($"SQL Server Agent job, '{jobName}', failed to start.");
break;
case JobExecutionResult.FailedAfterStart:
Console.WriteLine($"SQL Server Agent job, '{jobName}', started successfully, but encountered an error.");
break;
default:
Console.WriteLine($"Unknown result from attempting to run SQL Server Agent job, '{jobName}'.");
break;
}
Console.ReadLine();
return;
}
JobExecutionResult RunJob(string jobName)
{
int jobResult;
using (var jobConnection = new SqlConnection(connectionString))
{
SqlCommand jobCommand;
SqlParameter jobReturnValue;
SqlParameter jobParameter;
jobCommand = new SqlCommand("sp_start_job", jobConnection);
jobCommand.CommandType = CommandType.StoredProcedure;
jobReturnValue = new SqlParameter("#RETURN_VALUE", SqlDbType.Int);
jobReturnValue.Direction = ParameterDirection.ReturnValue;
jobCommand.Parameters.Add(jobReturnValue);
jobParameter = new SqlParameter("#job_name", SqlDbType.VarChar);
jobParameter.Direction = ParameterDirection.Input;
jobCommand.Parameters.Add(jobParameter);
jobParameter.Value = jobName;
jobConnection.Open();
try
{
jobCommand.ExecuteNonQuery();
jobResult = (Int32)jobCommand.Parameters["#RETURN_VALUE"].Value;
}
catch (SqlException)
{
jobResult = -1;
}
}
switch (jobResult)
{
case 0:
break;
default:
return JobExecutionResult.FailedToStart;
}
while (true)
{
using (var jobConnection2 = new SqlConnection(connectionString))
{
SqlCommand jobCommand2 = new SqlCommand("sp_help_jobactivity", jobConnection2);
jobCommand2.CommandType = CommandType.StoredProcedure;
SqlParameter jobReturnValue2 = new SqlParameter("#RETURN_VALUE", SqlDbType.Int);
jobReturnValue2.Direction = ParameterDirection.ReturnValue;
jobCommand2.Parameters.Add(jobReturnValue2);
SqlParameter jobParameter2 = new SqlParameter("#job_name", SqlDbType.VarChar);
jobParameter2.Direction = ParameterDirection.Input;
jobCommand2.Parameters.Add(jobParameter2);
jobParameter2.Value = jobName;
jobConnection2.Open();
SqlDataReader rdr = jobCommand2.ExecuteReader();
while (rdr.Read())
{
object msg = rdr["message"];
object run_status = rdr["run_status"];
if (!DBNull.Value.Equals(msg))
{
var message = msg as string;
var runStatus = run_status as Int32?;
if (message != null && message.StartsWith("The job succeeded")
&& runStatus.HasValue && runStatus.Value == 1)
{
return JobExecutionResult.Succeeded;
}
else if (message != null && message.StartsWith("The job failed"))
{
return JobExecutionResult.FailedAfterStart;
}
else if (runStatus.HasValue && runStatus.Value == 1)
{
return JobExecutionResult.Unknown;
}
}
}
}
System.Threading.Thread.Sleep(waitFor);
}
}
}
}
Note that you may need database/server owner permissions or something like that for this code to work.

How to monitor SQL Server Agent Job info in C#

I need to create an application for monitoring SQL Server 2000 Agent Job status and info when Job occur same as show on Windows application event log. Now I connect to the database already via a connection string, but I don't know how to get the status and info from Job.
I need to show status and info on Textbox.
What do you suggestion how to do.
Developer tools :
MS SQL Sever 2000 SP4
MS Visual Studio 2008 (C#)
I am a rookie programmer.
i can do this already...
i select form table "Sysjobserver" in database "msdb" for read status, date, time of job that i want.
use this code
public void GetJobsAndStatus()
{
string sqlJobQuery = "select j.job_id, j.name, j.enabled, jh.run_status," +
" js.last_outcome_message, jh.run_date, jh.step_name, jh.run_time" +
" from sysjobs j left join sysjobhistory jh on (j.job_id = jh.job_id)" +
" left join sysjobservers js on (j.job_id = js.job_id)" +
" where jh.run_date = (select Max(run_date) from sysjobhistory)" +
" and jh.run_time = (select Max(run_time) from sysjobhistory)";
// create SQL connection and set up SQL Command for query
using (SqlConnection _con = new SqlConnection("server=10.15.13.70;database=msdb;user id=sa;pwd="))
using (SqlCommand _cmd = new SqlCommand(sqlJobQuery, _con))
{
try
{
// open connection
_con.Open();
SqlConnection.ClearPool(_con);
// create SQL Data Reader and grab data
using (SqlDataReader rdr = _cmd.ExecuteReader())
{
// as long as we get information from the reader
while (rdr.Read())
{
Guid jobID = rdr.GetGuid(0); // read Job_id
string jobName = rdr.GetString(1); // read Job name
byte jobEnabled = rdr.GetByte(2); // read Job enabled flag
int jobStatus = rdr.GetInt32(3); // read last_run_outcome from sysjobserver
string jobMessage = rdr.GetString(4); // read Message from sysjobserver
int jobRunDate = rdr.GetInt32(5); // read run_date from sysjobhistory
string jobStepName = rdr.GetString(6); // read StepName from sysjobhistory
int jobRunTime = rdr.GetInt32(7); // read run_time from sysjobhistory
String[] lviData = new String[] // ตัวแปรอะเรย์ชื่อ lviData
{
jobID.ToString(),
jobName.ToString(),
jobStepName.ToString(),
jobMessage.ToString(),
jobStatus.ToString(),
jobRunDate.ToString(),
jobRunTime.ToString(),
//jobEnabled.ToString(),
};
newData = lviData;
DisplayList(); // for display data on datagridview
}
rdr.Close();
}
}
thank you for everybody help very much. :-D
SQL stored procedures of queries don't give you any system data unless you have db_owner rights on the msdb system database, at lease in SQL Server 2008. Therefore mentioned methods normally don't work for applications where you want to show or manage jobs. However SMO namespace provides you with managed code solution for many SQL Server management features, including the SQL Server Agent functions that only require SQLServerAgent* permissions that you normally could get sorted for your application user. A good intro of using SMO classes to work with jobs is given here:
http://www.codeproject.com/Tips/367470/Manage-SQL-Server-Agent-Jobs-using-Csharp
I work on a similar task now and whilst SQL queries give me access denied, with C# code and Microsoft.SqlServer.Management.Smo.Agent namespace I just listed all jobs with this code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.Smo.Agent;
namespace SmoTest
{
class Program
{
static readonly string SqlServer = #"SQL01\SQL01";
static void Main(string[] args)
{
ServerConnection conn = new ServerConnection(SqlServer);
Server server = new Server(conn);
JobCollection jobs = server.JobServer.Jobs;
foreach (Job job in jobs)
{
Console.WriteLine(job.Name);
}
}
}
}
This should be a good starting point to find out how to find your SQL Agent jobs using T-SQL:
View (and disable) SQL Agent Jobs with TSQL
The script will list out all your jobs on your database, and when they will be run next and so forth.
Using the job_name, you should also be able to find out details about your jobs using the SQL Server Agent Stored Procedures in the msdb database on your server.
On SQL Server 2005 and above, you can use the system stored procedure msdb.dbo.sp_help_job to get information, including status, about SQL Server Agent Jobs. You can read more about sp_help_job at http://msdn.microsoft.com/en-us/library/ms186722(v=SQL.90).aspx.
Here is the sample code to do this from C#.
private Dictionary<int, string> ExecutionStatusDictionary = new Dictionary<int, string>()
{
{0, "Not idle or suspended"},
{1, "Executing"},
{2, "Waiting for thread"},
{3, "Between retries"},
{4, "Idle"},
{5, "Suspended"},
{7, "Performing completion actions"}
};
public string GetStatus()
{
SqlConnection msdbConnection = new SqlConnection("Data Source=SERVERNAME;Initial Catalog=msdb;Integrated Security=SSPI");
System.Text.StringBuilder resultBuilder = new System.Text.StringBuilder();
try
{
msdbConnection.Open();
SqlCommand jobStatusCommand = msdbConnection.CreateCommand();
jobStatusCommand.CommandType = CommandType.StoredProcedure;
jobStatusCommand.CommandText = "sp_help_job";
SqlParameter jobName = jobStatusCommand.Parameters.Add("#job_name", SqlDbType.VarChar);
jobName.Direction = ParameterDirection.Input;
jobName.Value = "LoadRegions";
SqlParameter jobAspect = jobStatusCommand.Parameters.Add("#job_aspect", SqlDbType.VarChar);
jobAspect.Direction = ParameterDirection.Input;
jobAspect.Value = "JOB";
SqlDataReader jobStatusReader = jobStatusCommand.ExecuteReader();
while (jobStatusReader.Read())
{
resultBuilder.Append(string.Format("{0} {1}",
jobStatusReader["name"].ToString(),
ExecutionStatusDictionary[(int)jobStatusReader["current_execution_status"]]
));
}
jobStatusReader.Close();
}
finally
{
msdbConnection.Close();
}
return resultBuilder.ToString();
}
You can get a list of all server jobs using this SELECT:
SELECT [name] FROM msdb.dbo.sysjobs
If you'd like to get a list of currently running jobs and their information, I would recommend writing a stored procedure in SQL which your application calls. There's a good demonstration here you could use...
http://feodorgeorgiev.com/blog/2010/03/how-to-query-currently-running-sql-server-agent-jobs/
Good luck!
For my use case, I specifically needed to know when the job was finished running, and whether or not it succeeded. Here is my code to do that:
using System;
using System.Data;
using System.Data.SqlClient;
namespace LaunchJobAndWaitTillDone
{
class Program
{
const string connectionString = "Data Source=YOURSERVERNAMEHERE;Initial Catalog=msdb;Integrated Security=SSPI";
const string jobName = "YOURJOBNAMEHERE";
static readonly TimeSpan waitFor = TimeSpan.FromSeconds(1.0);
enum JobExecutionResult
{
Succeeded,
FailedToStart,
FailedAfterStart,
Unknown
}
static void Main(string[] args)
{
var instance = new Program();
JobExecutionResult jobResult = instance.RunJob(jobName);
switch (jobResult)
{
case JobExecutionResult.Succeeded:
Console.WriteLine($"SQL Server Agent job, '{jobName}', ran successfully to completion.");
break;
case JobExecutionResult.FailedToStart:
Console.WriteLine($"SQL Server Agent job, '{jobName}', failed to start.");
break;
case JobExecutionResult.FailedAfterStart:
Console.WriteLine($"SQL Server Agent job, '{jobName}', started successfully, but encountered an error.");
break;
default:
Console.WriteLine($"Unknown result from attempting to run SQL Server Agent job, '{jobName}'.");
break;
}
Console.ReadLine();
return;
}
JobExecutionResult RunJob(string jobName)
{
int jobResult;
using (var jobConnection = new SqlConnection(connectionString))
{
SqlCommand jobCommand;
SqlParameter jobReturnValue;
SqlParameter jobParameter;
jobCommand = new SqlCommand("sp_start_job", jobConnection);
jobCommand.CommandType = CommandType.StoredProcedure;
jobReturnValue = new SqlParameter("#RETURN_VALUE", SqlDbType.Int);
jobReturnValue.Direction = ParameterDirection.ReturnValue;
jobCommand.Parameters.Add(jobReturnValue);
jobParameter = new SqlParameter("#job_name", SqlDbType.VarChar);
jobParameter.Direction = ParameterDirection.Input;
jobCommand.Parameters.Add(jobParameter);
jobParameter.Value = jobName;
jobConnection.Open();
try
{
jobCommand.ExecuteNonQuery();
jobResult = (Int32)jobCommand.Parameters["#RETURN_VALUE"].Value;
}
catch (SqlException)
{
jobResult = -1;
}
}
switch (jobResult)
{
case 0:
break;
default:
return JobExecutionResult.FailedToStart;
}
while (true)
{
using (var jobConnection2 = new SqlConnection(connectionString))
{
SqlCommand jobCommand2 = new SqlCommand("sp_help_jobactivity", jobConnection2);
jobCommand2.CommandType = CommandType.StoredProcedure;
SqlParameter jobReturnValue2 = new SqlParameter("#RETURN_VALUE", SqlDbType.Int);
jobReturnValue2.Direction = ParameterDirection.ReturnValue;
jobCommand2.Parameters.Add(jobReturnValue2);
SqlParameter jobParameter2 = new SqlParameter("#job_name", SqlDbType.VarChar);
jobParameter2.Direction = ParameterDirection.Input;
jobCommand2.Parameters.Add(jobParameter2);
jobParameter2.Value = jobName;
jobConnection2.Open();
SqlDataReader rdr = jobCommand2.ExecuteReader();
while (rdr.Read())
{
object msg = rdr["message"];
object run_status = rdr["run_status"];
if (!DBNull.Value.Equals(msg))
{
var message = msg as string;
var runStatus = run_status as Int32?;
if (message != null && message.StartsWith("The job succeeded")
&& runStatus.HasValue && runStatus.Value == 1)
{
return JobExecutionResult.Succeeded;
}
else if (message != null && message.StartsWith("The job failed"))
{
return JobExecutionResult.FailedAfterStart;
}
else if (runStatus.HasValue && runStatus.Value == 1)
{
return JobExecutionResult.Unknown;
}
}
}
}
System.Threading.Thread.Sleep(waitFor);
}
}
}
}
Note that you may need database/server owner permissions or something like that for this code to work.

Categories

Resources