ADO.NET add SQLParameters - ado.net

I am trying to execute a stored procedure with parameters using ADO.NET in .NET Core.
When I try and pass in a SqlParameter object I get the below error.
I have a using statement at the top for System.Data.SqlClient.
Is there another way or new ways of passing Sql parameters to a stored procedure?
public async Task<List<SynchStockDto>> GetStockForSync(int locationBinId, DateTime lastSyncDate)
{
await EnsureConnectionOpenAsync();
var command = GetConnection().CreateCommand();
command.CommandText = "sp_GetStockForSync #LocationBinId, #LastSyncDate";
command.CommandType = CommandType.StoredProcedure;
command.Transaction = GetActiveTransaction();
command.Parameters.Add(new SqlParameter("LocationBinId",locationBinId)); //not finding SQLParameter method
using (var dataReader = await command.ExecuteReaderAsync())
{
var result = new List<SynchStockDto>();
while (dataReader.Read())
{
var stockItem = new SynchStockDto
{
};
result.Add(stockItem);
}
return result;
}
}

Related

Timeouts + Prepared Transactions with ADO.NET / Npgsql

Looking for some clarification on the below scenarios. CanAddConcurrently and DoesNotTimeout are failing.
CanAddConcurrently - (Npgsql.PostgresException: 55000: prepared transactions are disabled). I understand it's because I have this disabled in my postgres config, but WHY is this escalating to a prepared transaction? Is it because it's actually getting a different NpgsqlConnection? If so, does this really call for a distributed transaction? I can run the same sample with knex, node-postgres with same pool limits, prepared transactions disabled in postgres without issue under node.js
DoesNotTimeout - (The connection pool has been exhausted) I don't understand why the pooled connections are not being reused here. Are they not being disposed because they are associated with the top level TransactionScope in the test? Even under this scenario why can't the connection be reused if it's associated with the same transaction. I can run the same test case with knex, node-postgres with the same pool limit without issue under node.js.
using Npgsql;
using System.Threading.Tasks;
using System.Transactions;
namespace TestCases
{
public class Service
{
private readonly string connectionString;
public Service(string connectionString)
{
this.connectionString = connectionString;
}
// I am aware this is only executing 1 query so does not have a need for an embedded transaction, this is just to keep example simple
// removing the TransactionScope does not fix the issue, but for closer sample to original code it is here
public async Task Add(string val)
{
using (var nestedScope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
using (var conn = new NpgsqlConnection(this.connectionString))
{
await conn.OpenAsync();
var cmd = new NpgsqlCommand("INSERT INTO data(value) values(#p);", conn);
cmd.Parameters.AddWithValue("p", val);
await cmd.ExecuteNonQueryAsync();
nestedScope.Complete();
}
}
}
}
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Npgsql;
using System;
using System.Linq;
using System.Threading.Tasks;
using System.Transactions;
namespace TestCases
{
[TestClass]
public class ServiceTests
{
readonly string connectionString = "Server=127.0.0.1;Port=5432;Database=test_db;User Id=postgres;Password=postgres;MaxPoolSize=10;Pooling=true;";
private Service service;
[TestInitialize]
public async Task Initialize()
{
service = new Service(this.connectionString);
using (var conn = new NpgsqlConnection(this.connectionString))
{
await conn.OpenAsync();
var query = "CREATE TABLE IF NOT EXISTS data(value varchar(255));";
var cmd = new NpgsqlCommand(query, conn);
await cmd.ExecuteNonQueryAsync();
}
}
[TestCleanup]
public async Task Cleanup()
{
using (var conn = new NpgsqlConnection(this.connectionString))
{
await conn.OpenAsync();
var query = "DROP TABLE IF EXISTS data;";
var cmd = new NpgsqlCommand(query, conn);
await cmd.ExecuteNonQueryAsync();
}
}
/// <summary>
/// Failing with prepared PG 55000
/// </summary>
/// <returns></returns>
[TestMethod]
public async Task CanAddConcurrently()
{
using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
await Task.WhenAll(
Enumerable.Range(1, 10).Select(async i =>
{
var val = string.Format("CanAddConcurrently_Q{0};", i);
await service.Add(val);
})
);
scope.Complete();
}
using (var conn = new NpgsqlConnection(this.connectionString))
{
await conn.OpenAsync();
var query = "select count(*) from data WHERE value like 'CanAddConcurrently_Q%';";
var cmd = new NpgsqlCommand(query, conn);
long count = (long)await cmd.ExecuteScalarAsync();
Assert.AreEqual((long)100, count);
}
}
/// <summary>
/// Timing out
/// </summary>
/// <returns></returns>
[TestMethod]
public async Task DoesNotTimeout()
{
using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
await Task.WhenAll(
Enumerable.Range(1, 100).Select(async i =>
{
var val = string.Format("DoesNotTimeout_Q{0};", i);
await service.Add(val);
})
);
scope.Complete();
}
using (var conn = new NpgsqlConnection(this.connectionString))
{
await conn.OpenAsync();
var query = "select count(*) from data WHERE value like 'DoesNotTimeout_Q%';";
var cmd = new NpgsqlCommand(query, conn);
long count = (long)await cmd.ExecuteScalarAsync();
Assert.AreEqual((long)100, count);
}
}
/// <summary>
/// Passes OK
/// </summary>
/// <returns></returns>
[TestMethod]
public async Task CanAddSequentially()
{
using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
for (long i = 0; i < 100; i++)
{
var val = string.Format("CanAddSequentially_Q{0};", i);
await service.Add(val);
}
scope.Complete();
}
using (var conn = new NpgsqlConnection(this.connectionString))
{
await conn.OpenAsync();
var query = "select count(*) from data WHERE value like 'CanAddSequentially_Q%';";
var cmd = new NpgsqlCommand(query, conn);
long count = (long)await cmd.ExecuteScalarAsync();
Assert.AreEqual((long)100, count);
}
}
/// <summary>
/// Passes OK
/// </summary>
/// <returns></returns>
[TestMethod]
public async Task RollsBackIfError()
{
try
{
using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
for (long i = 0; i < 100; i++)
{
var val = string.Format("RollsBackIfError_Q{0};", i);
if (i == 99)
{
val = val.PadRight(300, 'e'); // trigger error on last item
}
await service.Add(val);
}
scope.Complete();
}
}
catch (Exception ex)
{
Assert.IsInstanceOfType(ex, typeof(NpgsqlException));
}
using (var conn = new NpgsqlConnection(this.connectionString))
{
await conn.OpenAsync();
var query = "select count(*) from data WHERE value like 'RollsBackIfError_Q%';";
var cmd = new NpgsqlCommand(query, conn);
long count = (long)await cmd.ExecuteScalarAsync();
Assert.AreEqual((long)0, count);
}
}
}
}
You need to understand exactly how Task.WhenAll() (and possibly async) operates.
Your code above invokes Service.Add() 10 times, concurrently, on the same transaction scope, and then waits for those 10 invocations to complete. Each invocation opens a new pooled connection and attempts to enlist to the single transaction scope that exists in your program; the moment more than one connection is enlisted to a transaction scope, that is a distributed transaction, which is why the escalation occurs.
The same likely explains the pool exhaustion in your second example - it's not about pool connections not being reused, it's about you trying to use too many at the same time.
You should run your code serially with a standard foreach, executing the next operation only after the previous one completed. It is possible to run multiple operations at the same time - for better performance - but you definitely can't share the same transaction scope between them.

