Tedious: Permintaan hanya dapat dibuat dalam status LoggedIn, bukan status SentClientRequest (kode: 'EINVALIDSTATE')

Dibuat pada 28 Sep 2016  ·  23Komentar  ·  Sumber: tediousjs/tedious

Dalam contoh saya di bawah ini, saya mencoba menjalankan serangkaian fungsi secara serial. Tetapi Permintaan hanya dapat dibuat dalam status Masuk, jadi saya harus memeriksanya connection.state !== connection.STATE.LOGGED_IN . Dan seperti yang Anda lihat dalam fungsi Baca di bawah ini, permintaan harus dikembalikan ke antrian untuk menghilangkan kesalahan.

{ [RequestError: Requests can only be made in the LoggedIn state, not the SentClientRequest state]
  message: 'Requests can only be made in the LoggedIn state, not the SentClientRequest state',
  code: 'EINVALIDSTATE' }

Apakah ada cara yang lebih baik untuk mencapai ini? Saya melihat bahwa masalah serupa diangkat sebelum #19 dan #355 . Apakah ada cara yang disarankan untuk mengeksekusi beberapa permintaan pada koneksi?

Kode:

var Connection = require('tedious').Connection;  
var Request = require('tedious').Request;  
var TYPES = require('tedious').TYPES;  
var async = require('async');

// Create connection to database
var config = {
        userName: 'mylogin',  
        password: 'mypassword',  
        server: 'localhost',
        options: {
            // encrypt: true, /*If you are connecting to a Microsoft Azure SQL database, you will need this*/
            database: 'testdb'
        }
    }
var connection = new Connection(config);  

// Attempt to connect and execute queries if connection goes through
connection.on('connect', function(err) {  
    if (err) {
        console.log(err);
    }  
    else    {
        console.log("Connected");

        // Execute all functions in the array serially
        async.waterfall([
            function Start(callback){
                console.log("Starting...");
                callback(null, 'Jake', 'United States');
            },
            function Insert(name, location, callback){
                console.log("Inserting into Table...");

                request = new Request("INSERT dbo.employees (name, location) OUTPUT INSERTED.id VALUES (<strong i="14">@Name</strong>, @Location);", function(err){  
                     if (err) {
                            console.log(err);
                        }  
                    }
                );  
                request.addParameter('Name', TYPES.NVarChar, name);  
                request.addParameter('Location', TYPES.NVarChar, location);  

                request.on('row', function(columns) {  
                    columns.forEach(function(column) {  
                      if (column.value === null) {  
                        console.log('NULL');  
                      } else {  
                        console.log("Employee id inserted is " + column.value);  
                      }  
                    });  
                }); 

                // Check how many rows were inserted
                request.on('doneInProc', function(rowCount, more) {  
                    console.log(rowCount + ' row(s) inserted');  
                    callback(null, 'Jared');
                });             

                connection.execSql(request);  
            },
            function Read(callback){
                // Requests can only be made in the LoggedIn state, so check for that
                if (connection.state !== connection.STATE.LOGGED_IN) {
                    // Put the request back on the dispatcher if connection is not in LoggedIn state
                    setTimeout(Read, 0, callback);
                    return;
                }

                console.log("Reading rows from the Table...");

                // Create the request to read from table
                request = new Request("SELECT * FROM dbo.employees;", function(err) {  
                    if (err) {
                        console.log(err);
                    }  
                });  

                // Output the number of rows read 
                request.on('doneInProc', function (rowCount, more, rows) {  
                    console.log(rowCount + ' row(s) returned');  
                    callback(null);
                });  

                connection.execSql(request);  
            }],
            function Complete(err, result) {
                if(err) {
                    console.log("Error:", err);
                }
                else {
                    console.log("Done!");
                }
            }
        )
    }
});

Komentar yang paling membantu

Untuk orang-orang yang datang ke sini dari Google (seperti yang saya lakukan): kumpulan koneksi yang membosankan menawarkan cara yang bagus untuk mengatasi masalah ini. Menggunakan pool.acquire((err, connection) => ...) dan connection.release() adalah cara mudah untuk mencegah masalah konkurensi. Jika Anda tidak ingin banyak koneksi, konfigurasikan kumpulan dengan {min: 1, max: 1} untuk membuat serial kueri secara efektif.

Semua 23 komentar

Ada properti 'panggilan balik' yang dapat Anda atur pada Permintaan dan ada acara 'requestCompleted' pada Permintaan, keduanya tampak berfungsi. Dan itu adalah dua hal yang tidak kami coba :)

