In this way, I retrieve data using the stored procedure:
var returnParameter = new SqlParameter("#RV", SqlDbType.Int) { Direction = ParameterDirection.Output };
var xmlParameter = new SqlParameter("#XML", XML) { DbType = DbType.Xml };
var familySentParameter = new SqlParameter("#FamilySent", SqlDbType.Int) { Direction = ParameterDirection.Output };
//var resultParameter = new SqlParameter("Result", SqlDbType.Text) { Direction = ParameterDirection.ReturnValue };
var sql = "exec #RV = uspConsumeSomething #XML, #FamilySent OUT";
var result = myContext.Database.ExecuteSqlCommand(sql, returnParameter, xmlParameter, familySentParameter);
RV = (int)returnParameter.Value;
Below the fragment of the stored procedure (termination of the procedure):
BEGIN CATCH
SELECT ERROR_MESSAGE();
IF ##TRANCOUNT > 0 ROLLBACK TRAN ENROLL;
SELECT * FROM #tblResult;
RETURN -400
END CATCH
SELECT Result FROM #tblResult;
RETURN 0 --ALL OK
END
How to obtain in C# data obtained from:
SELECT * FROM #tblResult;
SELECT Result FROM #tblResult;
They are not needed for the operation of the procedure but contain information about the type of error that arises in the stored procedure.
The use of Return in a stored procedure is supported by #RV
EDIT:
I'm trying a different approach but I still do not know how to get the values from SELECT here
DbConnection connection = myContext.Database.GetDbConnection();
using (DbCommand cmd = connection.CreateCommand())
{
cmd.CommandText = sql;
cmd.Parameters.AddRange(new[] { xmlParameter, returnParameter, familySentParameter });
if (connection.State.Equals(ConnectionState.Closed)) connection.Open();
using (var reader = cmd.ExecuteReader())
{
var entities = new List<string>();
while (reader.Read())
{
Debug.WriteLine("Reader: " + reader.GetValue(0));
}
}
}
As a result, there are 3 SELECT. How do you get access to 2 and 3?
Related
I have parameterized stored procedure (tested and works) in mysql.
I wanted to replace my long select statement with stored procedure in my code.
With select statement it worked but now I am not able to run it with sp (commented string req).
My stored procedure:
CREATE DEFINER=`root`#`%` PROCEDURE `GetProjectVM`(
IN projectName NVARCHAR(255)
)
BEGIN
select VirtualMachines.Name,
VirtualMachines.IpAddress,
VirtualMachines.DiskSize,
VirtualMachines.CPU,
VirtualMachines.Ram,
VirtualMachines.ImageUrl,
VirtualMachines.Role,
Hypervisors.Name as Hypervisor,
Managements.Netmask,
Managements.Gateway from VirtualMachines inner join Projects
on Projects.id = VirtualMachines.ProjectId inner join Hypervisors
on Hypervisors.HypervisorId = VirtualMachines.HypervisorId inner join Managements
on VirtualMachines.ManagementId = Managements.Id
where Projects.Name = projectName;
END
My code looks like:
private string _dbUrl;
private MySqlConnection _cnn;
public Dbmanagement(string dbUrl)
{
this._dbUrl = dbUrl;
this._cnn = new MySqlConnection(this._dbUrl);
}
private MySqlDataReader ExecuteMysqlCommand(string cmd)
{
_cnn.Open();
var result = new MySqlCommand(cmd, _cnn);
return result.ExecuteReader();
}
public IEnumerable<VM> GetProjectVM(string projectName)
{
var vmList = new List<VM>();
//string req =
// $"select VirtualMachines.Name,VirtualMachines.IpAddress,VirtualMachines.DiskSize,VirtualMachines.CPU,VirtualMachines.Ram,VirtualMachines.ImageUrl,VirtualMachines.Role,Hypervisors.Name as Hypervisor,Managements.Netmask,Managements.Gateway from VirtualMachines inner join Projects on Projects.id = VirtualMachines.ProjectId inner join Hypervisors on Hypervisors.HypervisorId = VirtualMachines.HypervisorId inner join Managements on VirtualMachines.ManagementId = Managements.Id where Projects.Name = \"{projectName}\" ;";
MySqlCommand cmd = new MySqlCommand("GetProjectVM", _cnn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#projectName", projectName);
var req = cmd;
var rd = ExecuteMysqlCommand(req.ToString());
while (rd.Read())
{
vmList.Add(new VM(rd));
}
_cnn.Close();
return vmList;
}
The problem - or at least the first problem - is how you're executing the command.
Calling ToString on a MySqlCommand is very unlikely to be able to convey all the relevant information. You're losing parameterization, command type etc.
Change this:
var req = cmd;
var rd = ExecuteMysqlCommand(req.ToString());
to
_cnn.Open();
var rd = req.ExecuteReader();
I'd also suggest not having a single MySqlConnection, but just constructing one when you need to, and letting the connection pool manage making that efficient. So something like:
using (var connection = new MySqlConnection(_dbUrl))
{
connection.Open();
using (var cmd = new MySqlCommand("GetProjectVM", connection))
{
cmd.CommandType = CommandType.StoredProcedure;
// Avoid AddWithValue where possible, to avoid conversion issues.
cmd.Parameters.Add("#projectName", MySqlDbType. VarChar).Value = projectName;
using (var reader = cmd.ExecuteReader())
{
var list = new List<VM>();
while (reader.Read())
{
list.Add(new VM(reader));
}
return list;
}
}
}
This is my stored procedure:
ALTER PROCEDURE [dbo].[GetAllOrdersHistoryForNofshonit]
#MemberId VARCHAR(50)
AS
BEGIN
SET NOCOUNT ON;
SELECT *
INTO #Temptable
FROM AttractionsOrdersHistory
UNION ALL
SELECT *
FROM MoviesOrdersHistory
UNION ALL
SELECT *
FROM SpaOrdersHistory
UNION ALL
SELECT *
FROM TzimersOrdersHistory
WHERE MemberId = #MemberId
END
I have this procedure that returns results from 4 views into a temp table.
How can I see the result from temp table?
How can I get the results into my existing code?
public async Task<bool> MemberOrderDateEXE(string memberId)
{
if (memberId != null)
{
using (var context = ContextManager.ClubContext())
{
var connectionString = ContextManager.ClubContext()._connectionString;
var connection = new SqlConnection(connectionString);
var command = new SqlCommand($"GetAllOrdersHistoryForNofshonit '{memberId}'", connection);
connection.Open();
var reader = command.ExecuteReader();
reader.Read();
var retValue = reader.GetValue(0);
if(retValue != null)
....
}
}
For the past few hours I am trying to do the simplest of the simple things (at least for SQL SERVER) in an Oracle Data Base, through a .NET application using ADO.NET. It seems impossible.
For SQL SERVER I would do this simple task, supposing I have an SqlCommand object
comm.CommandText = #"
DECLARE #next_id INT
SET #next_id = (SELECT ISNULL(MAX(id_col),0) FROM TABLE_1) + 1
INSERT INTO TABLE_1 (id_col, col1, ...) VALUES (#next_id, val1, ...)
SELECT #next_id";
int id = Convert.ToInt32(comm.ExecuteScalar());
That would insert a new record to table TABLE_1 and I would take back the new id in the "id" variable in c# code.
Four simple steps
Declare a variable
Set it to the next available id
Insert the record with the new variable
Return the variable's value
Ok I managed to declare the variable in Oracle query. Also I (think) I managed to give it a value (With SELECT INTO)
How can I get back this variable's value back in c#? How can i SELECT a variable's value to the output stream in Oracle SQL?
I know that there are better ways to achieve getting back an identity column, but that's not the question here. It could be a totally different example. The question is simple.: I have declared a variable inside an oracle sql script that will be executed from within .net app. How can i get the variable's value back to c#, from an oracle query? What is the above code's equivalent with Oracle ADO.NET query?
You'll want to use ODP.NET (Oracle's Oracle Data Access Components):
An example of this is below. Note that in ODP.NET, you can establish a parameters direction (input, inputoutput, output, returnvalue) to correspond with the parameters of the procedure or statement you're running. In this example, I'm grabbing a returnvalue, which is an ID that is generated by the db via a sequence and trigger (its created automagically as far as the .NET app is concerned):
int event_id = 0;
using (OracleConnection oraConn = new OracleConnection(connStr))
{
string cmdText = #"insert into EVENT
(EVENT_NAME, EVENT_DESC)
values
(:EVENT_NAME, :EVENT_DESC)
RETURNING EVENT_ID INTO :EVENT_ID
";
using (OracleCommand cmd = new OracleCommand(cmdText, oraConn))
{
oraConn.Open();
OracleTransaction trans = oraConn.BeginTransaction();
try
{
OracleParameter prm = new OracleParameter();
cmd.BindByName = true;
prm = new OracleParameter("EVENT_NAME", OracleDbType.Varchar2);
prm.Value = "SOME NAME"; cmd.Parameters.Add(prm);
prm = new OracleParameter("EVENT_DESC", OracleDbType.Varchar2);
prm.Value = "SOME DESC"; cmd.Parameters.Add(prm);
prm = new OracleParameter( "EVENT_ID"
, OracleDbType.Int32
, ParameterDirection.ReturnValue);
cmd.Parameters.Add(prm);
cmd.ExecuteNonQuery();
trans.Commit();
// return value
event_id = ConvertFromDB<int>(cmd.Parameters["EVENT_ID"].Value);
}
catch
{
trans.Rollback();
throw;
}
finally
{
trans.Dispose();
}
oraConn.Close();
}
}
The ConvertFromDB is just a generic to cast the return value to its .NET equivalent (an int in this case).
Hope that helps.
EDIT:
You can easily bind an array of values (and retrieve an array of return values) in ODP.NET:
using (OracleConnection oraConn = new OracleConnection(connStr))
{
string cmdText = #"insert into TEST_EVENT
(EVENT_NAME, EVENT_DESC)
values
(:EVENT_NAME, :EVENT_DESC)
RETURNING EVENT_ID INTO :EVENT_ID
";
using (OracleCommand cmd = new OracleCommand(cmdText, oraConn))
{
oraConn.Open();
OracleTransaction trans = oraConn.BeginTransaction();
try
{
string[] event_names = new string[2];
string[] event_descs = new string[2];
int[] event_ids = new int[2];
event_names[0] = "Event1";
event_descs[0] = "Desc1";
event_names[1] = "Event2";
event_descs[1] = "Desc2";
OracleParameter prm = new OracleParameter();
cmd.Parameters.Clear();
cmd.ArrayBindCount = 2;
cmd.BindByName = true;
prm = new OracleParameter("EVENT_NAME", OracleDbType.Varchar2);
prm.Value = event_names; cmd.Parameters.Add(prm);
prm = new OracleParameter("EVENT_DESC", OracleDbType.Varchar2);
prm.Value = event_descs; cmd.Parameters.Add(prm);
prm = new OracleParameter( "EVENT_ID"
, OracleDbType.Int32
, ParameterDirection.ReturnValue);
cmd.Parameters.Add(prm);
cmd.ExecuteNonQuery();
trans.Commit();
// get return values
event_ids = (int[])(cmd.Parameters["EVENT_ID"].Value);
}
catch
{
trans.Rollback();
throw;
}
finally
{
trans.Dispose();
}
oraConn.Close();
}
}
I am tryign to do bulk inserts/updates efficiently from c# code to oracle database.
If I done it by statement, then it doesn’t take much time.
I am using the ODP.NET
Currently insert of 6000 records via below stored proc is taking 15 mins.
I have to use this stored proc, because it generates unique user_id.
Is this proc doing auto-commit ? Is there any autocommit setting I should turn off ?
Please suggest ways to do it efficiently.
CREATE OR REPLACE
PROCEDURE sbx_staging_insert_user(client IN varchar2,
username IN varchar2,
comm_type IN varchar2,
email_addr IN varchar2,
buddy_name IN varchar2,
--default_flag IN char,
user_id OUT INT)
AS
BEGIN
select sbx_staging_user_id_seq.nextval into user_id from dual;
insert into sbx_staging_user
(user_id,
client,
username,
comm_type,
email_addr,
buddy_name,
default_flag)
values
(user_id,
client,
username,
comm_type,
email_addr,
buddy_name,
'Y');
end sbx_staging_insert_user;
and C# code is:
cmd.Transaction = conn.BeginTransaction();
cmd.CommandType = CommandType.Text;
cmd.CommandText = "sbx_staging_insert_user";
cmd.CommandType = CommandType.StoredProcedure;
var userClientParam = new OracleParameter(":client", OracleDbType.Varchar2);
var usernameParam = new OracleParameter(":username", OracleDbType.Varchar2);
var commTypeParam = new OracleParameter(":comm_type", OracleDbType.Varchar2);
var defaultParam = new OracleParameter(":default_flag", OracleDbType.Char) { Size = 1};
var emailParam = new OracleParameter(":email_addr", OracleDbType.Varchar2) { IsNullable = true };
var buddyParam = new OracleParameter(":buddy_name", OracleDbType.Varchar2) { IsNullable = true };
var userIdParam = new OracleParameter(":user_id", OracleDbType.Int32) { Direction = ParameterDirection.Output };
cmd.Parameters.Add(userClientParam);
cmd.Parameters.Add(usernameParam);
cmd.Parameters.Add(commTypeParam);
//cmd.Parameters.Add(defaultParam);
cmd.Parameters.Add(emailParam);
cmd.Parameters.Add(buddyParam);
cmd.Parameters.Add(userIdParam);
var cuList = new List<string>(Users.Count);
var uList = new List<string>(Users.Count);
var ctList = new List<string>(Users.Count);
var dfList = new List<char>(Users.Count);
var eaList = new List<string>(Users.Count);
var bnList = new List<string>(Users.Count);
var uiList = new List<decimal>(Users.Count);
int loopCnt = 0;
foreach (var ud in Users)
{
cuList.Add(ud.User.Client);
uList.Add(ud.User.Username);
ctList.Add(ud.User.CommType);
dfList.Add(ud.User.Default ? 'Y' : 'N');
eaList.Add(ud.User.Email);
bnList.Add(ud.User.BuddyName);
uiList.Add(-1);
}
userClientParam.Value = cuList.ToArray();
usernameParam.Value = uList.ToArray();
commTypeParam.Value = ctList.ToArray();
//defaultParam.Value = dfList.ToArray();
emailParam.Value = eaList.ToArray();
buddyParam.Value = bnList.ToArray();
userIdParam.Value = uiList.ToArray();
cmd.ArrayBindCount = cuList.Count;//Users.Count;//
cmd.ExecuteNonQuery();
cmd.Transaction.Commit();
Try something like this (untested, shortened for brevity):
public void insertRows()
{
if (Users.Count > 0)
{
OracleTransaction trans=_conn.BeginTransaction();
try
{
// create insert statement with bind vars
Stringbuilder sb = new Stringbuilder();
sb.Append("INSERT into sbx_staging_user(");
sb.Append("client,");
sb.Append("username,");
sb.Append("user_id");
sb.Append(") VALUES (");
sb.Append(":client,");
sb.Append(":username,");
sb.Append("seq_user_id.nextval");
sb.Append(") ");
OracleCommand cmd = new OracleCommand(sb.ToString(), _conn);
string[] ary_client = new string[Users.Count];
string[] ary_username = new string[Users.Count];
for (int i=0; i<Users.Count; i++)
{
User row=Users[i];
ary_client[i]=row.client;
ary_username[i]=row.username;
}
// prepare bind vars(bind in bulk using arrays)
OracleParameter prm=new OracleParameter();
cmd.Parameters.Clear();
cmd.ArrayBindCount=Users.Count;
cmd.BindByName=true;
prm=new OracleParameter("client", OracleDbType.Varchar2); prm.Value=ary_client; cmd.Parameters.Add(prm);
prm=new OracleParameter("username", OracleDbType.Varchar2); prm.Value=ary_username; cmd.Parameters.Add(prm);
cmd.ExecuteNonQuery();
trans.Commit();
trans.Dispose();
}
catch {
trans.Rollback();
trans.Dispose();
throw;
}
}
}
Maybe you should moove the bulk logic to the plsql.
See an example here:
http://dotnetslackers.com/articles/ado_net/BulkOperationsUsingOracleDataProviderForNETODPNET.aspx
I'm writing a code generator and am getting stuck on determining the nullable status of a stored procedure result set Column. I can query the DataType just fine but neither the datareader object nor a data table column contain the correct nullable value of my column.
public List<DataColumn> GetColumnInfoFromStoredProcResult(string schema, string storedProcName)
{
//build sql text
var sb = new StringBuilder();
sb.Append("SET FMTONLY OFF; SET FMTONLY ON; \n");//this is how EF4.1 did so I copied..not sure why the repeat
sb.Append(String.Format("exec {0}.{1} ", schema, storedProcName));
var prms = GetStoredProcedureParameters(schema: schema, sprocName: storedProcName);
var count = 1;
foreach (var param in prms)
{
sb.Append(String.Format("{0}=null", param.Name));
if (count < prms.Count)
{
sb.Append(", ");
}
count++;
}
sb.Append("\n SET FMTONLY OFF; SET FMTONLY OFF;");
var dataTable = new DataTable();
//var list = new List<DataColumn>();
using (var sqlConnection = this.SqlConnection)
{
using (var sqlAdapter = new SqlDataAdapter(sb.ToString(), sqlConnection))
{
if (sqlConnection.State != ConnectionState.Open) sqlConnection.Open();
sqlAdapter.SelectCommand.ExecuteReader(CommandBehavior.KeyInfo);
sqlConnection.Close();
sqlAdapter.Fill(dataTable);
}
//using (var sqlCommand = new SqlCommand())
//{
// sqlCommand.CommandText = sb.ToString();
// sqlCommand.CommandType = CommandType.Text;
// sqlCommand.Connection = sqlConnection;
// if (sqlConnection.State != ConnectionState.Open) sqlConnection.Open();
// var dr = sqlCommand.ExecuteReader(CommandBehavior.SchemaOnly);
// var whateva = dr.GetSchemaTable();
// foreach (DataColumn col in whateva.Columns)
// {
// list.Add(col);
// }
//}
}
var list = dataTable.Columns.Cast<DataColumn>().ToList();
return list;
}
I'm trying to end up with something similar to the the Entities Framework creation of a complex type from a stored procedure. Can I hijack that functionality?
On this example the Id column.. tblJobId (not my naming convention) would never be null.. But I selected null as ImNull and it has all the same properties so how does EF determine if the corresponding C# data type should be nullable or not?
Has anybody done this..
Ideas are appreciated.
The secret was to use Schema Only and fill a dataset not datatable. Now the AllowDbNull property on the datacolumn properly displays the nullable status of the return value.
This was it...
public List<DataColumn> GetColumnInfoFromStoredProcResult(string schema, string storedProcName)
{
//build sql text
var sb = new StringBuilder();
sb.Append("SET FMTONLY OFF; SET FMTONLY ON; \n");//this is how EF4.1 did so I copied..not sure why the repeat
sb.Append(String.Format("exec {0}.{1} ", schema, storedProcName));
var prms = GetStoredProcedureParameters(schema: schema, sprocName: storedProcName);
var count = 1;
foreach (var param in prms)
{
sb.Append(String.Format("{0}=null", param.Name));
if (count < prms.Count)
{
sb.Append(", ");
}
count++;
}
sb.Append("\n SET FMTONLY OFF; SET FMTONLY OFF;");
var ds = new DataSet();
using (var sqlConnection = this.SqlConnection)
{
using (var sqlAdapter = new SqlDataAdapter(sb.ToString(), sqlConnection))
{
if (sqlConnection.State != ConnectionState.Open) sqlConnection.Open();
sqlAdapter.SelectCommand.ExecuteReader(CommandBehavior.SchemaOnly);
sqlConnection.Close();
sqlAdapter.FillSchema(ds, SchemaType.Source, "MyTable");
}
}
var list = ds.Tables[0].Columns.Cast<DataColumn>().ToList();
return list;
}
public List<SqlParamInfo> GetStoredProcedureParameters(string schema, string sprocName)
{
var sqlText = String.Format(
#"SELECT
[Name] = N'#RETURN_VALUE',
[ID] = 0,
[Direction] = 6,
[UserType] = NULL,
[SystemType] = N'int',
[Size] = 4,
[Precision] = 10,
[Scale] = 0
WHERE
OBJECTPROPERTY(OBJECT_ID(N'{0}.{1}'), 'IsProcedure') = 1
UNION
SELECT
[Name] = CASE WHEN p.name <> '' THEN p.name ELSE '#RETURN_VALUE' END,
[ID] = p.parameter_id,
[Direction] = CASE WHEN p.is_output = 0 THEN 1 WHEN p.parameter_id > 0 AND p.is_output = 1 THEN 3 ELSE 6 END,
[UserType] = CASE WHEN ut.is_assembly_type = 1 THEN SCHEMA_NAME(ut.schema_id) + '.' + ut.name ELSE NULL END,
[SystemType] = CASE WHEN ut.is_assembly_type = 0 AND ut.user_type_id = ut.system_type_id THEN ut.name WHEN ut.is_user_defined = 1 OR ut.is_assembly_type = 0 THEN st.name WHEN ut.is_table_type =1 Then 'STRUCTURED' ELSE 'UDT' END,
[Size] = CONVERT(int, CASE WHEN st.name IN (N'text', N'ntext', N'image') AND p.max_length = 16 THEN -1 WHEN st.name IN (N'nchar', N'nvarchar', N'sysname') AND p.max_length >= 0 THEN p.max_length/2 ELSE p.max_length END),
[Precision] = p.precision,
[Scale] = p.scale
FROM
sys.all_parameters p
INNER JOIN sys.types ut ON p.user_type_id = ut.user_type_id
LEFT OUTER JOIN sys.types st ON ut.system_type_id = st.user_type_id AND ut.system_type_id = st.system_type_id
WHERE
object_id = OBJECT_ID(N'{0}.{1}')
ORDER BY 2", schema, sprocName);
using (var sqlConnection = this.SqlConnection)
{
using (var sqlCommand = new SqlCommand())
{
if (sqlConnection.State != ConnectionState.Open) sqlConnection.Open();
sqlCommand.Connection = sqlConnection;
sqlCommand.CommandType = CommandType.Text;
sqlCommand.CommandText = sqlText;
var dr = sqlCommand.ExecuteReader();
var result = new List<SqlParamInfo>();
while (dr.Read())
{
if (Convert.ToString(dr["Name"]) != "#RETURN_VALUE")
{
result.Add(new SqlParamInfo(dr));
}
}
return result;
}
}
}
Assume, that every column which comes from SP can be null - this is a valid assumption because stored procedure - its a kind of data abstraction layer and thus its code can change but still produce valid results.
If column was non-nullable yesterday it means nothing for today. So - all the columns which come from SP resultsets are nullable by design.
Update.
Assuming that table t1 has column Id INT IDENTITY PRIMARY KEY
Your stored proc looks like this:
CREATE PROC p1
AS
BEGIN
SELECT Id FROM t1
END
So it will never return an Id = NULL, but this is the SP - an abstraction of data, so - tomorrow i'll modify it like this:
CREATE PROC p1
AS
BEGIN
SELECT Id FROM t1
UNION
SELECT NULL
END
So, now it returns NULL - think about this. The difference in understanding of data abstraction