SqlParameter पहले से ही एक और SqlParameterCollection द्वारा समाहित है - क्या () {} धोखा का उपयोग करता है?


85

using() {}नीचे दिखाए अनुसार (सिक) ब्लॉकों का उपयोग करते हुए , और यह मानते हुए कि cmd1पहले using() {}ब्लॉक के दायरे से बाहर नहीं रहता है , दूसरे ब्लॉक को संदेश के साथ अपवाद क्यों फेंकना चाहिए

SqlParameter पहले से ही एक और SqlParameterCollection द्वारा समाहित है

क्या इसका मतलब है कि संसाधन और / या हैंडल - पैरामीटर सहित ( SqlParameterCollection) - संलग्न cmd1नहीं हैं, जब ब्लॉक के अंत में इसे नष्ट कर दिया जाता है?

using (var conn = new SqlConnection("Data Source=.;Initial Catalog=Test;Integrated Security=True"))
{
    var parameters = new SqlParameter[] { new SqlParameter("@ProductId", SqlDbType.Int ) };

    using(var cmd1 = new SqlCommand("SELECT ProductName FROM Products WHERE ProductId = @ProductId"))
    {
        foreach (var parameter in parameters)
        {
            cmd1.Parameters.Add(parameter);                
        }
        // cmd1.Parameters.Clear(); // uncomment to save your skin!
    }

    using (var cmd2 = new SqlCommand("SELECT Review FROM ProductReviews WHERE ProductId = @ProductId"))
    {
        foreach (var parameter in parameters)
        {
            cmd2.Parameters.Add(parameter);
        }
    }
}

नोट: कर रहे हैं cmd1.Parameters.Clear () पहले का उपयोग कर के अंतिम ब्रेस से पहले () {} ब्लॉक आपको अपवाद (और संभव शर्मिंदगी) से बचाएगा।

यदि आपको पुन: पेश करने की आवश्यकता है, तो आप वस्तुओं को बनाने के लिए निम्न स्क्रिप्ट का उपयोग कर सकते हैं:

CREATE TABLE Products
(
    ProductId int IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED,
    ProductName nvarchar(32) NOT NULL
)
GO

CREATE TABLE ProductReviews
(
    ReviewId int IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED,
    ProductId int NOT NULL,
    Review nvarchar(128) NOT NULL
)
GO

मैं यह भी देख रहा हूँ, लेकिन यह तय नहीं किया। निराशा होती। और मैं केवल एक ही cmd ऑब्जेक्ट का उपयोग कर रहा हूं, पुन: उपयोग नहीं किया गया है। यह एक async रिट्री लूप में लिपटे हुए है, इसलिए यह शायद एक ही मूल कारण है कि उसी तरह से नहीं बचा है।
एड विलियम्स

जवाबों:


109

मुझे संदेह है कि यह SqlParameter"जानता है" कि यह किस कमांड का हिस्सा है, और यह कि कमांड के निपटारे के समय यह जानकारी क्लियर नहीं होती है, लेकिन कॉल करने पर यह क्लियर हो जाता हैcommand.Parameters.Clear()

व्यक्तिगत रूप से मुझे लगता है कि मैं पहली जगह में वस्तुओं के पुन: उपयोग से बचूंगा, लेकिन यह आपके ऊपर है :)


2
धन्यवाद। मुझे संदेह था कि मामला यही है। इसका मतलब यह भी होगा कि SqlParameter खुद को एक
विवादित

@ जॉनगोथोगो: ठीक है, यह एक ऐसी वस्तु से जुड़ा है जिसे संघ के गठन के बाद निपटाया गया है। यह आदर्श नहीं है, निश्चित रूप से।
जॉन स्कीट

11
दूसरों के लिए एक नोट। मुझे Clearपहले usingब्लॉक से बाहर निकलने से पहले प्रदर्शन करना था । मेरे दूसरे usingब्लॉक में प्रवेश करते समय यह करना अभी भी इस त्रुटि को फेंक देता है।
स्नेक

