2010-01-28 8 views
5

Estoy tratando de insertar un registro y obtener su ID recién generado mediante la ejecución de dos consultas una por una, pero no sé por qué me da el siguiente error.SCOPE_IDENTITY no funciona en asp.net?

Object cannot be cast from DBNull to other types 

Mi código es la siguiente: (No quiero utilizar procedimientos almacenados 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(); 
      } 
     } 
    } 

Y también, si es posible, por favor verifica el código anterior es codificada así o podemos optimizarlo más para mejorar el rendimiento?

Gracias

+0

No olvide el error de ejecución paralela que existe en SQL Server para SCOPE_IDENTITY y @@ IDENTITY: http://support.microsoft.com/default.aspx?scid=kb;en-US;2019779 –

Respuesta

9

La llamada a SCOPE_IDENTITY() no se está tratando como si estuviera en el mismo "alcance" que el comando INSERT que está ejecutando.

En esencia, lo que hay que hacer es cambiar la línea:

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

a:

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

y luego llamar

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

en lugar de .ExecuteNonQuery y el bloque de código siguiendo el comentario "// obteniendo el último identificador de video insertado".

+0

Se ve bien, déjame probar esto. – Prashant

+0

No sé por qué, pero me está dando el error 'System.InvalidCastException: Cast especificado no es válido. 'Cuando lo estoy haciendo como' int lastVideoInsertedId = (int) sqlCmd.ExecuteScalar(); ' – Prashant

+0

Entendido: lo intenté es como 'int lastVideoInsertedId = Convert.ToInt32 (sqlCmd.ExecuteScalar());' y su funcionamiento. No sé la razón, si sabes, por favor explica. Y también edite su respuesta e incluya este método de conversión. Gracias – Prashant

0

El SCOPE_IDENTITY() deben ser extraídos de la primera orden (SELECT, RETURN o OUT) y se pasaron en el siguiente comando. Con eso, quiero decir que el SELECT_IDENTITY() debe estar al final del primer comando . En SQL 2008 hay una sintaxis adicional para devolver los valores como parte del INSERT, lo que hace esto más simple.

O más eficientemente: combine los comandos en uno para evitar viajes de ida y vuelta.

+0

pero usando 'sqlCmd.ExecuteNonQuery();' ¿cómo obtendremos el valor de identidad en una variable? y una cosa más que no quiero usar procedimientos almacenados sql. – Prashant

+0

O será bueno si puede proporcionar cualquier código de muestra, gracias. – Prashant

+0

Si se trata de un parámetro 'OUT', entonces' ExecuteNonQuery' está bien - ídem si es un parámetro 'RETURN'; si es un 'SELECT ', entonces usa' ExecuteReader' o 'ExecuteScalar'. Tenga en cuenta que un 'OUT' /' RETURN' es * marginalmente * más eficiente que 'SELECT', pero no por una gran cantidad. –

Cuestiones relacionadas