How do I use Entity Framework instead of Ado.net for stored procedure?

I am using a stored procedure in my SQL Server database to take input of the data through the datatable. As I am using ASP.NET MVC now, I want to use Entity Framework instead of ado.net
public void BulkUpload(DataTable dt)
{
dt.TableName = "MainTable";
DataSet dataset = new DataSet();
DataTable dataTable = new DataTable();
try
{
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["conn"].ConnectionString))
{
conn.Open();
{
SqlCommand cmd = new SqlCommand("DatatableToDataBase", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#mode", SqlDbType.VarChar).Value = "MainTB";
cmd.Parameters.AddWithValue("#Details", SqlDbType.VarChar).Value = dt;
cmd.ExecuteNonQuery();
conn.Close();
}
}
}
catch (Exception)
{ }
}
It's very easy just add an entity datamodel to your program
connect to you model :
"entitymodel" context = this.CurrentDataSource;
And pass the stored procedure where you like .
Thats everything
Example:
[WebGet]
public List<callersW> GetCaller()
{
testCDREntities1 context = this.CurrentDataSource;
//sql parameters here
List<callersW> result = context.Database.SqlQuery<callersW>("StoredProcedure").ToList();
return result;
}

Entity Framework (Telerik) call fails with ExecuteNonQuery to PostgreSQL stored procedure

