Tedious: يمكن تقديم الطلبات فقط في حالة تسجيل الدخول ، وليس حالة SentClientRequest (الرمز: "EINVALIDSTATE")

تم إنشاؤها على ٢٨ سبتمبر ٢٠١٦  ·  23تعليقات  ·  مصدر: tediousjs/tedious

في المثال أدناه ، أحاول تنفيذ مجموعة من الوظائف بشكل تسلسلي. لكن الطلبات لا يمكن إجراؤها إلا في حالة تسجيل الدخول ، لذلك كان علي التحقق من ذلك connection.state !== connection.STATE.LOGGED_IN . وكما ترى في وظيفة القراءة أدناه ، كان يجب إعادة الطلب إلى قائمة الانتظار للتخلص من الخطأ.

{ [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' }

هل هناك طريقة أفضل لتحقيق ذلك؟ أرى أنه تم طرح مشكلات مماثلة قبل # 19 و # 355 . هل هناك طريقة موصى بها لتنفيذ طلبات متعددة على اتصال؟

رمز:

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!");
                }
            }
        )
    }
});

التعليق الأكثر فائدة

بالنسبة للأشخاص القادمين إلى هنا من Google (مثلما فعلت): تقدم مجموعة الاتصال المملة طريقة رائعة لمواجهة هذه المشكلة. يعد استخدام pool.acquire((err, connection) => ...) و connection.release() طريقة سهلة لمنع مشاكل التزامن. إذا كنت لا تريد اتصالات متعددة ، فقم بتكوين التجمع باستخدام {min: 1, max: 1} لإجراء تسلسل فعال للاستعلامات.

ال 23 كومينتر

هناك خاصية "رد الاتصال" يمكنك تعيينها عند الطلب وهناك حدث "تم الطلب" عند الطلب ، وكلاهما يبدو عملاً. وهذان هما الشيئان اللذان لم نحاولهما :)

قد يكون هناك بعض التنظيف والوثائق المطلوبة في هذا الشأن.

جرب هذه ومعرفة ما إذا كانت تعمل.

باستخدام "رد الاتصال":

  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.
      }
  };

باستخدام "requestCompleted":

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

سأضع هذه الملاحظات في الواقع في https://github.com/tediousjs/tedious/issues/61 وأغلق هذه الملاحظة لأنها نفس المشكلة.

إعادة فتح القضية. https://github.com/tediousjs/tedious/issues/61 لا علاقة له تمامًا. المشكلات المشار إليها بواسطة SaloniSonpal مغلقة بالفعل. إعادة فتح هذا للتتبع.

هذا يحتاج إلى بعض التنظيف والتوثيق.

شكراtvrprasad. كلاهما يعمل ، وهما طريقة أنظف. يعطي الأسلوب callback ميزة معالجة الخطأ. يجب توثيق Event: 'requestCompleted' بالتأكيد.

(الإرسال من هاتفي ، معذرة البُعد).

سيؤدي تعيين خاصية رد الاتصال إلى الكتابة فوق رد الاتصال الذي مررته
إلى مُنشئ الطلب. بدلاً من ذلك ، يجب عليك استدعاء رد الاتصال من
تم تمرير غير متزامن داخل رد الاتصال إلى مُنشئ الطلب. بهذه الطريقة أنت
يمكن أيضًا إعادة توجيه الأخطاء بشكل صحيح إلى غير المتزامن.

آه ، فاتنا أن رد النداء تم تعيينه على رد الاتصال الذي تم تمريره. لذا فإن الطريقة الصحيحة ستكون:

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

arthurschreiber هل من سبب لحدث "requestCompleted"؟ يبدو زائدا عن الحاجة. شيء آخر لاحظته هو أن معامل الصفوف الذي تم تمريره إلى رد الاتصال فارغ دائمًا. هل هناك سيناريو يحتوي على بيانات صحيحة؟ سوف أقوم بتحديث الوثائق.

شيء آخر لاحظته هو أن معامل الصفوف الذي تم تمريره إلى رد الاتصال فارغ دائمًا. هل هناك سيناريو يحتوي على بيانات صحيحة؟ سوف أقوم بتحديث الوثائق.

يوجد الخيار rowCollectionOnRequestCompletion الذي يمكن تحديده عند الاتصال. إن امتلاكها كخيار عالمي أمر غريب بعض الشيء ، وأعتقد أنه سيكون من المنطقي أن يكون لديك هذا لكل طلب.

