I am writing a CLR function to parse a table column and write the results into another table. Basic requirement is parsing the Detail column, which contains a Time part and an ID part. Results will be the time difference between two Ids.
Ex: Time1,Id1;Time2,Id2;Time3,Id3... and so on
Time2-Time1 is time taken by Id1 in seconds.
Same function is working in normal console application but CLR function is throwing the exception when I call it from SQL server.
Error is
A .NET Framework error occurred during execution of user-defined routine or aggregate "Function1":
System.Security.HostProtectionException: Attempted to perform an operation that was forbidden by the CLR host.
The protected resources (only available with full trust) were: All
The demanded resources were: UI
System.Security.HostProtectionException:
at UserDefinedFunctions.Function1(String msisdn, String promptdetails)
My code is:
SqlConnection conn = new SqlConnection();
SqlCommand cmd = new SqlCommand();
int count = 0;
string PromptPart = string.Empty;
string PrevPromptPart = string.Empty;
DateTime TimePart;
TimePart = new DateTime();
DateTime PrevTimePart;
PrevTimePart = new DateTime();
TimeSpan difference;
try
{
count++;
conn.ConnectionString = "Context Connection=true";
cmd.Connection = conn;
String[] string1 = promptdetails.Split(";".ToCharArray());
foreach (var item1 in string1)
{
count++;
String[] string2 = item1.Split(",".ToCharArray());
PromptPart = string2[1];
TimePart = DateTime.ParseExact(string2[0], "M/d/yyyy h:mm:ss tt", System.Globalization.CultureInfo.InvariantCulture);
if (count > 1)
{
StringBuilder sbQuery = new StringBuilder(1024);
sbQuery.Append("INSERT INTO [Shami].[DBO].[data] (MSISDN,PromptID1,PromptID2,TimeDifference) VALUES");
sbQuery.Append("('");
sbQuery.Append(msisdn);
sbQuery.Append("',");
difference = TimePart.Subtract(PrevTimePart);
sbQuery.Append("'");
sbQuery.Append(PrevPromptPart);
sbQuery.Append("','");
sbQuery.Append(PromptPart);
sbQuery.Append("',");
sbQuery.Append(difference.Seconds);
sbQuery.Append(")");
string sub = string.Empty;
sub = sbQuery.ToString();
try
{
conn.Open();
cmd = new SqlCommand(sub);
SqlContext.Pipe.ExecuteAndSend(cmd);
}
catch (Exception ie)
{
Console.WriteLine("Error..");
}
}
if (count <= 1)
{
StringBuilder sbQuery = new StringBuilder(1024);
sbQuery.Append("INSERT INTO [Shami].[DBO].[data] (MSISDN,PromptID1,PromptID2,TimeDifference) VALUES");
sbQuery.Append("('");
sbQuery.Append(msisdn);
sbQuery.Append("',");
sbQuery.Append("'_'");
sbQuery.Append(",");
sbQuery.Append(PromptPart);
sbQuery.Append(",");
sbQuery.Append("'0'");
sbQuery.Append(")");
string sub = string.Empty;
sub = sbQuery.ToString();
try
{
conn.Open();
cmd = new SqlCommand(sub);
SqlContext.Pipe.ExecuteAndSend(cmd);
}
catch (Exception ie)
{
Console.WriteLine("Error..");
}
}
PrevPromptPart = PromptPart;
PrevTimePart = TimePart;
}
}
catch (Exception)
{ ;}
finally
{
conn.Close();
conn.Dispose();
cmd.Dispose();
}
return msisdn;
Please let me know where I'm going wrong. I can't debug in CLR.
You would just need to replace your line
Console.WriteLine("Error...");
with this line:
SqlContext.Pipe.Send("Error...");
This will do the same thing for you and it will run
Related
I am creating a simple inventory system using c#.
When I am generating the invoice number, the form is loaded but it doesn't show anything.
It is an auto-incremented invoice number; order is completed incrementally by 1.
For example, starting at E-0000001, after order we expect E-0000002. I don't understand why it is blank.
No error displayed. I tried to debug the code but I couldn't find what's wrong.
public void invoiceno()
{
try
{
string c;
sql = "SELECT MAX(invoid) FROM sales";
cmd = new SqlCommand(sql, con);
var maxInvId = cmd.ExecuteScalar() as string;
if (maxInvId == null)
{
label4.Text = "E-000001";
}
else
{
int intVal = int.Parse(maxInvId.Substring(2, 6));
intVal++;
label4.Text = String.Format("E-{0:000000}", intVal);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
Console.Write(ex.StackTrace);
}
}
Let's extract a method - NextInvoiceId - we
Open connection
Execute query
Obtain next invoice number
Code:
private int NextInvoiceNumber() {
//TODO: put the right connection string here
using(var conn = new SqlConnection(ConnectionStringHere)) {
conn.Open();
string sql =
#"SELECT MAX(invoid)
FROM sales";
using (var cmd = new SqlCommand(sql, conn)) {
var raw = cmd.ExecuteScalar() as string;
return raw == null
? 1 // no invoces, we start from 1
: int.Parse(raw.Trim('e', 'E', '-')) + 1;
}
}
}
Then we can easily call it:
public void invoiceno() {
label4.Text = $"E-{NextInvoiceNumber():d6}";
}
Edit: You should not swallow exceptions:
try
{
...
}
// Don't do this!
catch (Exception ex) // All the exceptions will be caught and...
{
// printed on the Console...
// Which is invisible to you, since you develop Win Forms (WPF) application
Console.WriteLine(ex.ToString());
Console.Write(ex.StackTrace);
}
let system die and inform you that something got wrong
I have an application currently fully operational using OracleDB.Recently, I converted my OracleDB into PostgreSQL and I am facing a weird problem.Whenever I try to access my procedure of PostGreSQL through application, sometimes it works perfectly and sometimes does not.It gives an error saying that "this procedure(my procedure name) does not exist,while it exists in the database and works perfectly with pgAdmin query tool. I am really confused,please help me out. Here I am giving one of my sample procedure below and attaching error log too.
CREATE OR REPLACE FUNCTION sylvia.pro_occupation_add(
p_occupationid bigint,
p_occupationname text,
p_occupationdetails text)
RETURNS void
LANGUAGE 'plpgsql'
AS $BODY$
begin
insert into sylvia.occupation (occupationid,occupationname,occupationdetails)
values (p_occupationid, p_occupationname, p_occupationdetails);
end;
$BODY$;
C# code :
private DataSet _createOccupation(DataSet inputDS)
{
ErrorDS errDS = new ErrorDS();
EmployeeHistDS employeeHistDS = new EmployeeHistDS();
DBConnectionDS connDS = new DBConnectionDS();
//extract dbconnection
connDS.Merge(inputDS.Tables[connDS.DBConnections.TableName], false, MissingSchemaAction.Error);
connDS.AcceptChanges();
//create command
NpgsqlCommand cmd = new NpgsqlCommand();
cmd.CommandText = "PRO_OCCUPATION_ADD";
cmd.CommandType = CommandType.StoredProcedure;
employeeHistDS.Merge(inputDS.Tables[employeeHistDS.Occupation.TableName], false, MissingSchemaAction.Error);
employeeHistDS.AcceptChanges();
foreach (EmployeeHistDS.OccupationRow row in employeeHistDS.Occupation)
{
long genPK = IDGenerator.GetNextGenericPK();
if (genPK == -1)
{
return UtilDL.GetDBOperationFailed();
}
cmd.Parameters.Add("p_OccupationId", genPK);
cmd.Parameters.Add("p_OccupationName", row.OccupationName);
if (!row.IsOccupationDetailsNull()) cmd.Parameters.Add("p_OccupationDetails", row.OccupationDetails);
else cmd.Parameters.Add("p_OccupationDetails", DBNull.Value);
bool bError = false;
int nRowAffected = -1;
nRowAffected = ADOController.Instance.ExecuteNonQuery(cmd, connDS.DBConnections[0].ConnectionID, ref bError);
if (bError == true)
{
ErrorDS.Error err = errDS.Errors.NewError();
err.ErrorCode = ErrorCode.ERR_DB_OPERATION_FAILED.ToString();
err.ErrorTier = ErrorTier.ERR_TIER_DL.ToString();
err.ErrorLevel = ErrorLevel.ERR_LEVEL_SEVER.ToString();
err.ErrorInfo1 = ActionID.ACTION_OCCUPATION_ADD.ToString();
errDS.Errors.AddError(err);
errDS.AcceptChanges();
return errDS;
}
}
errDS.Clear();
errDS.AcceptChanges();
return errDS;
}
public int ExecuteNonQuery(NpgsqlCommand cmdInsertUpdateDelete, int connectionID, ref bool bError)
{
CConnectionInfo connInfo = (CConnectionInfo)this.m_hashConn[connectionID];
if (connInfo == null)
{
AppLogger.LogWarning("Connection ID '" + connectionID + "' does not exist in connection pool.\nCould not execute ExecuteNonQuery method.");
bError = true;
return -1;
}
NpgsqlConnection conn = connInfo.m_Conn_PG;
NpgsqlTransaction tx = connInfo.m_Tx_PG;
cmdInsertUpdateDelete.Connection = conn;
cmdInsertUpdateDelete.Transaction = tx;
try
{
bError = false;
return cmdInsertUpdateDelete.ExecuteNonQuery();
}
catch (Exception ex)
{
WriteErrorLog(ex, connectionID);
AppLogger.LogFatal("Error executing ExecuteNonQuery method.\n" + cmdInsertUpdateDelete.CommandText, ex);
invalidateConnectionID(connectionID);
bError = true;
return -1;
}
finally
{
cmdInsertUpdateDelete.Transaction = null;
cmdInsertUpdateDelete.Connection = null;
}
}
Error Log
Please find the source code below. I am not getting any error but
_oracleCommand.ExecuteNonQuery() is going infinite - debugger is not moving to next line. I am able to update the data using Toad. I am stuck with this.
public void UpdateNewResignationRequestInSynergy(string _employeeno, string _comment, string _changeby, string _changeddt, string _reason)
{
int rowsaffected = 0;
string returnStatus;
string _synquery = "";
DateTime dtDate;
_employeeno = "774647";
_comment = "contract eand";
dtDate = DateTime.Parse(_changeddt, System.Globalization.CultureInfo.CreateSpecificCulture("en-CA"));
_oracleCommand = new OracleCommand(_synquery, _synergyDb);
_synergyDb.Open();
_oracleCommand.CommandText = string.Format(#"update wipinfo.fms_resignation set str_comments = 'contract end' where STR_CONTRACTOR_ID = 774647");
_oracleCommand.CommandType = CommandType.Text;
try
{
_oracleCommand.ExecuteNonQuery();
}
catch(Exception ex)
{
}
_synergyDb.Close();
}
You have to stop the oracle server and start again( do not restart it will not help you). I solved this problem by doing this process.
I am new to MySQL database, I am using Visual Studio C# to connect to my database. I have got a following select method. How can I run it to check if it is working?
EDITED The open and close connection methods
//Open connection to database
private bool OpenConnection()
{
try
{
// connection.open();
return true;
}
catch (MySqlException ex)
{
//When handling errors, your application's response based
//on the error number.
//The two most common error numbers when connecting are as follows:
//0: Cannot connect to server.
//1045: Invalid user name and/or password.
switch (ex.Number)
{
case 0:
MessageBox.Show("Cannot connect to server.");
break;
case 1045:
MessageBox.Show("Invalid username/password, please try again");
break;
}
return false;
}
}
//Close connection
private bool CloseConnection()
{
try
{
connection.Close();
return true;
}
catch (MySqlException ex)
{
MessageBox.Show(ex.Message);
return false;
}
}
Select method which is in the same class as the close and open connection as shown above
public List<string>[] Select()
{
string query = "SELECT * FROM Questions";
//Create a list to store the result
List<string>[] list = new List<string>[3];
list[0] = new List<string>();
list[1] = new List<string>();
list[2] = new List<string>();
list[3] = new List<string>();
list[4] = new List<string>();
list[5] = new List<string>();
list[6] = new List<string>();
list[7] = new List<string>();
//Open connection
if (this.OpenConnection() == true)
{
//Create Command
MySqlCommand cmd = new MySqlCommand(query, connection);
//Create a data reader and Execute the command
MySqlDataReader dataReader = cmd.ExecuteReader();
//Read the data and store them in the list
while (dataReader.Read())
{
list[0].Add(dataReader["id"] + "");
list[1].Add(dataReader["difficulty"] + "");
list[2].Add(dataReader["qustions"] + "");
list[3].Add(dataReader["c_answer"] + "");
list[4].Add(dataReader["choiceA"] + "");
list[5].Add(dataReader["choiceB"] + "");
list[6].Add(dataReader["choiceC"] + "");
list[7].Add(dataReader["choiceD"] + "");
}
//close Data Reader
dataReader.Close();
//close Connection
this.CloseConnection();
//return list to be displayed
return list;
}
else
{
return list;
}
}
This method is in a separate class which has got all the database connection settings. Now that I want to call this method from my main class to test it to see if it's working, how can I do this?
You should create an object instance of that DB class and then call the Select() method.
So, supposing that this DB class is named QuestionsDB you should write something like this:
QuestionDB questionDAL = new QuestionDB();
List<string>[] questions = questionDAL.Select();
However, before this, please correct this line
List<string>[] list = new List<string>[8]; // you need 8 lists for your db query
You could check if you have any record testing if the first list in your array list has more than zero elements.
if(questions[0].Count > 0)
... // you have read records.
However, said that, I will change your code adding a specific class for questions and using a list(of Question) instead of an array of list
So, for example, create a class like this
public class Question
{
public string ID;
public string Difficulty;
public string Question;
public string RightAnswer;
public string AnswerA;
public string AnswerB;
public string AnswerC;
public string AnswerD;
}
and change your select to return a List(of Question)
List<Question> list = new List<Question>;
......
while (dataReader.Read())
{
Question qst = new Question();
qst.ID = dataReader["id"] + "";
qst.Difficulty = dataReader["difficulty"] + "";
qst.Question = dataReader["qustions"] + "";
qst.RightAnswer = dataReader["c_answer"] + "";
qst.AnswerA = dataReader["choiceA"] + "";
qst.AnswerB = dataReader["choiceB"] + "";
qst.AnswerC = dataReader["choiceC"] + "";
qst.AnswerD = dataReader["choiceD"] + "";
list.Add(qst);
}
return list;
You can test whether the method works by writing a unit test for it. A good unit testing frame work is Nunit. Before you call this you must create and open a connection to the DB:
//Open connection
if (this.OpenConnection() == true)
{
as the other person said, you will want to fix the lists up.
I've been beating myself over the head with this for a few days now, searched up and down the internet. I know there is a lot I don't quite get about Com Interop but I've had success building and using simpler DLL's with Excel. Anyways to the point.
I get the above mentioned Error -2147467259 80004005 Automation Error Unspecified Error in Excel VBA when running the following code wrapped in a DLL.
[GuidAttribute("96BE21CD-887B-4770-9FAA-CF395375AEA9")]
[ComVisibleAttribute(true)]
interface QInterface
{
void ConnectionFill(string SQLQuery, string CONStr);
string QueryValue(int QueryKey);
string ConnectionValue(int ConnectionKey);
string outputFile { get; set; }
void ThreadTest();
}
[ClassInterfaceAttribute(ClassInterfaceType.None)]
[ProgIdAttribute("QueryThread")]
class QueryThread : QInterface
{
DataSet QueryReturn = new DataSet();
private ArrayList SQLList = new ArrayList();
private ArrayList ConList = new ArrayList();
private string OutputFile;
public void ConnectionFill(string SQLQuery, string CONStr)
{
SQLList.Add(SQLQuery);
ConList.Add(CONStr);
}
public string QueryValue(int QueryKey)
{
return SQLList[QueryKey].ToString();
}
public string ConnectionValue(int ConnectionKey)
{
return ConList[ConnectionKey].ToString();
}
public string outputFile
{
set { OutputFile = value; }
get { return OutputFile; }
}
public void ThreadTest()
{
int i = 0;
i = SQLList.Count;
Thread[] myThreads;
myThreads = new Thread[i];
for (int t = 0; t != i; t++)
{
myThreads[t] = new Thread(() => ThreadRun(SQLList[t].ToString(), ConList[t].ToString()));
myThreads[t].Name = "Thread " + t;
myThreads[t].IsBackground = true;
myThreads[t].Start();
Thread.Sleep(600);
if (t > 9)
{
myThreads[t - 9].Join();
}
}
for (int t = 0; t != i; t++)
{
while (myThreads[t].IsAlive)
{
Thread.Sleep(20);
}
}
TextWriter tw = new StreamWriter(OutputFile);
for (int t = 0; t < QueryReturn.Tables.Count; t++)
{
DataTableReader DR = QueryReturn.Tables[t].CreateDataReader();
while (DR.Read())
{
tw.WriteLine("{0} : {1}", DR.GetValue(0), DR.GetValue(1));
}
}
tw.Close();
QueryReturn.Dispose();
}
private void ThreadRun(string SQLString, string ConString)
{
try
{
OleDbConnection DBCon = new OleDbConnection(ConString);
DBCon.Open();
Thread.Sleep(200);
OleDbCommand DBCmd = new OleDbCommand(SQLString, DBCon);
OleDbDataAdapter DataAdapter = new OleDbDataAdapter(DBCmd);
Thread.Sleep(200);
DataAdapter.Fill(QueryReturn, Thread.CurrentThread.Name.ToString());
DBCon.Close();
DataAdapter.Dispose();
DBCon.Dispose();
DBCmd.Dispose();
}
finally
{
}
}
}
using this VBA code...
Sub test()
Dim QT As New QueryThreading.QueryThread
Dim MyResults As String
Dim outputfile As String
Dim InputStuff(1, 1) As String
InputStuff(0, 0) = "Select DISTINCT * From TrackingData;"
InputStuff(0, 1) = "Provider =Microsoft.ACE.OLEDB.12.0;Data Source =C:\Users\Nick\Desktop\test.accdb; Persist Security Info =False;Connection Timeout=7;"
InputStuff(1, 0) = "Select DISTINCT * From TrackingData;"
InputStuff(1, 1) = "Provider =Microsoft.ACE.OLEDB.12.0;Data Source =C:\Users\Nick\Desktop\test2.accdb; Persist Security Info =False;Connection Timeout=7;"
QT.ConnectionFill InputStuff(0, 0), InputStuff(0, 1)
QT.ConnectionFill InputStuff(1, 0), InputStuff(1, 1)
outputfile = "C:\Users\Nick\Desktop\testrun.txt"
QT.outputfile = outputfile
QT.ThreadTest
End Sub
It runs fine is a pure C# console application. Works perfect and quick with no problems. But via VBA I get the error.
I'm assuming it's something to do with the calling of the access databases in multiple threads. I know there is a lot of junk code in there, it's not optimized of course I'm still in the "playing around" phase.
I've used RegAsm and enabled com interop and all such stuff, I can read back from the returns just fine. So I know the DLL is working right, just when I fill the threads and run "ThreadTest()" I get the automation error.
If I run it a second time Excel locks up.
Any help would be greatly appreciated.
You are calling QT.DictionaryFill though I can't seem to find a corresponding method in you C# code... what happens if you change the call to QT.ConnectionFill instead ?
Another point: IIRC then VBA runs the object in STA - not sure whether MTA is supported though
EDIT:
According to this rather old post VBA does not support multi-threading...