Mungkin ada beberapa pembersihan dan dokumentasi yang diperlukan untuk ini.

Coba ini dan lihat apakah itu berhasil.

Menggunakan 'panggilan balik':

  request.callback = function (err, rowCount, rows) {
      // rows is not being set but rowCount is. May be a bug.
      if (err) {
        // Error handling.
      } else {
        // Next SQL statement.
      }
  };

Menggunakan 'requestCompleted':

  request.on('requestCompleted', function () {
      // Next SQL statement.
  });

Saya benar-benar akan menaruh catatan ini di https://github.com/tediousjs/tedious/issues/61 dan menutup yang ini karena ini masalah yang sama.

Membuka kembali masalah. https://github.com/tediousjs/tedious/issues/61 sama sekali tidak terkait. Masalah yang dirujuk oleh @SaloniSonpal sebenarnya sudah ditutup. Jadi buka kembali ini untuk pelacakan.

Ini membutuhkan beberapa pembersihan dan dokumentasi.

Terima kasih @tvrprasad. Keduanya bekerja, dan jauh lebih bersih. Pendekatan callback memang memberikan keuntungan dalam menangani kesalahan. Event: 'requestCompleted' pasti perlu didokumentasikan.

(Mengirim dari ponsel saya, maafkan ketusnya).

Menyetel properti panggilan balik akan menimpa panggilan balik yang Anda lewati
ke konstruktor Permintaan. Sebagai gantinya, Anda harus memanggil panggilan balik dari
async di dalam panggilan balik yang diteruskan ke konstruktor Permintaan. Dengan cara ini kamu
juga dapat meneruskan kesalahan dengan benar ke async.

Ah, tidak terjawab bahwa panggilan balik disetel ke panggilan balik yang diteruskan. Jadi cara yang benar adalah:

  request = new Request(sqlQuery, function (err, rowCount, rows) {
    if (err) {
      // Error handling.
    } else {
      // Next SQL statement.
    }
  });

@arthurschreiber Ada alasan untuk acara 'requestCompleted'? Tampaknya berlebihan. Hal lain yang saya perhatikan adalah parameter baris yang diteruskan ke panggilan balik selalu kosong. Apakah ada skenario di mana itu akan memiliki data yang valid? Saya akan memperbarui dokumentasi.

Hal lain yang saya perhatikan adalah parameter baris yang diteruskan ke panggilan balik selalu kosong. Apakah ada skenario di mana itu akan memiliki data yang valid? Saya akan memperbarui dokumentasi.

Ada opsi rowCollectionOnRequestCompletion yang dapat ditentukan saat menghubungkan. Memilikinya sebagai opsi global agak aneh, saya pikir akan lebih masuk akal untuk memiliki per-permintaan ini.

Gagasan utama di balik ini adalah sebagai berikut: Jika opsi rowCollectionOnRequestCompletion diaktifkan, baris disimpan dalam memori hingga Request yang sesuai selesai (dan saat ini, saya pikir mereka bahkan disimpan hingga Request mendapat gc'ed). Dengan kumpulan hasil yang kecil, biasanya hal itu tidak menjadi masalah, tetapi dengan kumpulan hasil yang besar, hal itu dapat sangat berdampak negatif pada kinerja . Pendekatan yang lebih disukai adalah menggunakan callback rows saja, dan jika Anda perlu mengumpulkan semua baris ke dalam array, Anda dapat melakukannya sendiri.

Saya tidak berpikir ini optimal, memiliki per-permintaan yang dapat dikonfigurasi ini daripada per-koneksi mungkin akan lebih baik - tetapi konstruktor Request belum mengambil opsi apa pun, jadi tanda tangan metode harus berubah.

Adakah alasan untuk acara 'requestCompleted'?

Saya tidak yakin - ini ditambahkan sebelum saya mulai mengerjakan yang membosankan.

Dibuka #459 untuk melacak masalah rowCollectionOnRequestCompletion. Saya yakin kita bisa menambah dan menanganinya
dengan cara yang kompatibel ke belakang.

Menyetel properti panggilan balik akan menimpa panggilan balik yang Anda lewati
ke konstruktor Permintaan. Sebagai gantinya, Anda harus memanggil panggilan balik dari
async di dalam panggilan balik yang diteruskan ke konstruktor Permintaan. Dengan cara ini kamu
juga dapat meneruskan kesalahan dengan benar ke async.

@arthurschreiber Anda benar. Itu adalah cara yang tepat untuk tidak menimpa hasil/kesalahan. Bekerja seperti pesona :-)

Dibuat #464 untuk meningkatkan dokumentasi pada Event: 'requestCompleted'