الفكرة الرئيسية وراء ذلك هي ما يلي: إذا تم تمكين الخيار rowCollectionOnRequestCompletion يتم تخزين الصفوف في الذاكرة حتى يكتمل Request المقابل (وفي الوقت الحالي ، أعتقد أنه يتم الاحتفاظ بها حتى Request تحصل على شهادة gc'ed). مع مجموعات النتائج الصغيرة ، لا يمثل ذلك عادةً مشكلة ، ولكن مع مجموعات النتائج الكبيرة ، يمكن أن يؤثر ذلك سلبًا على الأداء كثيرًا . الطريقة المفضلة هي استخدام رد النداء rows ، وإذا كنت بحاجة إلى تجميع كل الصفوف في مصفوفة ، يمكنك القيام بذلك بنفسك.

لا أعتقد أن هذا هو الأمثل ، فمن المحتمل أن يكون الحصول على هذا القابل للتكوين لكل طلب بدلاً من كل اتصال أفضل - لكن المُنشئ Request لا يتخذ أي خيارات حتى الآن ، لذلك يجب أن يكون توقيع الطريقة تغير.

أي سبب لحدث "تم إكمال الطلب"؟

لست متأكدا - تمت إضافة هذا قبل أن أبدأ العمل على مملة.

تم فتح # 459 لتعقب مشكلة rowCollectionOnRequestCompletion. أنا متأكد من أنه يمكننا إضافة ذلك والتعامل معه
بطريقة متوافقة مع الإصدارات السابقة.

سيؤدي تعيين خاصية رد الاتصال إلى الكتابة فوق رد الاتصال الذي مررته
إلى مُنشئ الطلب. بدلاً من ذلك ، يجب عليك استدعاء رد الاتصال من
تم تمرير غير متزامن داخل رد الاتصال إلى مُنشئ الطلب. بهذه الطريقة أنت
يمكن أيضًا إعادة توجيه الأخطاء بشكل صحيح إلى غير المتزامن.

arthurschreiber أنت على حق. هذه هي الطريقة الصحيحة لعدم تجاوز النتائج / الأخطاء. يعمل كالسحر :-)

تم إنشاء # 464 لتحسين التوثيق على Event: 'requestCompleted'

بالنسبة للأشخاص القادمين إلى هنا من Google (مثلما فعلت): تقدم مجموعة الاتصال المملة طريقة رائعة لمواجهة هذه المشكلة. يعد استخدام pool.acquire((err, connection) => ...) و connection.release() طريقة سهلة لمنع مشاكل التزامن. إذا كنت لا تريد اتصالات متعددة ، فقم بتكوين التجمع باستخدام {min: 1, max: 1} لإجراء تسلسل فعال للاستعلامات.

يُرجى من أي شخص أن يقترح ، وضع مصادقة Windows لا يعمل لاتصال nodejs2Sql.

wafaabbass tedious لا يدعم مصادقة Windows المتكاملة حتى الآن. نحن نعمل على ذلك ، يمكنك تتبعه في # 415.

مرحبًا ، لدي نفس المشكلة.

تصحيح:

لا يمكن إجراء الطلبات إلا في حالة تسجيل الدخول ، وليس في حالة SentClientRequest
(العقدة: 5180) UnhandledPromiseRejectionWarning: رفض الوعد الذي لم تتم معالجته (معرّف الرفض: 1): RequestError: لا يمكن تقديم الطلبات إلا في حالة LoggedIn ، وليس حالة SentClientRequest
(العقدة: 5180) [DEP0018] تحذير الإيقاف: تم إهمال رفض الوعود غير المعالجة. في المستقبل ، ستؤدي حالات رفض الوعد التي لم تتم معالجتها إلى إنهاء عملية Node.js برمز إنهاء غير صفري.

هل تم حلها بعد؟ هذا هو الكود الخاص بي.

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()

}

إذا اتصلت بـ willIGetArea و willIGetSymptom ، فإن كل هذا يعمل ، ولكن إذا اتصلت به باستخدام willGetModifier ، فأنا أعاني من نفس المشكلة أيضًا. شكرا جزيلا.

