После использования window.location функция asyn c ведет себя как функция syn c - PullRequest
0 голосов
/ 06 мая 2020

Я работал над проектом ASP.NET MVC 4, в котором пользователи могут применять фильтр к пользовательскому интерфейсу и извлекать отчет Excel. Данные хранились в MS SQL server. Чтобы повысить производительность, я решил использовать async в своем приложении, чтобы:

  1. Сократить время извлечения
  2. Разрешить пользователям иметь параллельное извлечение

Для этого я использую следующие методы:

  1. Когда пользователь нажимает кнопку извлечения в пользовательском интерфейсе -> от клиента будет выполнен вызов ajax к функции async на сервере -> Эта функция async, в свою очередь, создаст объект Command и сделает ExecuteReaderAsync(). Используя этот DbDatareader, сгенерируйте файл Excel с помощью NPOI и сохраните содержимое файла в TempData. Обработчик для получения файла будет возвращен клиенту для последующей загрузки с использованием window.location. Я перенял эти методы из этой публикации. Загрузить файл Excel через AJAX MVC

  2. После первого извлечения, если пользователи хотят извлекать другие наборы данных параллельно, они могут нажать кнопку извлечения еще раз, и приложение повторит шаг 1.

В результате может произойти 2 или более извлечения данных одновременно.

Моя проблема, возьмем, к примеру, 4 извлечения, которые в настоящее время выполняются параллельно, если какое-либо из этих извлечений завершено и загружен 1 файл (с использованием window.location). В следующий раз, когда пользователь нажмет кнопку извлечения (которая повторяет шаг 1), он больше не будет асинхронным c, и более поздние извлечения будут ждать завершения предыдущего извлечения sh перед выполнением.

При отладке, если я перезапустите сервер ISS, проблема исчезла на некоторое время, пока не будет загружен 1 файл, поэтому я сомневался, что window.location делает что-то, что блокирует потоки на сервере при загрузке любого файла.

ОБНОВЛЕНИЕ 1

Класс:

public class QUERYREADER
{
    public DbConnection   CONNECTION { get; set; }
    public DbDataReader   READER { get; set; }
}

Модель:

public async Task<QUERYREADER> GET_DATA(CancellationToken ct)
{
    //Create the query reader
    QUERYREADER qr  = new QUERYREADER();

    //Set up the database instances
    DbProviderFactory dbFactory = DbProviderFactories.GetFactory(db.Database.Connection);

    //Defined the query
    var query = "SELECT * FROM Table";

    //Set up the sql command object
    using (var cmd = dbFactory.CreateCommand())
    {
        //Try to open the database connection 
        try
        {
            //Check if SQL connection is set up
            if (cmd.Connection == null)
            {
                cmd.CommandType = CommandType.Text;
                cmd.Connection  = db.Database.Connection;
            } 
            //Open connection to SQL if current state is closed
            if (cmd.Connection.State == ConnectionState.Closed)
            {
                //Change the connection string to set the packet size to max. value = 32768 to improve efficiency for I/O transmit to SQL server 
                cmd.Connection.ConnectionString = cmd.Connection.ConnectionString + ";Packet Size=20000";
                //Open connection
                await cmd.Connection.OpenAsync(ct);
            }
            //Save the connection 
            qr.CONNECTION = cmd.Connection;

        } catch (Exception ex) {
            //If errors throw, close the connection
            cmd.Connection.Close();
        };


        //Retrieve the database reader of provided sql query
        cmd.CommandText = query;       
        DbDataReader dr = await cmd.ExecuteReaderAsync(ct);
        qr.READER.Add(dr);       
    }  

    //Return the queryreader
    return qr;
}

Контроллер:

  public async Task<JsonResult> SQL_TO_EXCEL()
  {        
       //Set up the subscription to client for "cancellation request, browser closing"
        CancellationToken   disToken = Response.ClientDisconnectedToken;             

        //Get the datareader
        try 
        {
            qr  = await GET_DATA(disToken);        
        } 
        catch(Exception ex) { }

        //Open the connection to SQL server
        using (qr.CONNECTION)
        {
            using (var dr = qr.READER)
            {                                            
                while (await dr.ReadAsync(disToken))
                {
                    for (int k = 0; k < dr.FieldCount; k++)
                    {
                        //.... using NPOI to write Excel file to MemoryStream
                    }
                }
                dr.Close();
            }
        }

        //Generate XL file if controller action is still running (no "cancellation request, browser closing")
        if (!disToken.IsCancellationRequested)
        {

            string file_id = Guid.NewGuid().ToString();
            //... Write the NPOI excel file to TempData and then create a handler for later download at client
            //This line caused trouble
            TempData["file_id"] = XLMemoryStream.ToArray();

            HANDLER["file_id"]      = file_id;
            HANDLER["file_name"]    = FILE["FILE_NAME"].ToString().NonUnicode() + FILE["FILE_TYPE"].ToString() ;
        }

        //Return JSON to caller
        var JSONRESULT                  = Json(JsonConvert.SerializeObject(HANDLER), JsonRequestBehavior.AllowGet);
            JSONRESULT.MaxJsonLength    = int.MaxValue;

        return JSONRESULT;
  }

    public async Task<ActionResult> DOWNLOAD_EXCEL(string file_id, string file_name)
    {
        if (TempData[file_id] != null)
        {
            byte[] data = await Task.Run(() => TempData[file_id] as byte[]);
            return File(data, "application/vnd.ms-excel", file_name);
        }
        else
        {
            return new EmptyResult();
        }
    }   

Javascript

    $.ajax({
        type: 'POST',
        async: true,
        cache: false,
        url:  'SQL_TO_EXCEL',
        success: function (data)
        {
            var response = JSON.parse(data);
            window.location =
            (
                "DOWNLOAD_EXCEL"    +
                '?file_id='         + response.file_id +
                '&file_name='       + response.file_name
            );
        },
        error: function (XMLHttpRequest, textStatus, errorThrown) {
            console.log(errorThrown);
        }
    });    

ОБНОВЛЕНИЕ 2:

После множества тестов я понял, что window.location не имеет ничего общего с потоками на сервере, строка TempData[file_id] = XLMemoryStream.ToArray() вызвали проблемы. Похоже, проблема аналогична описанной в этом посте. Два параллельных ajax запроса к методам Action поставлены в очередь, почему?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...