Untuk orang-orang yang datang ke sini dari Google (seperti yang saya lakukan): kumpulan koneksi yang membosankan menawarkan cara yang bagus untuk mengatasi masalah ini. Menggunakan pool.acquire((err, connection) => ...) dan connection.release() adalah cara mudah untuk mencegah masalah konkurensi. Jika Anda tidak ingin banyak koneksi, konfigurasikan kumpulan dengan {min: 1, max: 1} untuk membuat serial kueri secara efektif.

Siapa pun tolong sarankan, mode otentikasi windows tidak berfungsi untuk konektivitas nodejs2Sql.

@wafaabbass tedious belum mendukung otentikasi terintegrasi windows. Kami sedang mengerjakannya, Anda dapat melacaknya di #415.

Hai semua, saya punya masalah yang sama.

debug:

Permintaan hanya dapat dibuat dalam status LoggedIn, bukan status SentClientRequest
(node:5180) UnhandledPromiseRejectionWarning: Penolakan janji yang tidak ditangani (id penolakan: 1): RequestError: Permintaan hanya dapat dibuat dalam status LoggedIn, bukan status SentClientRequest
(node:5180) [DEP0018] DeprecationWarning: Penolakan janji yang tidak ditangani tidak digunakan lagi. Di masa mendatang, penolakan janji yang tidak ditangani akan menghentikan proses Node.js dengan kode keluar bukan nol.

Apakah sudah teratasi? Ini adalah kode saya.

function createAsPromiseWithTransaction(callback, data, connection) {

  const transactionReady = (err) => {
    if (err) {
      return callback(err)
    }
    return callback(null)
  }

  const transactionFailed = (originalErr) =>
    (err) => {
      if (err) {
        return callback(err)
      }
      return callback(originalErr)
    }

  const willIGetArea = new Promise(
    (resolve, reject) => {
      const create = function(data, connection) {
        let areaIdInserted = null
        const request = new Request(CONSTANTS.SPNames.Area,
          (err, rowCount, rows) => err ? reject(err) : resolve(areaIdInserted))

        request.addParameter('IUser', TYPES.UniqueIdentifier, data.iUser)
        request.addParameter('ParentArea', TYPES.Int, data.parentArea)
        request.addParameter('Name', TYPES.VarChar, data.nameArea)
        request.addParameter('ShortName', TYPES.VarChar, data.shortNameArea)
        request.addParameter('Description', TYPES.VarChar, data.descriptionArea)
        request.addParameter('Gender', TYPES.Bit, data.gender)
        request.addParameter('Colour', TYPES.VarChar, data.colour)
        request.addParameter('X', TYPES.Int, data.x)
        request.addParameter('Y', TYPES.Int, data.y)
        request.addParameter('Active', TYPES.Int, data.active)

        request.on('row', function(columns) {
          areaIdInserted = columns[1].value
        });

        connection.callProcedure(request)
      }

      const transactionStarted = function(err) {
        if (err) return callback(err)
        create(data, connection)
      }

      connection.beginTransaction(transactionStarted)
    })

  const willIGetSymptom = function(areaId) {

    return new Promise(function(resolve, reject) {

      const create = function(data, connection, areaId) {
        let symptomIdInserted = null
        const request = new Request(CONSTANTS.SPNames.Symptom,
          (err, rowCount, rows) => err ? reject(err) : resolve(symptomIdInserted))

        request.addParameter('IUser', TYPES.UniqueIdentifier, data.iUser)
        request.addParameter('AreaId', TYPES.BigInt, areaId)
        request.addParameter('Code', TYPES.VarChar, data.code)
        request.addParameter('Name', TYPES.VarChar, data.nameSymptom)
        request.addParameter('ShortName', TYPES.VarChar, data.shortNameSymptom)
        request.addParameter('Description', TYPES.VarChar, data.descriptionSymptom)
        request.addParameter('Gender', TYPES.Bit, data.gender)
        request.addParameter('Active', TYPES.Bit, data.active)

        request.on('row', function(columns) {
          symptomIdInserted = columns[1].value
        });


        connection.callProcedure(request)
      }

      create(data, connection, areaId)

    })
  }

  const willIGetModifier = new Promise(function(resolve, reject) {

    const request = new Request(
      CONSTANTS.SPNames.Modifier,
      function(err, rowCount, rows) {
        if (err) {
          reject(err)
        } else {
          resolve(rowCount,rows)
        }
      })

    request.addParameter('IUser', TYPES.UniqueIdentifier, data.iUser)
    request.addParameter('Name', TYPES.VarChar, data.nameModifier)
    request.addParameter('Order', TYPES.SmallInt, data.order)
    request.addParameter('Active', TYPES.Bit, data.active)

    connection.callProcedure(request)

  })

  const runPromise = () =>
    willIGetArea
    .then( (areaId) =>
      willIGetSymptom(areaId)
      .then( (symptomId) =>
        willIGetModifier
        .then((rowCount,rows) => connection.commitTransaction(transactionReady))
        .catch((err) => connection.rollbackTransaction(transactionFailed(err)))
      )
      .catch((err) => connection.rollbackTransaction(transactionFailed(err)))
    )it 
    .catch((err) => connection.rollbackTransaction(transactionFailed(err)))

  runPromise()

}