9

ब्लॉकों का उपयोग यह सुनिश्चित नहीं करता है कि एक वस्तु "नष्ट" हो जाती है, बस यह कि Dispose()विधि कहा जाता है। जो वास्तव में विशिष्ट कार्यान्वयन तक है और इस मामले में यह स्पष्ट रूप से संग्रह को खाली नहीं करता है। यह विचार यह सुनिश्चित करने के लिए है कि कचरा प्रबंधन द्वारा साफ नहीं किए जाने वाले मानव रहित संसाधनों को सही ढंग से निपटाया जाए। जैसा कि पैरामीटर संग्रह एक अप्रबंधित संसाधन नहीं है, यह पूरी तरह से आश्चर्यचकित करने वाला नहीं है क्योंकि यह निपटान विधि द्वारा साफ नहीं किया गया है।


7

Cmd.Parameters.Clear जोड़ना (); निष्पादन के बाद ठीक होना चाहिए।


3

usingएक गुंजाइश को परिभाषित करता है, और वह स्वचालित कॉल करता है Dispose()जिसके लिए हम इसे प्यार करते हैं।

यदि किसी अन्य ऑब्जेक्ट का संदर्भ है, तो इस दायरे से बाहर होने वाला संदर्भ स्वयं "गायब" नहीं होगा, जो इस मामले parametersमें संदर्भ होने के लिए होगा cmd1


2

मुझे इसके अलावा एक ही मुद्दा मिला है धन्यवाद @Jon, इसके आधार पर मैंने उदाहरण दिया।

जब मैंने नीचे दिए गए फ़ंक्शन को कॉल किया जिसमें 2 बार समान sqlparameter पारित हुआ। पहले डेटाबेस कॉल में, इसे ठीक से कॉल किया गया था, लेकिन दूसरी बार में, यह उपरोक्त त्रुटि दे रहा था।

    public Claim GetClaim(long ClaimId)
    {
        string command = "SELECT * FROM tblClaim "
            + " WHERE RecordStatus = 1 and ClaimId = @ClaimId and ClientId =@ClientId";
        List<SqlParameter> objLSP_Proc = new List<SqlParameter>(){
                new SqlParameter("@ClientId", SessionModel.ClientId),
                new SqlParameter("@ClaimId", ClaimId)
            };

        DataTable dt = GetDataTable(command, objLSP_Proc);
        if (dt.Rows.Count == 0)
        {
            return null;
        }

        List<Claim> list = TableToList(dt);

        command = "SELECT * FROM tblClaimAttachment WHERE RecordStatus = 1 and ClaimId = @ClaimId and ClientId =@ClientId";

        DataTable dt = GetDataTable(command, objLSP_Proc); //gives error here, after add `sqlComm.Parameters.Clear();` in GetDataTable (below) function, the error resolved.


        retClaim.Attachments = new ClaimAttachs().SelectMany(command, objLSP_Proc);
        return retClaim;
    }

यह सामान्य डीएएल फ़ंक्शन है

       public DataTable GetDataTable(string strSql, List<SqlParameter> parameters)
        {
            DataTable dt = new DataTable();
            try
            {
                using (SqlConnection connection = this.GetConnection())
                {
                    SqlCommand sqlComm = new SqlCommand(strSql, connection);

                    if (parameters != null && parameters.Count > 0)
                    {
                        sqlComm.Parameters.AddRange(parameters.ToArray());
                    }

                    using (SqlDataAdapter da = new SqlDataAdapter())
                    {
                        da.SelectCommand = sqlComm;
                        da.Fill(dt);
                    }
                    sqlComm.Parameters.Clear(); //this added and error resolved
                }
            }
            catch (Exception ex)
            {                   
                throw;
            }
            return dt;
        }

2

