I'm working on a little C# WPF application, in which I need to have an opportunity to create a PLAN_TABLE (I work with Oracle), but the name of the table can be customized.
So, user can type in his PLAN_TABLE name and if it doesn't exist, It will be created automatically.
Here is what I have for creating a plan table:
private void CreatePlanTable()
{
using (OracleConnection connection = new OracleConnection(ConnectionString))
{
connection.Open();
var cmd = connection.CreateCommand();
cmd.CommandText = SqlUtils.GetPlanTableScript(planTable);
cmd.ExecuteNonQuery(); //Here is where I get ORA-00922 exception
}
}
SqlUtils.GetPlanTableScript() returns a string with create plan_table command, here it is:
public static string GetPlanTableScript(string planTableName)
{
return
new StringBuilder()
.AppendLine($"create table \"{planTableName}\" sharing=none (")
.AppendLine("statement_id varchar2(30),")
.AppendLine("plan_id number,")
.AppendLine("timestamp date,")
.AppendLine("remarks varchar2(4000),")
.AppendLine("operation varchar2(30),")
.AppendLine("options varchar2(255),")
.AppendLine("object_node varchar2(128),")
.AppendLine("object_owner varchar2(128),")
.AppendLine("object_name varchar2(128),")
.AppendLine("object_alias varchar2(261),")
.AppendLine("object_instance numeric,")
.AppendLine("object_type varchar2(30),")
.AppendLine("optimizer varchar2(255),")
.AppendLine("search_columns number,")
.AppendLine("id numeric,")
.AppendLine("parent_id numeric,")
.AppendLine("depth numeric,")
.AppendLine("position numeric,")
.AppendLine("cost numeric,")
.AppendLine("cardinality numeric,")
.AppendLine("bytes numeric,")
.AppendLine("other_tag varchar2(255),")
.AppendLine("partition_start varchar2(255),")
.AppendLine("partition_stop varchar2(255),")
.AppendLine("partition_id numeric,")
.AppendLine("other long,")
.AppendLine("distribution varchar2(30),")
.AppendLine("cpu_cost numeric,")
.AppendLine("io_cost numeric,")
.AppendLine("temp_space numeric,")
.AppendLine("access_predicates varchar2(4000),")
.AppendLine("filter_predicates varchar2(4000),")
.AppendLine("projection varchar2(4000),")
.AppendLine("time numeric,")
.AppendLine("qblock_name varchar2(128),")
.AppendLine("other_xml clob")
.AppendLine(");")
.ToString();
}
This one is looking pretty stupid and I'd like to ask: if there's a better way of creating the table?
I call CreatePlanTable() from the other method, that checks the connection and existence of the plan table:
public string Test(bool createPlanTable)
{
try
{
using (OracleConnection connection = new OracleConnection(ConnectionString))
{
connection.Open();
var cmd = connection.CreateCommand();
cmd.CommandText = "select 1 from all_tables where table_name = :name";
var p = cmd.Parameters;
p.Add("name", planTable.ToUpper());
var reader = cmd.ExecuteReader();
isValid = reader.HasRows;
if (isValid)
{
return "Success";
}
else
{
if (createPlanTable)
{
CreatePlanTable();
isValid = true;
return "Success";
}
return $"There is no {planTable} exists";
}
}
}
catch (Exception e)
{
return e.Message;
}
}
So, everything works fine, when user wants to work with an existing table, but when they want me to create it, the ORA-00922 is threw. How do I avoid this exception?
In fact, you don't need own PLAN_TABLE anymore: for a long time(at least since 10.1) Oracle creates a global temporary table SYS.PLAN_TABLE$(granted to public) and a public synonym PLAN_TABLE to it by default during db creation.
I just needed to remove ; from the last string of create table statement:
.AppendLine(")")
The question about any possible way of creating the table more elegantly is still open.
Related
I am testing Oracle database. I wrote some code but my datareader has no rows, why? My "carros" table has data and I am selecting all of them but I am getting an empty result set it seems.
string constr = "Data Source=localhost:1521/XE;User Id=System;Password=password;";
OracleConnection con = new OracleConnection(constr);
OracleCommand oracleCommand = new OracleCommand();
oracleCommand.Connection = con;
oracleCommand.CommandText = "select preco from carro";
con.Open();
OracleDataReader oracleDataReader = oracleCommand.ExecuteReader();
string resultado = String.Empty;
//My test, I got hasRows as false
if (oracleDataReader.HasRows == false)
{
resultado = "no results";
}
//never enters this loop.
while (oracleDataReader.Read())
{
resultado += (string)oracleDataReader["preco"];
}
// Close and Dispose OracleConnection
con.Close();
con.Dispose();
return resultado;
If HasRows is false after ExecuteReader, the problem is simply that the query is returning no rows so Read will return false as well. Perhaps the someValue variable is not set correctly.
From you description, it seems that your table is named carros while your query has used carro. Try to use
oracleCommand.CommandText = "select preco from carros";
A path towards solving this can include a scattering of logging commands that write to the file system.
System.IO.File.AppendAllText(#"\\server\\C\\log.txt", "put error message here" + "\r\n");
Or for printing a variable:
System.IO.File.AppendAllText(#"\\server\\C\\log.txt", ex.ToString() + "\r\n");
Pepper variations of this command throughout your script to see where failures are happening and to validate variable values.
I'm sort of having a hard time with this one. Well ok, I have two different solutions (solution1 has a WebApplication Project; solution2 has a Website Project). Inside the two solutions, there's a WCF service structure. I have the exact same code in both services (in their respective solutions). My code compiles just fine. From the service I do a simple call to a procedure that returns a cursor. When I execute the service from the WebApplication it works just fine; when I do the same from the Website I get error: "wrong number or types of arguments". They both call the same procedure, in the same DB. And I triple check my code, and is the same in both services. Any ideas or suggestions? My code is as follows in both solutions:
Service.cs
public List<A1001310> SearchClient_A1001310()
{
DataTable dataTable = new DataTable();
dataTable = DataManager.SearchClient();
List<A1001310> list = new List<A1001310>();
list = (from DataRow dr in dataTable.Rows
select new A1001310()
{
Id = Convert.ToInt32(dr["CLIENT_ID"]),
//ClientName = dr["NOM_CLIENTE"].ToString()
}).ToList();
return list;
}
DataManager.cs
public static DataTable SearchClient()
{
try
{
using (OleDbCommand cmd = new OleDbCommand(packetName + ".select_A1001310"))
{
cmd.CommandType = CommandType.StoredProcedure;
SqlManager sqlManager = new SqlManager();
return sqlManager.GetDataTable(cmd);
}
}
catch (Exception ex)
{
//TODO; Handle exception
}
return null;
}
The call to DataTable is:
public DataTable GetDataTable(OleDbCommand cmd)
{
using (DataSet ds = GetDataSet(cmd))
{
return ((ds != null && ds.Tables.Count > 0) ? ds.Tables[0] : null);
}
}
public DataSet GetDataSet(OleDbCommand cmd)
{
using (DataSet ds = new DataSet())
{
this.ConvertToNullBlankParameters(cmd);
using (OleDbConnection conn = new OleDbConnection(cmd.Connection == null ? _dbConnection : cmd.Connection.ConnectionString))
{
cmd.Connection = conn;
cmd.CommandTimeout = _connTimeout;
conn.Open();
//cmd.ExecuteScalar();
using (OleDbDataAdapter da = new OleDbDataAdapter(cmd))
da.Fill(ds);
}
return ds;
}
}
The procedure is as follow:
PROCEDURE select_A1001310(io_cursor OUT lcursor_data)
AS
BEGIN
OPEN io_cursor FOR
--
SELECT client_id
FROM a1001310
WHERE status = 'A'
--
EXCEPTION
WHEN OTHERS THEN
IF io_cursor%ISOPEN THEN
CLOSE io_cursor;
END IF;
--REVIRE: EXCEPTION HANDLER
END select_A1001310;
So if it helps anyone, I resolved my issue by specifying the OUT parameter declared in the procedure. This resulted in me changing from Oledb to OracleClient as follow:
public static DataTable SearchClient()
{
string connection = ConfigurationManager.ConnectionStrings["DBConnection_Oracle"].ToString();
string procedure = packetName + ".p_search_client";
OracleParameter[] parameters = new OracleParameter[1];
parameters[0] = new OracleParameter("io_cursor", OracleType.Cursor, 4000, ParameterDirection.Output, true, 0, 0, "", DataRowVersion.Current, String.Empty);
DataTable dt = new DataTable();
dt = DataManager_Oracle.GetDataTable_(connection, procedure, parameters);
return dt;
}
It seems that on the Website environment it didn't like leaving out the OUT parameter; whereas on the WebApplication I did not specify it, and it worked just fine... If some one know the why , PLEASE let me know :)
I´m trying to use dapper with Oracle (ODP.NET) and I would like to use the "QueryMultiple" functionality.
Passing this string to the QueryMultiple method:
var query = "Select CUST_ID CustId from Customer_info WHERE CUST_ID=:custId;" +
"Select CUST_ID CustId from BCR WHERE CUST_ID=:custId";
I´m getting a ORA-00911: invalid character error
Is there any way to do this or it´s not possible?
Tks
The OP has probably long since solved the issue by now, but as of the time of writing, this question has only one answer and it doesn't really solve the problem of using Dapper's QueryMultiple() method with Oracle. As #Kamolas81 correctly states, by using the syntax from the official examples, one will indeed get the ORA-00933: SQL command not properly ended error message. I spent a while searching for some sort of documentation about how to do QueryMultiple() with Oracle, but I was surprised that there wasn't really one place that had an answer. I would have thought this to be a fairly common task. I thought that I'd post an answer here to save me :) someone some time in the future just in case anybody happens to have this same problem.
Dapper seems to just pass the SQL command straight along to ADO.NET and whatever db provider is executing the command. In the syntax from the examples, where each command is separated by a line break, SQL server will interpret that as multiple queries to run against the database and it will run each of the queries and return the results into separate outputs. I'm not an ADO.NET expert, so I might be messing up the terminology, but the end effect is that Dapper gets the multiple query outputs and then works its magic.
Oracle, though, doesn't recognize the multiple queries; it thinks that the SQL command is malformed and returns the ORA-00933 message. The solution is to use cursors and return the output in a DynamicParameters collection. For example, whereas the SQL Server version would look like this:
var sql =
#"
select * from Customers where CustomerId = #id
select * from Orders where CustomerId = #id
select * from Returns where CustomerId = #id";
the Oracle version of the query would need to look like this:
var sql = "BEGIN OPEN :rslt1 FOR SELECT * FROM customers WHERE customerid = :id; " +
"OPEN :rslt2 FOR SELECT * FROM orders WHERE customerid = :id; " +
"OPEN :rslt3 FOR SELECT * FROM returns Where customerid = :id; " +
"END;";
For queries run against SQL Server, Dapper can handle it from there. However, because we're returning the result sets into cursor parameters, we'll need to use an IDynamicParameters collection to specify parameters for the command. To add an extra wrinkle, the normal DynamicParameters.Add() method in Dapper uses a System.Data.DbType for the optional dbType parameter, but the cursor parameters for the query need to be of type Oracle.ManagedDataAccess.Client.OracleDbType.RefCursor. To solve this, I used the solution which #Daniel Smith proposed in this answer and created a custom implementation of the IDynamicParameters interface:
using Dapper;
using Oracle.ManagedDataAccess.Client;
using System.Data;
public class OracleDynamicParameters : SqlMapper.IDynamicParameters
{
private readonly DynamicParameters dynamicParameters = new DynamicParameters();
private readonly List<OracleParameter> oracleParameters = new List<OracleParameter>();
public void Add(string name, OracleDbType oracleDbType, ParameterDirection direction, object value = null, int? size = null)
{
OracleParameter oracleParameter;
if (size.HasValue)
{
oracleParameter = new OracleParameter(name, oracleDbType, size.Value, value, direction);
}
else
{
oracleParameter = new OracleParameter(name, oracleDbType, value, direction);
}
oracleParameters.Add(oracleParameter);
}
public void Add(string name, OracleDbType oracleDbType, ParameterDirection direction)
{
var oracleParameter = new OracleParameter(name, oracleDbType, direction);
oracleParameters.Add(oracleParameter);
}
public void AddParameters(IDbCommand command, SqlMapper.Identity identity)
{
((SqlMapper.IDynamicParameters)dynamicParameters).AddParameters(command, identity);
var oracleCommand = command as OracleCommand;
if (oracleCommand != null)
{
oracleCommand.Parameters.AddRange(oracleParameters.ToArray());
}
}
}
So all of the code together goes something like this:
using Dapper;
using Oracle.ManagedDataAccess.Client;
using System.Data;
int selectedId = 1;
var sql = "BEGIN OPEN :rslt1 FOR SELECT * FROM customers WHERE customerid = :id; " +
"OPEN :rslt2 FOR SELECT * FROM orders WHERE customerid = :id; " +
"OPEN :rslt3 FOR SELECT * FROM returns Where customerid = :id; " +
"END;";
OracleDynamicParameters dynParams = new OracleDynamicParameters();
dynParams.Add(":rslt1", OracleDbType.RefCursor, ParameterDirection.Output);
dynParams.Add(":rslt2", OracleDbType.RefCursor, ParameterDirection.Output);
dynParams.Add(":rslt3", OracleDbType.RefCursor, ParameterDirection.Output);
dynParams.Add(":id", OracleDbType.Int32, ParameterDirection.Input, selectedId);
using (IDbConnection dbConn = new OracleConnection("<conn string here>"))
{
dbConn.Open();
var multi = dbConn.QueryMultiple(sql, param: dynParams);
var customer = multi.Read<Customer>().Single();
var orders = multi.Read<Order>().ToList();
var returns = multi.Read<Return>().ToList();
...
dbConn.Close();
}
Building on greyseal96's helpful answer, I created this implementation of IDynamicParameters:
public class OracleDynamicParameters : SqlMapper.IDynamicParameters
{
private readonly DynamicParameters dynamicParameters;
private readonly List<OracleParameter> oracleParameters = new List<OracleParameter>();
public OracleDynamicParameters(params string[] refCursorNames) {
dynamicParameters = new DynamicParameters();
AddRefCursorParameters(refCursorNames);
}
public OracleDynamicParameters(object template, params string[] refCursorNames) {
dynamicParameters = new DynamicParameters(template);
AddRefCursorParameters(refCursorNames);
}
private void AddRefCursorParameters(params string[] refCursorNames)
{
foreach (string refCursorName in refCursorNames)
{
var oracleParameter = new OracleParameter(refCursorName, OracleDbType.RefCursor, ParameterDirection.Output);
oracleParameters.Add(oracleParameter);
}
}
public void AddParameters(IDbCommand command, SqlMapper.Identity identity)
{
((SqlMapper.IDynamicParameters)dynamicParameters).AddParameters(command, identity);
var oracleCommand = command as OracleCommand;
if (oracleCommand != null)
{
oracleCommand.Parameters.AddRange(oracleParameters.ToArray());
}
}
}
Assuming the same query, it can be used so:
var queryParams = new { id };
string[] refCursorNames = { "rslt1", "rslt2", "rslt3" };
var dynParams = new OracleDynamicParameters(queryParams, refCursorNames);
...
var multi = dbConn.QueryMultiple(sql, param: dynParams);
I suspect this is two or three separate things:
Your first query should not have a semi-colon
There is no new-line character between the queries
The usage notes imply that the bind character is # not : (no idea if this depends on the RDBMS being used).
If you look at the Dapper Google Code page the example given for QueryMultiple() is:
var sql =
#"
select * from Customers where CustomerId = #id
select * from Orders where CustomerId = #id
select * from Returns where CustomerId = #id";
using (var multi = connection.QueryMultiple(sql, new {id=selectedId}))
{
var customer = multi.Read<Customer>().Single();
var orders = multi.Read<Order>().ToList();
var returns = multi.Read<Return>().ToList();
...
}
Remove the semi-colon; add a new line and if you still have issues change the bind character.
I have a stored procedure as
pr___GetArchiveData
Select * from TABLE1
SELECT * FROM TABLE2
SELECT * FROM TABLE 3
I want to get this result set into a dataset. Or access the values of three select queries!!
I have a DBML file in which when i drag and drop the stored procedure generates a code as follows:-
global::System.Data.Linq.Mapping.FunctionAttribute(Name="dbo.pr___GetArchiveData")]
public ISingleResult<pr___GetArchiveDataResult> pr___GetArchiveData([global::System.Data.Linq.Mapping.ParameterAttribute(DbType="UniqueIdentifier")] System.Nullable<System.Guid> projectID)
{
IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), projectID);
return ((ISingleResult<pr__Project_pr___GetArchiveData>)(result.ReturnValue));
}
In the code MVC3 Architecture + LINQ i have written a code to get the result set as follows :-
using (HBDataContext hb = new HBDataContext())
{
System.Data.DataSet ds = new System.Data.DataSet();
String connString = connString;
var conn = new System.Data.SqlClient.SqlConnection(connString);
var cmd = conn.CreateCommand();
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.CommandText = "pr__GetArchiveData";
cmd.Connection.Open();
var mReader = cmd.ExecuteReader(System.Data.CommandBehavior.SequentialAccess);
//var reader = cmd.ExecuteReader();
//using (System.Data.SqlClient.SqlDataReader mReader = cmd.ExecuteReader())
//{
// while (mReader.Read())
//{
// mReader.Read();
var tbl1 = hb.Translate<tbl1 >(mReader).ToList();
// mReader = cmd.ExecuteReader();
mReader.NextResult();
var tbl2 = hb.Translate<tbl2 >(mReader).ToList();
mReader.NextResult();
var tbl3 = hb.Translate<tbl3>(mReader).ToList();
// }
// }
}
But while running it throws error as -
"Invalid attempt to call NextResult when reader is closed."
I am not sure where i am wrong!!
I have tried using it as while
(mReader.Read())
Kindly suggest!!!!
The code gen for ISingleResult won't provide multiple result sets
Try adding your own IMultipleResults wrapper - see the tutorial in http://blogs.msdn.com/b/dditweb/archive/2008/05/06/linq-to-sql-and-multiple-result-sets-in-stored-procedures.aspx
In .dbml file for the stored procedure the code will be like
[global::System.Data.Linq.Mapping.FunctionAttribute(Name="dbo.pr__Home_GetArchiveData")]
public ISingleResult<pr__Home_GetArchiveData> pr__Home_GetArchiveData([global::System.Data.Linq.Mapping.ParameterAttribute(Name="AlbumID", DbType="UniqueIdentifier")] System.Nullable<System.Guid> albumID)
{
IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), albumID);
return ((ISingleResult<pr__Album_GetAlbumsFilesResult>)(result.ReturnValue));
}
Replace it with IMultipleResult as below `
[global::System.Data.Linq.Mapping.FunctionAttribute(Name = "dbo.pr__Home_GetArchiveData")]
[ResultType(typeof(tbl1))]
[ResultType(typeof(tbl2))]
[ResultType(typeof(tbl3))]
[ResultType(typeof(tbl4))]
public IMultipleResults pr__Home_GetArchiveData([global::System.Data.Linq.Mapping.ParameterAttribute(Name = "HOMEID", DbType = "UniqueIdentifier")] System.Nullable hOMEID)
{
IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), hOMEID);
return ((IMultipleResults)result.ReturnValue);
}
in the code .
using (HBDataContext hb = new HBDataContext())
{
using (System.Data.Linq.IMultipleResults _results = hb.pr__Home_GetArchiveData(model.HomeID))
{
List<tbl1> _tbl1= _results.GetResult<tbl1>().ToList();
List<tbl2> _tbl2= _results.GetResult<tbl2>().ToList();
List<tbl3> _tbl3= _results.GetResult<tbl3>().ToList();
List<tbl4> _tbl4= _results.GetResult<tbl4>().ToList();}}
You will get the values of the Select queries from theStoredProcedure ...
I am inserting values in to Database from a Webform using ADO.NET, C#. DB I am using is Oracle Database. Values are not being inserted and the program gets struck at the cmd.ExecuteNonquery()
Here is my Code below, Please let me know If I am doing any mistake.. I am using some Static Methods will that be any problem ?..
public Boolean AddDivCo(Int32 UserNo,String ID, String Role, String DivName )
{
Boolean ret = false;
OracleCommand cmd = new OracleCommand();
OracleConnection conn = new OracleConnection();
int i = 0;
try
{
conn.ConnectionString = ConfigurationManager.ConnectionStrings["Conn_RIS"].ConnectionString;
conn.Open();
cmd.Connection = conn;
String mySQL = "INSERT INTO R4CAD_ADMIN (AdminUserNo, AdminID, AdminRole, AdminDivName)VALUES(:AdminUserNo,:AdminID,:AdminRole,:DivName)";
OracleParameter p1 = new OracleParameter("AdminUserNo", OracleType.Number);
p1.Value = UserNo;
cmd.Parameters.Add(p1);
OracleParameter p2 = new OracleParameter("AdminID", OracleType.VarChar);
p2.Value = ID;
cmd.Parameters.Add(p2);
OracleParameter p3 = new OracleParameter("AdminRole", OracleType.VarChar);
p3.Value = Role;
cmd.Parameters.Add(p3);
OracleParameter p4 = new OracleParameter("DivName", OracleType.VarChar);
p4.Value = DivName;
cmd.Parameters.Add(p4);
cmd.CommandText = mySQL;
i = cmd.ExecuteNonQuery();
if (i != 0)
{
ret = true;
}
else
{
ret = false;
}
}
catch (Exception err)
{
Console.WriteLine(err.Message.ToString());
}
finally
{
cmd.Dispose();
//cmd = null;
//conn = null;
conn.Close();
}
return ret;
}
Is there a primary key defined on this table? If so, then my guess is that you have another session that already has inserted a record with this key, but has not yet terminated the transaction with a commit or rollback. I don't see a commit as part of your code - I assume you're doing that somewhere else?
Execute your code above once more, and while it's hung run the following query from another session:
SELECT
(SELECT username FROM v$session WHERE sid=a.sid) blocker,
a.sid,
' is blocking ',
(SELECT username FROM v$session WHERE sid=b.sid) blockee,
b.sid
FROM v$lock a JOIN v$lock b ON (a.id1 = b.id1 AND a.id2 = b.id2)
WHERE a.block = 1
AND b.request > 0;
This should tell you if you're being blocked by another session and what the SID is of that session.