Jika saya memanggil willIGetArea dan willIGetSymptom, semua ini berfungsi, tetapi jika saya memanggilnya dengan willGetModifier, saya juga memiliki masalah yang sama. Terima kasih banyak.

Jika seseorang merasa terbantu, karena Google mengembalikan halaman ini terlebih dahulu untuk pesan kesalahan ini.
Ini berfungsi untuk layanan berbasis koneksi tunggal saya untuk membuat pembungkus berbasis Janji untuk koneksi. Tidak yakin ini adalah solusi yang baik secara umum, karena saya bukan pengembang js yang berpengalaman:

class PromisedConnection {
    constructor(config) {
        // this.connection always points to a promise resolving after the last assigned request
        // initial setting is either resolved once a new connection is established or rejected if an error occurs
        this.connection = new Promise((resolve, reject) => {
            const dbConnection = new Connection(config);
            dbConnection.on("connect", function (err) {
                if (err) {
                    reject(err);
                }
                else {
                    resolve(dbConnection);
                }
            });
        });
    }

    execute(request) {
        const nextConnection = new Promise((resolve, reject) => {
            // after scheduling new request this.connection should be reassigned to be the last in promise queue
            this.connection
                .catch( (reason) => {
                    reject(reason);
                } )
                .then( (dbConnection) => { // a new request can be executed only within the connection is free again (resolved after the last request)
                    request.on("requestCompleted", () => { // add an additional event listener in order to release connection after the request is done
                        resolve(dbConnection);
                    });
                    dbConnection.execSql(request);
                });
        } );
        this.connection = nextConnection;
    }
}

Dan kueri itu sendiri dapat dieksekusi seperti ini:

const dbConnection = new PromisedConnection(config);

function getObjList(query, parameters = []) {
    return new Promise((resolve, reject) => {
        const objList = [];
        let request = new Request(
            query,
            function (err, rowCount, rows) {
                if (err) {
                    reject(err);
                } else if (rowCount < 1) {
                    reject(new Error("0 rows returned from DB"));
                }
            }
        );
        for (const {name, type, value} of parameters) {
            request.addParameter(name, type, value);
        };

        request.on("row", (columns) => {
            objList.push(Obj.fromColumns(columns)); // here I just make a specific object from each row
        });

        request.on("requestCompleted", () => {
            resolve(objList);
        });

        dbConnection.execute(request);
    });
}

Jadi getObjList sekarang dapat dieksekusi kapan saja tanpa memeriksa status koneksi.

Kekhawatiran di sini baik-baik saja. Dalam kode saya, saya melakukan permintaan koneksi, permintaan sql, dan kemudian permintaan insert. Itu kesalahan pada permintaan penyisipan kecuali saya melakukan permintaan koneksi lain antara sql dan permintaan penyisipan bahkan setelah saya menunggu requestCompleted setelah permintaan sql.

Alasan umum untuk kesalahan ini adalah bahwa _hanya satu kueri yang dapat dieksekusi pada koneksi dalam satu waktu_. Anda harus menunggu sampai permintaan panggilan balik dijalankan, baik dengan kesalahan atau dengan hasil sebelum membuat permintaan lain. Silakan kunjungi halaman FAQ di sini

Karena sepertinya ada terlalu banyak berbagai masalah di forum ini. Kesalahan Requests can only be made in the LoggedIn state, not the SentClientRequest state dapat disebabkan oleh berbagai alasan yang berbeda. Jika Anda mengalami kesalahan ini dan Anda tidak menemukan halaman FAQ membantu, silakan angkat masalah Anda sendiri dengan informasi berikut sehingga kami dapat mengatasi masalah Anda dengan lebih tepat:

  • Versi yang Membosankan
  • Konfigurasi Anda untuk Koneksi
  • Skrip yang dapat direproduksi

Terima kasih! 😄.

