I have written a function that retrieve some data from database (using a stored procedure, Oracle database).
The stored procedure has 4 parameter and one of them is am out parameter. Here is my code for this:
string commandName = "someStoredProc";
List<IDataParameter> parameters = new List<IDataParameter>() { new SqlParameter("#param1", param1), new SqlParameter("#param2", param2), new SqlParameter("#param3", param3) };
SqlParameter calcId = new SqlParameter("#param4", SqlDbType.BigInt);
calcId.Direction = ParameterDirection.Output;
parameters.Add(calcId);
Dictionary<long, ITrade> trades = _genericDataReader.ExecuteSqlQuery(parameters, CommandTypeEnum.Command, commandName);
long CalculationID = (long)calcId.Value;
This code is working fine.
Now I am writing unit test cases for this. I am able to mock the database result whatever I want but not this fourth out Parameter:
test failed when it try to convert calcId.Value to long as it is null.
How can I mock this out parameter value?
What is actually necessary here is writting an implementation of ExecuteSqlQuery for testing purpose, which has an access to the arguments and can do the job.
Rhino Mock provides a few ways to add a custom behavior to mocks/stubs. One of possible ways is to write The Do() handler.
Here is a short example:
_genericDataReader
.Stub(gdr => gdr.ExecuteSqlQuery(Arg<List<IDataParameter>>.Is.Anything, Arg<CommandTypeEnum>.Is.Anything, Arg<string>.Is.Anything))
.Do((Func<List<IDataParameter>, CommandTypeEnum, string, IDictionary<long, ITrade>>)((param, cmd, cmdName) =>
{
param[3].Value = 12L;
return null; // or return whatever is required dataset here
}));
Related
I'm finding myself using a very similar set of code over and over for different stored procedues
SqlParameter[] parameters = {
new SqlParameter("#somevar", 123),
new SqlParameter("#othervar", 456)
}
List<MyClass> result;
using (MyContext contextName = new MyContext())
{
result = contextName.Database.SqlQuery<MyClass>("mystoredproc #somevar, #othervar", parameters).ToList();
}
And I thought to myself, why not make a standard set of code that passes in the stored procedure and SqlParameter[]
MyClass result = getStoredProcResult("mystoredproc", parameters);
Where I'm struggling is making SqlQuery generic.
I tried using
result = contextName.Database.SqlQuery<object>(procname, parameters);
However, I always just get an object out, and it will not return back to MyClass
I tried using Type, but get an error with that..
Is there something I'm missing, or can it just not handle a generic class and I'm doomed to just repeat myself?
I am calling a stored procedure from my .net application using EF Core 7 which provides the of FromSql to execute the stored procedure. However I am struggling to pass named paramaters. This is what I have attempted so far and even sample code which ispretty much the same:
using (accountingContext db = new())
{
var TransactionClassId = new SqlParameter("TransactionClassId", 3);
var list = db.MonthlyTotals
.FromSql($"EXECUTE dbo.Reporting_Monthly_Totals #TransactionClassId;", TransactionClassId)
.ToList();
}
The above produces the error that FromSql does not contain an overload which accept s 2 arguments.
Is there a correct way to parametrized data using FromSql above?
I believe the problem in your code is located in creating the parameter for the procedure.
Instead of your code:
var TransactionClassId = new SqlParameter("TransactionClassId", 3);
Try this:
var TransactionClassId = new SqlParameter("#TransactionClassId", 3);
Add the "#" symbol in the SqlParameter() method;
In an MVC 5 web app using Entity Framework, I learned how to populate an Index view by using db.Database.SqlQuery<model> to execute a stored procedure and show the results in the Index View.
This is the relevant code in my Index View (and it works).
// supply parameter values required by the stored procedure
object[] parameters = {
new SqlParameter("#campus",SqlDbType.NVarChar,3) {Value=vm.SelectedCampus},
new SqlParameter("#date1",SqlDbType.DateTime) {Value=Convert.ToDateTime(vm.SelectedStartDate)},
new SqlParameter("#date2",SqlDbType.DateTime) {Value=Convert.ToDateTime(vm.SelectedEndDate)}
};
// populate the list by calling the stored procedure and supplying parameters
IEnumerable<PerfOdomoeterDate> query =
db.Database.SqlQuery<PerfOdomoeterDate>("PerfOdomoeterDate #campus, #date1, #date2",
parameters).OrderBy(m => m.StudentName).ToList();
And to put that code into better context, here is the entire Index ActionResult.
private PerformanceContext db = new PerformanceContext();
private static readonly string d1 = DateTime.Now.ToShortDateString();
private static readonly string d2 = DateTime.Now.ToShortDateString();
[HttpGet]
public ActionResult Index(int? page, string SelectedCampus = "CRA", string SelectedStartDate=null, string SelectedEndDate=null)
{
int PageNumber = (page ?? 1);
PerfOdomoeterDateViewModel vm = new PerfOdomoeterDateViewModel();
vm.SelectedCampus = SelectedCampus;
vm.SelectedStartDate = string.IsNullOrEmpty(SelectedStartDate) ? d1 : SelectedStartDate;
vm.SelectedEndDate = string.IsNullOrEmpty(SelectedEndDate) ? d2 :SelectedEndDate;
vm.CampusList = StaticClasses.ListBank.CampusList();
// supply parameter values required by the stored procedure
object[] parameters = {
new SqlParameter("#campus",SqlDbType.NVarChar,3) {Value=vm.SelectedCampus},
new SqlParameter("#date1",SqlDbType.DateTime) {Value=Convert.ToDateTime(vm.SelectedStartDate)},
new SqlParameter("#date2",SqlDbType.DateTime) {Value=Convert.ToDateTime(vm.SelectedEndDate)}
};
// populate the list by calling the stored procedure and supplying parameters
IEnumerable<PerfOdomoeterDate> query =
db.Database.SqlQuery<PerfOdomoeterDate>("PerfOdomoeterDate #campus, #date1, #date2",
parameters).OrderBy(m => m.StudentName).ToList();
vm.CreditTable = query.ToPagedList(PageNumber, 25);
return View(vm);
}
As I stated, this code works perfectly in the Index View. However, in a separate ActionResult, where the user has an option to export the data set to an Excel file, I use the same code, and I get this runtime error:
The SqlParameter is already contained by another SqlParameterCollection.
I was under the impression that each ActionResult is in its own scope, so how is it that I am getting this error when I am calling up a new query from a separate ActionResult?
Intellisense did not give me any clues as to how I could explicitly empty the parameters after executing the stored procedure.
This is the code in the ActionResult that is producing the error.
public ActionResult ExportToExcel(string SelectedCampus, string SelectedStartDate, string SelectedEndDate)
{
object[] parameters2 = {
new SqlParameter("#campus",SqlDbType.NVarChar,3) {Value=SelectedCampus},
new SqlParameter("#date1",SqlDbType.DateTime) {Value=Convert.ToDateTime(SelectedStartDate)},
new SqlParameter("#date2",SqlDbType.DateTime) {Value=Convert.ToDateTime(SelectedEndDate)}
};
IEnumerable<PerfOdomoeterDate> query =
db.Database.SqlQuery<PerfOdomoeterDate>("PerfOdomoeterDate #campus, #date1, #date2",
parameters2).OrderBy(m => m.StudentName).AsEnumerable();
...
The ADO.Net objects (like SqlParameter, SqlCommand etc.) presented to us by the .Net framework are a mere layer on top of the real stuff under the hood that is managed by the .Net connection pool. If we create a new SqlConnection —which is implicitly done by db.Database.SqlQuery— we don't really establish a new connection to the database. That would be far too expensive. In reality, our connection object "plugs" in to an available connection in the connection pool.
Normally, this mechanism is pretty transparent, but it is unveiled in issues like the one you see here. I remember having experienced similar issues (exceptions that persisted longer than met the eye).
The message is: you can't beat it, so join it. The quick solution seems to be renaming the parameters in one of the methods. A better solution, of course, is to factor out the repetitive part of your code into a method that contains the identical parts.
I would say, This is how as per the design.
You need to extract the data right from there .ToArray() or .ToList() etc...
Do not try to re execute the query for further data operations.
I am using Dapper with Oracle Managed and trying to use QueryMultiple method. I have a stored procedure in an Oracle package that returns multiple recordsets. I want to reuse my code for multiple package methods and the data coming back is coming from different source tables, so columns and actual data will vary.
To handle this I have a class that uses an IEnumerable<string> for storing FieldNames and an IEnumerable<object[]> for storing the data for each row.
With just a single recordset I am able to use the ExecuteReader method to iterate the results and add them myself. However, I would like to use QueryMultiple to get everything in a single call. Right now I have two recordsets coming back but others may be added.
After a few different attempts I was able to get the following code to work. However, it seems like there should be a better way to get this. Further below is a piece of code found in another SO question that seemed to be what I wanted but I just couldn't get it to work. Does anyone have any suggestions about how I'm getting the FieldNames and Data in the code below and if there is a better way using Dapper API. Loving Dapper by the way. Thanks for any suggestions.
// class to load results into
public class Results {
public IEnumerable<string> FieldNames { get; set; }
public DetailInfo Detail { get; set; }
public IEnumerable<object[]> Data { get; set; }
}
// code to get the results from the database field names and data varies
// method is private so that external callers cannot pass any just any string as methodName
private Results GetTableResults(string methodName) {
var queryparams = new OracleDynamicParameters();
queryparams.Add(name: "l_detail_cursor", dbType: OracleDbType.RefCursor, direction: ParameterDirection.Output);
queryparams.Add(name: "l_data_cursor", dbType: OracleDbType.RefCursor, direction: ParameterDirection.Output);
// ... other parameters go here, removed for example
Results results = new Results();
string sql = string.Format("{0}.GET_{1}_DETAILS", PackageName, methodName);
using (IDbConnection db = new OracleConnection(this.ConnectionString)) {
db.Open();
using (var multi = db.QueryMultiple(sql: sql, param: queryparams, commandType: CommandType.StoredProcedure)) {
// detail in first cursor, no problems here
results.Detail = multi.Read<DetailInfo>().Single();
// --------------------------------------------------
// this is the code I'm trying to see if there is a better way to handle
// --------------------------------------------------
// data in second cursor
var data = multi.Read().Select(dictionary =>
// cast to IDictionary
dictionary as IDictionary<string, object>
);
// pull from Keys
results.FieldNames = data.First().Select(d => d.Key);
// pull from values
results.Data = data.Select(d => d.Values.ToArray());
// --------------------------------------------------
}
}
return results;
}
I was attempting to try to use something like the following but only get an exception at run time about splitOn needing to be specified. I tried using something like ROWNUM to even give the recordset an "id" but that didn't seem to help.
var data = multi.Read<dynamic, dynamic, Tuple<dynamic, dynamic>>(
(a, b) => Tuple.Create((object)a, (object)b)).ToList();
I'm looking for the absolute easiest way to call stored procedures from C# without explicit parameter objects, like so:
using (DataTable dt=conn.ExecuteQuery("MySP", param1, "param2", p3, p4)) {
On first invocation the library queries the DB schema for the SP signature then caches it for subsequent calls.
A) Is there any way to do it THIS SIMPLY with the Enterprise Library Data Access Block?
B) I don't find ORMs attractive because of synchronization issues between schema and code metadata.
I DID find this generator-less wrapper but am hoping there is a major library or best practice I somehow just haven't discovered yet.
I do have an example of an SqlDataReader where the Function call is
ExecuteNonQuery("dbo.[Sp_Skp_UpdateFuncties]", parameters);
This is in a class DataBaseManager which hold the databaseconnectionstring
public classDataBaseManager
{
...
public int ExecuteStoredProcedure(string storedprocedureNaam, IEnumerable<KeyValuePair<string, object>> parameters)
{
var sqlCommand = new SqlCommand
{
Connection = DatabaseConnectie.SqlConnection,
CommandType = CommandType.StoredProcedure,
CommandText = storedprocedureNaam,
};
foreach (KeyValuePair<string, object> keyValuePair in parameters)
{
sqlCommand.Parameters.Add(
new SqlParameter { ParameterName = "#" + keyValuePair.Key, Value = keyValuePair.Value ?? DBNull.Value }
);
}
if (sqlCommand == null)
throw new KoppelingException("Stored procedure ({0}) aanroepen lukt niet", storedprocedureNaam);
return sqlCommand.ExecuteNonQuery();
}
....
}
Dapper?
var rows = conn.Query("procname",
new { name = "abc", id = 123 }, // <=== args, fully named and typed
commandType: CommandType.StoredProcedure
).ToList();
The above is the dynamic API which allows automatic binding to column names:
foreach(var row in rows) {
int x = row.X; // look ma, no column mappings
string y = row.Y;
//...
}
But you can also use Query<SomeType> and it will populate an object model for you. When binding to an object model it includes all the meta-programming / caching you might expect from people obsessive-compulsive about performance. Hint: I usually use the generic API - it is very very fast.