2010-01-28 16 views
5

Sto cercando di inserire un record e ottenere il suo id appena generato eseguendo due query una alla volta, ma non so perché mi sta dando il seguente errore.SCOPE_IDENTITY non funziona in asp.net?

Object cannot be cast from DBNull to other types 

Il mio codice è il seguente: (Non voglio usare stored procedure di SQL)

SqlParameter sqlParam; 
    int lastInsertedVideoId = 0; 

    using (SqlConnection Conn = new SqlConnection(ObjUtils._ConnString)) 
    { 
     Conn.Open(); 
     using (SqlCommand sqlCmd = Conn.CreateCommand()) 
     { 
      string sqlInsertValues = "@Name,@Slug"; 
      string sqlColumnNames = "[Name],[Slug]"; 
      string sqlQuery = "INSERT INTO videos(" + sqlColumnNames + ") VALUES(" + sqlInsertValues + ");"; 
      sqlCmd.CommandText = sqlQuery; 
      sqlCmd.CommandType = CommandType.Text; 

      sqlParam = sqlCmd.Parameters.Add("@Name", SqlDbType.VarChar); 
      sqlParam.Value = txtName.Text.Trim(); 

      sqlParam = sqlCmd.Parameters.Add("@Slug", SqlDbType.VarChar); 
      sqlParam.Value = txtSlug.Text.Trim(); 


      sqlCmd.ExecuteNonQuery(); 

      //getting last inserted video id 
      sqlCmd.CommandText = "SELECT SCOPE_IDENTITY() AS [lastInsertedVideoId]"; 
      using (SqlDataReader sqlDr = sqlCmd.ExecuteReader()) 
      { 
       sqlDr.Read(); 
       lastInsertedVideoId = Convert.ToInt32(sqlDr["lastInsertedVideoId"]); 
      } 
     } 
    } 

    //tags insertion into tag table 
    if (txtTags.Text.Trim().Length > 0 && lastInsertedVideoId > 0) 
    { 
     string sqlBulkTagInsert = ""; 
     string[] tags = txtTags.Text.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries); 
     foreach (string tag in tags) 
     { 
      sqlBulkTagInsert += "INSERT INTO tags(VideoId, Tag) VALUES(" + lastInsertedVideoId + ", " + tag.Trim().ToLowerInvariant()+ "); "; 
     } 

     using (SqlConnection Conn = new SqlConnection(ObjUtils._ConnString)) 
     { 
      Conn.Open(); 
      using (SqlCommand sqlCmd = Conn.CreateCommand()) 
      { 
       string sqlQuery = sqlBulkTagInsert; 
       sqlCmd.CommandText = sqlQuery; 
       sqlCmd.CommandType = CommandType.Text; 

       sqlCmd.ExecuteNonQuery(); 
      } 
     } 
    } 

E anche, se possibile, si prega di controllare è il codice di cui sopra codificato bene o siamo in grado di ottimizzare più per migliorare le prestazioni?

Grazie

+0

Non dimenticare il bug di esecuzione parallela esistente in SQL Server per SCOPE_IDENTITY e @@ IDENTITY: http://support.microsoft.com/default.aspx?scid=kb;en-US;2019779 –

risposta

9

La chiamata a SCOPE_IDENTITY() non viene considerata come nello stesso "ambito" del comando INSERT che si sta eseguendo.

In sostanza, ciò che devi fare è cambiare la linea:

string sqlQuery = "INSERT INTO videos(" + sqlColumnNames + ") VALUES(" + sqlInsertValues + ");"; 

a:

string sqlQuery = "INSERT INTO videos(" + sqlColumnNames + ") VALUES(" + sqlInsertValues + "); SELECT SCOPE_IDENTITY() AS [lastInsertedVideoId]"; 

e quindi chiamare

int lastVideoInsertedId = Convert.ToInt32(sqlCmd.ExecuteScalar()); 

invece di .ExecuteNonQuery e il blocco di codice seguendo il commento "// ottenendo l'ultimo ID video inserito".

+0

Sembra buono, fammi provare. – Prashant

+0

Non so perché ma mi sta dando 'System.InvalidCastException: Cast specificato non è valido. Errore quando lo faccio come' int lastVideoInsertedId = (int) sqlCmd.ExecuteScalar(); ' – Prashant

+0

Accetto: ho provato mi piace 'int lastVideoInsertedId = Convert.ToInt32 (sqlCmd.ExecuteScalar());' e il suo funzionamento. Non ne conosco il motivo, se lo sai per favore spiega. Inoltre, modifica la risposta e includi questo metodo di conversione. Grazie – Prashant

0

Il SCOPE_IDENTITY() devono essere estratti dal primo comando (SELECT, RETURN o OUT) e passati nel comando successivo. Con ciò intendo che lo SELECT_IDENTITY() dovrebbe trovarsi alla fine del comando primo. In SQL 2008 è disponibile una sintassi aggiuntiva per riportare i valori come parte dello INSERT, il che rende questo più semplice.

O in modo più efficiente: combinare i comandi in uno per evitare round-trip.

+0

ma utilizzando 'sqlCmd.ExecuteNonQuery();' Come otterremo il valore dell'identità in una variabile? e un'altra cosa non voglio usare le stored procedure sql. – Prashant

+0

O sarà bello se è possibile fornire qualsiasi codice di esempio, grazie. – Prashant

+0

Se è un parametro 'OUT', quindi' ExecuteNonQuery' va bene - idem se è un parametro 'RETURN'; se è un 'SELECT', quindi usare' ExecuteReader' o 'ExecuteScalar'. Si noti che un 'OUT' /' RETURN' è * marginalmente * più efficiente di 'SELECT', ma non di una quantità enorme. –

Problemi correlati