मुझे इस विशेष त्रुटि का सामना करना पड़ा क्योंकि मैं कई बार किसी प्रक्रिया को कॉल करने के लिए SqlParameter संग्रह के हिस्से के रूप में समान SqlParameter ऑब्जेक्ट का उपयोग कर रहा था। इस त्रुटि IMHO के लिए कारण है कि SqlParameter ऑब्जेक्ट किसी विशेष SqlParameter संग्रह से संबंधित हैं और आप एक नया SqlParameter संग्रह बनाने के लिए समान SqlParameter ऑब्जेक्ट का उपयोग नहीं कर सकते हैं।

तो, इसके बजाय:

var param1 = new SqlParameter{ DbType = DbType.String, ParameterName = param1,Direction = ParameterDirection.Input , Value = "" };
var param2 = new SqlParameter{ DbType = DbType.Int64, ParameterName = param2, Direction = ParameterDirection.Input , Value = 100};

SqlParameter[] sqlParameter1 = new[] { param1, param2 };

ExecuteProc(sp_name, sqlParameter1);

/*ERROR : 
SqlParameter[] sqlParameter2 = new[] { param1, param2 };
ExecuteProc(sp_name, sqlParameter2);
*/ 

यह करो:

var param3 = new SqlParameter{ DbType = DbType.String, ParameterName = param1, Direction = ParameterDirection.Input , Value = param1.Value };
var param4 = new SqlParameter{ DbType = DbType.Int64, ParameterName = param2, Direction = ParameterDirection.Input , Value = param2.Value};

SqlParameter[] sqlParameter3 = new[] { param3, param4 };

ExecuteProc(sp_name, sqlParameter3);

0

मुझे इस अपवाद का सामना करना पड़ा क्योंकि मैं एक पैरामीटर ऑब्जेक्ट को इंस्टेंट करने में विफल रहा था। मुझे लगा कि यह दो प्रक्रियाओं के बारे में शिकायत कर रहा है जिसमें एक ही नाम के पैरामीटर हैं। एक ही पैरामीटर को दो बार जोड़े जाने की शिकायत थी।

            Dim aParm As New SqlParameter()
            aParm.ParameterName = "NAR_ID" : aParm.Value = hfCurrentNAR_ID.Value
            m_daNetworkAccess.UpdateCommand.Parameters.Add(aParm)
            aParm = New SqlParameter
            Dim tbxDriveFile As TextBox = gvNetworkFileAccess.Rows(index).FindControl("tbxDriveFolderFile")
            aParm.ParameterName = "DriveFolderFile" : aParm.Value = tbxDriveFile.Text
            m_daNetworkAccess.UpdateCommand.Parameters.Add(aParm)
            **aParm = New SqlParameter()**  <--This line was missing.
            Dim aDDL As DropDownList = gvNetworkFileAccess.Rows(index).FindControl("ddlFileAccess")
            aParm.ParameterName = "AccessGranted" : aParm.Value = aDDL.Text
            **m_daNetworkAccess.UpdateCommand.Parameters.Add(aParm)** <-- The error occurred here.

0

समस्या
जब मैं इस समस्या का सामना कर रहा था तो C # से SQL सर्वर संग्रहीत कार्यविधि निष्पादित कर रहा था:

अपवाद संदेश [SqlParameter पहले से ही एक और SqlParameterCollection द्वारा समाहित है।]

क्योंकि
मैं अपनी संग्रहीत प्रक्रिया के लिए 3 पैरामीटर पारित कर रहा था। मैंने जोड़ा

param = command.CreateParameter();

केवल एक बार पूरी तरह से। मुझे प्रत्येक पैरामीटर के लिए इस लाइन को जोड़ना चाहिए था, इसका मतलब पूरी तरह से 3 गुना है।

DbCommand command = CreateCommand(ct.SourceServer, ct.SourceInstance, ct.SourceDatabase);
command.CommandType = CommandType.StoredProcedure;
command.CommandText = "[ETL].[pGenerateScriptToCreateIndex]";

DbParameter param = command.CreateParameter();
param.ParameterName = "@IndexTypeID";
param.DbType = DbType.Int16;
param.Value = 1;
command.Parameters.Add(param);