(PostgreSQL 9.1, Telerik OpenAccess v2.0.50727, PgAdmin III).
I'm having difficulty calling a stored procedure from the (Telerik) Entity Framework. The exact error is:
NpgsqlException was unhandled by user code.
ERROR: 42703: column "cpatient" does not exist.
The Telerik templated call is:
public int SaveDx(string cpatient, Object o, Object n)
{
OAParameter parameterCpatient = new OAParameter();
parameterCpatient.ParameterName = "cpatient";
parameterCpatient.Size = -1;
if(cpatient != null)
{
parameterCpatient.Value = cpatient;
}
else
{
parameterCpatient.DbType = DbType.String;
parameterCpatient.Value = DBNull.Value;
}
OAParameter parameterO = new OAParameter();
parameterO.ParameterName = "o";
parameterO.Value = o;
OAParameter parameterN = new OAParameter();
parameterN.ParameterName = "n";
parameterN.Value = n;
int queryResult = this.ExecuteNonQuery("SELECT * FROM \"public\".\"g_savedx\"(cpatient, o, n)", CommandType.Text, parameterCpatient, parameterO, parameterN);
return queryResult;
}
Where the ExecuteNonQuery statement generates the error. The PostgreSQL stored procedure is:
FUNCTION g_savedx(cpatient character varying, o view_dx, n view_dx)
RETURNS void AS ...
The postgreSQL function has been tested to work correctly from pgAdmin.
So where is the column "cpatient" coming from?? What am I doing wrong?
TIA
I never could get the Telerik EntitiesModel ExecuteNonQuery to work under any conditions. Hence the suggested code of:
using (var cxt = new Nova.Data.Data())
{
cxt.SaveDx();
cxt.SaveChanges();
}
where cxt.SaveDx() is the domain model name for the postgresql g_savedx stored procedure, fails.
My eventual workaround for PostgreSQL is to use Npgsql directly as:
public void SaveDx(View_dx dx, bool alldx = false)
{
using (var cxt = new Nova.Data.Data())
{
string connstring = cxt.Connection.ConnectionString;
using (NpgsqlConnection conn = new NpgsqlConnection(connstring))
{
conn.Open();
using (var cmd = conn.CreateCommand())
{
cmd.CommandText = "g_savedx";
cmd.CommandType = CommandType.StoredProcedure;
NpgsqlCommandBuilder.DeriveParameters(cmd);
cmd.Parameters["groupid"].Value = ....
var rowsAffected = cmd.ExecuteNonQuery();
}
}
}
}
When doing it this way, only use the types defined in the NpgsqlDbType enumeration in the PostgreSQL procedure interface. (PostgreSQL can use composite types, Npgsql not so much).
It would sure be nice for Telerik ExecuteNonQuery to work.

RuntimeBinderException: Convert type System.Threading.Tasks.Task<object> to string

I do an example with signalR. But it doesn't function because of one mistake.
The one mistake (can not convert type system.threading.tasks.task< object> to string) is in this line:
return context.Clients.All.RecieveNotification(simple);
It is at the bottom of the code you can see below.
Below you see the method I wrote. There I do a connection with the database and get the content with a command/query.
Then a few checks and also SqlDependency.
public string SendNotifications()
{
using (var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString))
{
string query = "SELECT eintrag FROM [dbo].[simple_column]";
connection.Open();
using (SqlCommand command = new SqlCommand(query, connection))
{
command.Notification = null;
DataTable dt = new DataTable();
SqlDependency dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
if (connection.State == ConnectionState.Closed)
connection.Open();
var reader = command.ExecuteReader();
dt.Load(reader);
if (dt.Rows.Count > 0)
{
simple = dt.Rows[0]["eintrag"].ToString();
}
}
}
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<NotificationHub>();
return context.Clients.All.RecieveNotification(simple);
}
And here I use the function:
var notifications = $.connection.notificationHub;
notifications.client.recieveNotification = function (simple) {
// Add the message to the page.
$('#dbMessage').text(simple);
};
I hope you can help me.
Thanks.

Incorrect number of arguments for PROCEDURE ntc_sales.AgentAmounts; expected 1, got 0

Here i am tring to call the stored procedure with paramter using entityframework
it's get the error:Incorrect number of arguments for PROCEDURE ntc_sales.AgentAmounts; expected 1, got 0
Below code
`enter code here`[HttpGet]
public virtual List<AgentAmounts> AgentAmountbyId(int id)
{
string SQLQuery = #"call AgentAmounts();";
var objectContext = ((IObjectContextAdapter)db).ObjectContext;
//List<object> listobj = new List<object>();
List<AgentAmounts> data = objectContext.ExecuteStoreQuery<AgentAmounts>(SQLQuery,
id).AsQueryable().ToList();
return data;
}
public virtual List<AgentAmounts> AgentAmountbyId(int id)
{
string SQLQuery = #"call AgentAmounts(#vagent);";
var objectContext = ((IObjectContextAdapter)db).ObjectContext;
//List<object> listobj = new List<object>();
List<AgentAmounts> data = objectContext.ExecuteStoreQuery<AgentAmounts>(SQLQuery,new Mysqlparamter("#vagent",id).AsQueryable().ToList();
return data;
}