في حالة ما إذا وجدها شخص ما مفيدة ، حيث تقوم Google بإرجاع هذه الصفحة أولاً لرسالة الخطأ هذه.
لقد عملت مع خدمتي القائمة على الاتصال الفردي لإنشاء غلاف مستند إلى Promise للاتصال. لست متأكدًا من أنه حل جيد بشكل عام ، فأنا لست مطور js متمرسًا:

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;
    }
}

ويمكن تنفيذ الاستعلام نفسه على النحو التالي:

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);
    });
}

لذلك يمكن تنفيذ getObjList الآن في أي وقت دون التحقق من حالة الاتصال.

القلق هنا جيد. في الكود الخاص بي ، أقوم بطلب اتصال وطلب SQL ثم طلب إدراج. إنها أخطاء في طلب الإدراج ما لم أقوم بطلب اتصال آخر بين sql وطلب الإدراج حتى بعد انتظار requestCompleted بعد طلب sql.

من الأسباب الشائعة لهذا الخطأ أنه يمكن تنفيذ استعلام واحد فقط على اتصال في المرة الواحدة. أنت بحاجة إلى الانتظار حتى يتم تنفيذ طلب رد الاتصال ، إما بخطأ أو بالنتيجة قبل تقديم طلب آخر. الرجاء زيارة صفحة الأسئلة الشائعة هنا

لأنه يبدو أن هناك العديد من القضايا المختلفة في هذا المنتدى. يمكن أن يكون سبب الخطأ Requests can only be made in the LoggedIn state, not the SentClientRequest state عدة أسباب مختلفة. إذا واجهت هذا الخطأ ولم تجد صفحة الأسئلة الشائعة مفيدة ، فالرجاء إثارة مشكلتك بالمعلومات التالية حتى نتمكن من معالجة مشكلتك بمزيد من الدقة:

  • نسخة مملة
  • التكوين الخاص بك للاتصال
  • نص قابل للاستنساخ

شكرا! 😄

تمكنت من حل هذا ، كانت المشكلة من جانبي ، ولم أكن أدرك أنك بحاجة إلى اتصال منفصل لكل طلب

mgarf ماذا كان الحل الخاص بك؟ لم أدرك أيضًا أنني بحاجة إلى اتصال منفصل لكل طلب. أواجه مشكلات التزامن مثل الآخرين في هذه المناقشة.

mikebutak لا تحتاج بالضرورة إلى اتصال منفصل لكل طلب. أنت فقط بحاجة إلى اتصال واحد يتعامل مع طلب واحد _في كل مرة_. للتعامل مع الطلبات المتعددة على اتصال واحد ، تحتاج إلى تنفيذ طلب جديد في رد الاتصال الخاص بالاتصال السابق ، بعد انتهائه. علي سبيل المثال،

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);

أقوم بنشر حل كامل باستخدام عمليات الاسترجاعات لهذه المشكلة. واجهت نفس المشكلة وعندما نظرت إلى google انتهى بي الأمر هنا. لقد قمت بتحميل هذا الرمز الذي تم اختباره باستخدام برنامج نصي يقوم بتنفيذ مكالمة http 1 كل ثانية وأيضًا نصف ثانية دون أي مشاكل.

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);
});

أقوم بنشر حل كامل باستخدام عمليات الاسترجاعات لهذه المشكلة. واجهت نفس المشكلة وعندما نظرت إلى google انتهى بي الأمر هنا. لقد قمت بتحميل هذا الرمز الذي تم اختباره باستخدام برنامج نصي يقوم بتنفيذ مكالمة http 1 كل ثانية وأيضًا نصف ثانية دون أي مشاكل.

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);
});

هذا الحل يعمل ولكن هل هو تطبيق صحيح؟ ما مدى جدوى ذلك ، مع الأخذ في الاعتبار أنه قد يكون هناك مئات الآلاف من الإدخالات / التحديدات وإنشاء اتصال لكل منها؟ شكرا على الحل راجع للشغل ، أنا فقط أحاول أن أفهم ما إذا كان حلا قابلا للتطبيق. خاصة إذا كنا نستخدم وظيفة Azure؟

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات

القضايا ذات الصلة

tvrprasad picture tvrprasad  ·  6تعليقات

jstephens7 picture jstephens7  ·  5تعليقات

David-Engel picture David-Engel  ·  5تعليقات

aniltomar10 picture aniltomar10  ·  5تعليقات

diginfo picture diginfo  ·  6تعليقات