Mampu menyelesaikan ini, masalah ada di pihak saya, tidak menyadari bahwa Anda memerlukan koneksi terpisah untuk setiap permintaan

@mgarf Apa solusi Anda? Saya juga tidak menyadari bahwa saya memerlukan koneksi terpisah untuk setiap permintaan. Saya mengalami masalah konkurensi seperti yang lain dalam diskusi ini.

@mikebutak Anda tidak perlu koneksi terpisah untuk setiap permintaan. Anda hanya perlu satu koneksi menangani satu permintaan _pada satu waktu_. Untuk menangani beberapa permintaan pada satu koneksi, Anda perlu menjalankan permintaan baru di panggilan balik yang sebelumnya, setelah selesai. Sebagai contoh,

function executeStatement() {
  request = new Request("select 42, 'hello world'", function(err, rowCount) {
    if (err) {
      console.log(err);
    } else {
      console.log(rowCount + ' rows');
    }

    connection.execSql(new Request("select 42, 'hello world'", function(err, rowCount) {
      if (err) {
        console.log(err);
      } else {
        console.log(rowCount + ' rows');
      }
      connection.close();
    }))
  });
  connection.execSql(request);

Saya memposting solusi lengkap menggunakan panggilan balik untuk masalah ini. Saya memiliki masalah yang sama dan ketika mencari di google saya berakhir di sini. Saya memuat menguji kode ini dengan skrip yang mengeksekusi 1 panggilan http setiap detik dan juga setengah detik tanpa masalah.

const {Connection, Request} = require("tedious");

const executeSQL = (sql, callback) => {
  let connection = new Connection({
    "authentication": {
      "options": {
        "userName": "USERNAME",
        "password": "PASSWORD"
      },
      "type": "default"
    },
    "server": "SERVER",
    "options": {
      "validateBulkLoadParameters": false,
      "rowCollectionOnRequestCompletion": true,
      "database": "DATABASE",
      "encrypt": true
    }
  });

  connection.connect((err) => {
    if (err)
      return callback(err, null);

    const request = new Request(sql, (err, rowCount, rows) => {
      connection.close();

      if (err)
        return callback(err, null);

      callback(null, {rowCount, rows});
    });

    connection.execSql(request);
  });
};

executeSQL("SELECT * FROM users", (err, data) => {
  if (err)
    console.error(err);

  console.log(data.rowCount);
});

//or

executeSQL("SELECT * FROM users", (err, {rowCount, rows}) => {
  if (err)
    console.error(err);

  console.log(rowCount);
});

Saya memposting solusi lengkap menggunakan panggilan balik untuk masalah ini. Saya memiliki masalah yang sama dan ketika mencari di google saya berakhir di sini. Saya memuat menguji kode ini dengan skrip yang mengeksekusi 1 panggilan http setiap detik dan juga setengah detik tanpa masalah.

const {Connection, Request} = require("tedious");

const executeSQL = (sql, callback) => {
  let connection = new Connection({
    "authentication": {
      "options": {
        "userName": "USERNAME",
        "password": "PASSWORD"
      },
      "type": "default"
    },
    "server": "SERVER",
    "options": {
      "validateBulkLoadParameters": false,
      "rowCollectionOnRequestCompletion": true,
      "database": "DATABASE",
      "encrypt": true
    }
  });

  connection.connect((err) => {
    if (err)
      return callback(err, null);

    const request = new Request(sql, (err, rowCount, rows) => {
      connection.close();

      if (err)
        return callback(err, null);

      callback(null, {rowCount, rows});
    });

    connection.execSql(request);
  });
};

executeSQL("SELECT * FROM users", (err, data) => {
  if (err)
    console.error(err);

  console.log(data.rowCount);
});

//or

executeSQL("SELECT * FROM users", (err, {rowCount, rows}) => {
  if (err)
    console.error(err);

  console.log(rowCount);
});

Solusi ini berfungsi tetapi apakah ini implementasi yang valid? Seberapa layak itu, mengingat mungkin ada ratusan ribu sisipan/pilihan dan membuat koneksi untuk masing-masingnya? Terima kasih atas solusinya btw, saya hanya mencoba memahami apakah itu solusi yang layak. Apalagi jika kita menggunakan Fungsi Azure?

Apakah halaman ini membantu?
0 / 5 - 0 peringkat

Masalah terkait

ghost picture ghost  ·  5Komentar

tvrprasad picture tvrprasad  ·  5Komentar

jstephens7 picture jstephens7  ·  5Komentar

SaloniSonpal picture SaloniSonpal  ·  5Komentar

ggazulla picture ggazulla  ·  4Komentar