param = command.CreateParameter(); --This is the line I was missing
param.ParameterName = "@SchemaName";
param.DbType = DbType.String;
param.Value = ct.SourceSchema;
command.Parameters.Add(param);

param = command.CreateParameter(); --This is the line I was missing
param.ParameterName = "@TableName";
param.DbType = DbType.String;
param.Value = ct.SourceDataObjectName;
command.Parameters.Add(param);

dt = ExecuteSelectCommand(command);

समाधान
प्रत्येक पैरामीटर के लिए कोड की निम्न पंक्ति जोड़ना

param = command.CreateParameter();

0

यह मैंने कैसे किया है!

        ILease lease = (ILease)_SqlParameterCollection.InitializeLifetimeService();
        if (lease.CurrentState == LeaseState.Initial)
        {
            lease.InitialLeaseTime = TimeSpan.FromMinutes(5);
            lease.SponsorshipTimeout = TimeSpan.FromMinutes(2);
            lease.RenewOnCallTime = TimeSpan.FromMinutes(2);
            lease.Renew(new TimeSpan(0, 5, 0));
        }

0

यदि आप EntityFramework का उपयोग कर रहे हैं

मेरा भी यही अपवाद था। मेरे मामले में, मैं SQL को EntityFramework DBContext के माध्यम से बुला रहा था। निम्नलिखित मेरा कोड है, और मैंने इसे कैसे तय किया।

टूटा हुआ कोड

string sql = "UserReport @userID, @startDate, @endDate";

var sqlParams = new Object[]
{
    new SqlParameter { ParameterName= "@userID", Value = p.UserID, SqlDbType = SqlDbType.Int, IsNullable = true }
    ,new SqlParameter { ParameterName= "@startDate", Value = p.StartDate, SqlDbType = SqlDbType.DateTime, IsNullable = true }
    ,new SqlParameter { ParameterName= "@endDate", Value = p.EndDate, SqlDbType = SqlDbType.DateTime, IsNullable = true }
};

IEnumerable<T> rows = ctx.Database.SqlQuery<T>(sql,parameters);

foreach(var row in rows) {
    // do something
}

// the following call to .Count() is what triggers the exception
if (rows.Count() == 0) {
    // tell user there are no rows
}

नोट: उपरोक्त कॉल SqlQuery<T>()वास्तव में एक रिटर्न देता है DbRawSqlQuery<T>, जो लागू करता हैIEnumerable

कॉलिंग क्यों करता है .Count () अपवाद को फेंकें?

मैंने पुष्टि करने के लिए SQL Profiler को निकाल नहीं दिया है, लेकिन मुझे संदेह है कि .Count()SQL सर्वर पर किसी अन्य कॉल को ट्रिगर कर रहा है , और आंतरिक रूप से यह उसी SQLCommandऑब्जेक्ट का पुन: उपयोग कर रहा है और डुप्लिकेट मापदंडों को फिर से जोड़ने की कोशिश कर रहा है।

समाधान / कार्य कोड

मैंने अपने अंदर एक काउंटर जोड़ा foreach, ताकि मैं कॉल किए बिना एक पंक्ति गणना रख सकूं.Count()

int rowCount = 0;

foreach(var row in rows) {
    rowCount++
    // do something
}

if (rowCount == 0) {
    // tell user there are no rows
}

Afterthough

मेरी परियोजना शायद ईएफ के पुराने संस्करण का उपयोग कर रही है। नए संस्करण ने इस आंतरिक बग को मापदंडों को साफ़ करके, या SqlCommandऑब्जेक्ट का निपटान करके तय किया हो सकता है ।

या हो सकता है, ऐसे स्पष्ट निर्देश हों जो डेवलपर्स को यह बताएं कि इसमें .Count()पुनरावृत्ति होने के बाद फोन नहीं करना चाहिए DbRawSqlQuery, और मैं इसे गलत कोडित कर रहा हूं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.