ExecuteNonQueryAsync hangs in MVC app - sql

I have a DAL project I call from my MVC controller. I pass the method a list of objects I want updated. I added break points and it gets to ExecuteNonQueryAsync() and then just hangs. The website keeps "loading" and it never gets to return 1; The query worked before I tried to move it to execute async (you can see where I commented out the execution of the query. I did delete the connection open/close pieces but the query worked fine before this.
Also I updated the code below so I can post it with OBJECT, ListObjects and COL1/COL2 instead of the actual values/object names used.
Thanks in advance!
static async Task<int> NonQuery(System.Data.SqlClient.SqlConnection conn, System.Data.SqlClient.SqlCommand cmd)
{
await conn.OpenAsync();
await cmd.ExecuteNonQueryAsync();
return 1;
}
public static bool updateQuery(List<OBJECT> listObjects)
{
string cnn = System.Configuration.ConfigurationManager.ConnectionStrings["connstring"].ToString();
using (System.Data.SqlClient.SqlConnection mySqlConnection = new System.Data.SqlClient.SqlConnection(cnn))
{
StringBuilder SQLQuery = new StringBuilder();
SQLQuery.AppendLine("UPDATE TABLENAME ");
SQLQuery.AppendLine("SET COL1 = #COL1, ");
SQLQuery.AppendLine("WHERE COL2 = #COL2 ");
//mySqlConnection.Open();
foreach (ObjectType ot in listObjects)
{
System.Data.SqlClient.SqlCommand cmd = new System.Data.SqlClient.SqlCommand(SQLQuery.ToString(), mySqlConnection);
cmd.Parameters.Add("#COL1", System.Data.SqlDbType.Int).Value = COL1VALUE;
cmd.Parameters.Add("#COL2", System.Data.SqlDbType.Int).Value = COL2VALUE;
int result = NonQuery(mySqlConnection, cmd).Result;
//cmd.ExecuteNonQuery();
}
//mySqlConnection.Close();
}

As I describe on my blog, this is a common problem when converting your code to async. The proper solution is to replace all Result and Wait calls with await, as such:
public static async Task<bool> updateQueryAsync(List<OBJECT> listObjects)
{
...
int result = await NonQueryAsync(mySqlConnection, cmd);
...
}
static async Task<int> NonQueryAsync(SqlConnection conn, SqlCommand cmd)
I also changed your async methods to end in Async, as specified by the TAP documentation.

Related

How to get a list of all Table names through asp.net API controller

So I want to get a list of all the table names from the database through a controller as an ASP.net API project. I tried to do it through raw sql query without entity and it looked something like this.
public async Task<ActionResult<IList>> GetAllTableNames()
{
using (var context = new DbContext())
{
List<string> results = context.Database.SqlQuery<string>("SELECT name FROM sys.tables").ToListAsync();
}
}
But when I try to use the SqlQuery method I get the error
" 'DatabaseFacade' does not contain a definition for 'SqlQuery' and no accessible extension method 'SqlQuery' ". Anybody that has any idea how to solve this?
First create an Helper method in your controller as shown below
using System.Data.SqlClient;
public async IAsyncEnumerable<string> GetTableNamesAsync()
{
using var connection = new SqlConnection(_dbContext.Database.GetConnectionString());
var command = new SqlCommand("SELECT name FROM sys.tables",connection);
await connection.OpenAsync();
var reader = await command.ExecuteReaderAsync();
while (await reader.ReadAsync())
{
yield return reader.GetString(0);
}
}
Then call in your action Like this
public async Task<IActionResult> Index()
{
var list=new List<string>();
await foreach (var item in GetTableNamesAsync())
{
list.Add(item);
}
return Ok(list);
}

Could not GetActiveTransaction

I need to call stored procedure in my App Service. I'm following this tutorial to create a custom repository. But I could not find the Context.Database.
Here is the code:
[UnitOfWork]
public virtual async Task<List<string>> GetUserNames()
{
EnsureConnectionOpen();
using (var command = CreateCommand("GetUsernames", CommandType.storedProcedure))
{
using (var dataReader = await command.ExecuteReaderAsync())
{
var result = new List<string>();
while (dataReader.Read())
{
result.Add(dataReader["UserName"].ToString());
}
return result;
}
}
}
What is the Context.Database and how do I use it?
My application is MVC in .Net4.6.1.
Update 1
I have resolved this issue by inheriting MyApplicationRepositoryBase.
Now I face a new issue when trying to GetActiveTransaction. I don't have any idea about this. What can I do?
Here is the code:
private DbTransaction GetActiveTransaction()
{
return (DbTransaction)_transactionProvider.GetActiveTransaction(new ActiveTransactionProviderArgs
{
{"ContextType", typeof(MyDbContext) },
{"MultiTenancySide", MultiTenancySide }
});
}
Here is the error log:
System.NullReferenceException: Object reference not set to an instance of an object.
at Abp.EntityFramework.EfActiveTransactionProvider.GetActiveTransaction(ActiveTransactionProviderArgs args) in D:\Github\aspnetboilerplate\src\Abp.EntityFramework\EntityFramework\EfActiveTransactionProvider.cs:line 21
Update 2
I tried to comment out the command.Transaction and it worked:
private DbCommand CreateCommand(string commandText, CommandType commandType, params SqlParameter[] parameters)
{
var command = Context.Database.GetDbConnection().CreateCommand();
command.CommandText = commandText;
command.CommandType = commandType;
// command.Transaction = GetActiveTransaction();
foreach (var parameter in parameters)
{
command.Parameters.Add(parameter);
}
return command;
}
I still need to put my code under a Transaction, so I don't want to comment out this line. What should I do?
Now I faced the new issue when trying to get Active Transaction
Add this in PreInitialize method of YourProjectNameDataModule:
Configuration.ReplaceService<IEfTransactionStrategy, DbContextEfTransactionStrategy>(DependencyLifeStyle.Transien‌​t);

Stored Procedure in Orchard

Hi We are working on a project and i am trying to call a stored procedure. I have searched for the solution but i didn't find any way that how to call a stored procedure so can any one please tell me how to execute stored procedure.
How ever finally i am using the below code to execute the stored procedure and get the result.
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Suppress))
{
// temporary scope for new connection and setting stored procedure, parameters, and return RecordName list
using (SqlConnection cn = new SqlConnection(_settingsManager.LoadSettings().First().DataConnectionString))
{
if (cn.State == ConnectionState.Closed)
{
cn.Open();
}
const string storedProcedure = "usp_spName";
SqlCommand cmd = new SqlCommand(storedProcedure, cn);
cmd.Parameters.AddWithValue("#IsVerified", "value");
cmd.Parameters.AddWithValue("#page", "value");
// for out put parameter
cmd.Parameters.AddWithValue("#totalRows", 0);
cmd.Parameters["#totalRows"].Direction = ParameterDirection.Output;
cmd.CommandType = CommandType.StoredProcedure;
IDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
// To Get The Values of the result
int id=Convert.ToInt32(reader["Id"].ToString());
}
reader.Close();
try
{
// To Get the Out Put Perameter
totalRecords = (int)cmd.Parameters["#totalRows"].Value;
}
catch
{
totalRecords = 0;
}
cmd.Parameters.Clear();
cmd.Dispose();
cn.Close();
}
}
actually, I don't know about stored procedures, but you can call stored functions using code like examaple below. I'm new to C# and orchard, so may be my approach is not correct and good enough. At first you should get ISessionLocator object instance, using IOrchardServices, then create NHibernate ISession instance and then IQuery/ISQLQuery with CreateSQLQuery().
here is example code of Services class
public class ExampleService {
private readonly IOrchardServices _oServices;
public EParamsServices(IOrchardServices oServices) {
_oServices = oServices;
}
public float GetRegionPopulationDencity(int rId){
//resolve ISession locator (you can do this using dependencies in ctor)
ISessionLocator sl = _oServices.WorkContext.Resolve<ISessionLocator>();
//create session
ISession _session = sl.For(null);
// create raw SQL query
return _session.CreateSQLQuery(#"SELECT data.RegionDencity(:rId) as result")
.AddScalar("result", NHibernateUtil.Single)
.SetParameter<int>("rId", rId)
.List<float>()
.FirstOrDefault<float>();
}
}
I think you can exec stored procs the same way, just change SQL code do exec yourProcName and ExecuteUpdate() instead of List() method (I'm not sure in this part)
you also should add reference to NHibernate assembly to your project and add NHibernate & Orchard.Data to your using part.
You have to reference NHibernate and System.Data in your module , then you can use the below code
var cmd = _transactionManager.GetSession().Connection.CreateCommand();
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.CommandText = "MyStoredProcedure";
_transactionManager.GetSession().Transaction.Enlist(cmd);
cmd.ExecuteNonQuery();

Task.WaitAll(...) blocks on MVC 4 application

In a MVC 4 application I have a task that copies a file to a destination folder. Because I have multiple files, I create a task for every file, and I want to wait untill they all finish. The problem is my code blocks at Task.WaitAll(copyingTasks.ToArray()) as if the tasks never end, so it never passes that line of code. Below you have the sample code:
private void CopyFilesFromWorkingCopyForProject(string projectName)
{
var copyingTasks = new List<Task>
{
CopyAllFromDirectoryToDirectory(FilesUtils.AndroidConfigsPath(), FilesUtils.AndroidPathForProject(projectName)),
CopyAllFromDirectoryToDirectory(FilesUtils.AndroidValuesPath(), FilesUtils.AndroidPathForProject(projectName)),
CopyFileToDirectory(FilesUtils.AndroidManifestPath(), FilesUtils.AndroidPathForProject(projectName)),
CopyAllFromDirectoryToDirectory(FilesUtils.IosConfigsPath(), FilesUtils.IosPathForProject(projectName))
};
Task.WaitAll(copyingTasks.ToArray());
}
private async Task CopyAllFromDirectoryToDirectory(string sourceDirectory, string destinationDirectory)
{
foreach (string filename in Directory.EnumerateFiles(sourceDirectory))
{
await CopyFileToDirectory(filename, destinationDirectory);
}
}
private async Task CopyFileToDirectory(string filename, string destinationDirectory)
{
using (FileStream sourceStream = File.Open(filename, FileMode.Open))
{
using (FileStream destinationStream = File.Create(destinationDirectory + filename.Substring(filename.LastIndexOf('\\'))))
{
await sourceStream.CopyToAsync(destinationStream);
}
}
}
If I comment Task.WaitAll(copyingTasks.ToArray()); it doesn't block anymore, but I want to wait for all the files to be copied.
Combining await and synchronous wait leads to deadlocks, because async methods try to resume on the context that's currently blocked by your wait.
What you should do instead is to make CopyFilesFromWorkingCopyForProject() also async (and the method that calls that, and the method that calls that, …):
private async Task CopyFilesFromWorkingCopyForProject(string projectName)
{
var copyingTasks = new List<Task>
{
CopyAllFromDirectoryToDirectory(FilesUtils.AndroidConfigsPath(), FilesUtils.AndroidPathForProject(projectName)),
CopyAllFromDirectoryToDirectory(FilesUtils.AndroidValuesPath(), FilesUtils.AndroidPathForProject(projectName)),
CopyFileToDirectory(FilesUtils.AndroidManifestPath(), FilesUtils.AndroidPathForProject(projectName)),
CopyAllFromDirectoryToDirectory(FilesUtils.IosConfigsPath(), FilesUtils.IosPathForProject(projectName))
};
await Task.WhenAll(copyingTasks);
}
If you can't or don't want to do that, you need to make sure the async methods don't resume on the current context. To do that, you can use ConfigureAwait(false) for all your awaits, or you can call the async methods on a background thread using Task.Run().

Using Massive Insert I get DBNull when trying to get scope_identity()

I have a table with an identity column.
Using Massive with code like this
var table = new Categories();
var newID = table.Insert(new {CategoryName = "Buck Fify Stuff", Description = "Things I like"});
then
table.Scalar("select scope_identity()");
returns DBNull :(
What do I need to do different to get the actual inserted identity value
The MSDN documentation states that SCOPE_IDENTITY:
"retrieves the last identity values that are generated in any table in the current session"
Looking at the Massive source code, it appears that every call to Scalar() opens a new connection:
/// <summary>
/// Returns a single result
/// </summary>
public virtual object Scalar(string sql, params object[] args) {
object result = null;
using (var conn = OpenConnection()) { // <-- see this ...
result = CreateCommand(sql, conn, args).ExecuteScalar();
}
return result;
}
...
/// <summary>
/// Returns and OpenConnection
/// </summary>
public virtual DbConnection OpenConnection() {
var result = _factory.CreateConnection();
result.ConnectionString = ConnectionString;
result.Open(); // <-- ...and this
return result;
}
Therefore, every time you are doing table.Scalar("select scope_identity()"); you are actually doing this in a new connection (which means a different session/scope).
This explains the DBNull result.
But since you are already doing:
var newID = table.Insert(...)
you might want to inspect the value of newID after the insert happens; I hope you'll find something nice in there.
At least, that's what the code for Insert() leads me to believe:
public virtual dynamic Insert(object o) {
var ex = o.ToExpando();
if (!IsValid(ex)) {
throw new InvalidOperationException("Can't insert: " + String.Join("; ", Errors.ToArray()));
}
if (BeforeSave(ex)) {
using (dynamic conn = OpenConnection()) {
var cmd = CreateInsertCommand(ex);
cmd.Connection = conn;
cmd.ExecuteNonQuery();
cmd.CommandText = "SELECT ##IDENTITY as newID";
ex.ID = cmd.ExecuteScalar();
Inserted(ex);
}
return ex;
} else {
return null;
}
}