none
AutoIncrement en relación padre hijo RRS feed

  • Pregunta

  • Hola!

     

    Tengo que insertar registros en un par de tablas con una relación maestro detalle, para ello tengo un dataset generado con un código parecido al siguiente : 

     

                        SqlDataAdapter daMaestro = new SqlDataAdapter("Select * From Maestro",con);
                        SqlDataAdapter daDetalle = new SqlDataAdapter("SELECT * from Detalle", con);
                        SqlCommandBuilder bld = new SqlCommandBuilder(daMaestro);
                        bld = new SqlCommandBuilder(daDetalle);
    
                        DataSet dsFactTemp = new DataSet();
                        daMaestro.FillSchema( dsFactTemp,SchemaType.Source,"Maestro");
                        daDetalle.FillSchema( dsFactTemp, SchemaType.Source,"Detalle");
                        daMaestro.MissingSchemaAction = MissingSchemaAction.AddWithKey;
                        daDetalle.MissingSchemaAction = MissingSchemaAction.AddWithKey;
                        dsFactTemp.Tables["Maestro"].Columns["ID"].AutoIncrementSeed = -1;
                        dsFactTemp.Tables["Maestro"].Columns["ID"].AutoIncrementStep = -1;
    
                        dsFactTemp.Tables["Detalle"].Columns["ID"].AutoIncrementSeed = -1;
                        dsFactTemp.Tables["Detalle"].Columns["ID"].AutoIncrementStep = -1;
    
                        dsFactTemp.Relations.Add("FK_Child_Parent", dsFactTemp.Tables["Maestro"].Columns["ID"],
                            dsFactTemp.Tables["Detalle"].Columns["PerentId"]);
    
    

     

    para insertar los registros realizo : 

     

    for ( i = 0; i< lulu ; i++ ) {
    
          DataRow rP = dtMaestro.newRow();
    
          rP = rellenaMastro();
    
    
         for ( h = 0 ; h < lala ; i++ ) {
    
                DataRow rH = dtHijo.newRow();<br/><span style="white-space:pre">		</span>rH["ParentId"] = rP["Id"];
         }
    }
    


    Cómo los identificadores estan con el autincrement i autosed se generan negativos.

    Al llamar a da.Update() del maestro los identificadores negativos se generan bien, pero al hacer el update del hijo los id se generan bien pero la referencia al campo maestro mantiene el identificador negativo lo que hace saltar una Excepción por violación de la foreign key.

     

    Cómo puedo solucionarlo ?

    El ejemplo en el que me estoy basando es el punto 5.8 del libro "ADO .NET 3.5 cookboox" de la editorial O'reilly , en el cual hacen lo mismo ( pero sin el da.update() ).

     

    Muchísimas gracias!

     

    lunes, 10 de octubre de 2011 9:23

Respuestas

  • Solucionado! :D

    Añadiendo una select al final del insert command para que vuelva a cargar el id ;)

    Nota : si se usan triggers hay que usar SCOPE_IDDENTITY()

    Por si alguien tubiera algún problema parecido adjunto un código que funciona.

            public static SqlDataAdapter CreateParentDataAdapter() {
    
                SqlCommand selectcmd = new SqlCommand("SELECT * FROM Parent");
                SqlDataAdapter da = new SqlDataAdapter(selectcmd);
    
                SqlCommandBuilder bld = new SqlCommandBuilder(da);
                SqlCommand insertcmd = new SqlCommand("INSERT INTO Parent(a,b) VALUES(@a,@b);"+
                        " SELECT ParentId,a,b from Parent where ( ParentId = @@IDENTITY)");
                insertcmd.Parameters.Add("@a", SqlDbType.NVarChar, 20, "a");
                insertcmd.Parameters.Add("@b", SqlDbType.NVarChar, 20, "b");
                da.InsertCommand = insertcmd;
                return da;
            }
            public static SqlDataAdapter CreateChildDataAdapter() {
    
                SqlCommand selectcmd = new SqlCommand("SELECT * FROM Child");
                
                SqlDataAdapter da = new SqlDataAdapter(selectcmd);
    
                SqlCommandBuilder bld = new SqlCommandBuilder(da);
                SqlCommand insertcmd = new SqlCommand("INSERT INTO Child(ParentId,c,d) VALUES(@ParentId,@c,@d);"+
                        "SELECT ChildId,ParentId,c,d From Child where ( ChildId = @@IDENTITY)");
                insertcmd.Parameters.Add("@ParentId", SqlDbType.Int, 30, "ParentId");
                insertcmd.Parameters.Add("@c", SqlDbType.NVarChar, 20, "c");
                insertcmd.Parameters.Add("@d", SqlDbType.NVarChar, 20, "d");
                da.InsertCommand = insertcmd;
                return da;
            }
            public static void PruebaInsert() {
    
                using (SqlConnection con = new SqlConnection(CON)) {
    
                    SqlDataAdapter daParent = CreateParentDataAdapter();
                    daParent.SelectCommand.Connection = con;
    
                    SqlDataAdapter daChild = CreateChildDataAdapter();
                    daChild.SelectCommand.Connection = con;
                    
                    DataSet ds = new DataSet();
                    daParent.FillSchema(ds, SchemaType.Source,"Parent");
                    daChild.FillSchema(ds, SchemaType.Source,"Child");
    
                    ds.Tables["Parent"].Columns["ParentId"].AutoIncrementSeed = -1;
                    ds.Tables["Parent"].Columns["ParentId"].AutoIncrementStep = -1;
    
                    ds.Tables["Child"].Columns["ChildId"].AutoIncrementSeed = -1;
                    ds.Tables["Child"].Columns["ChildId"].AutoIncrementStep = -1;
    
                    ds.Relations.Add("FK_Child_parent",
                        ds.Tables["Parent"].Columns["ParentId"],
                        ds.Tables["Child"].Columns["ParentId"],true);
    
                    for (int x = 0; x < 3; x++) {
                        
                        DataRow r = ds.Tables["Parent"].NewRow();
    
                        r["a"] = "a" + x;
                        r["b"] = "b" + x;
    
                        ds.Tables["Parent"].Rows.Add(r);
                        for (int i = 0; i < 5; i++) {
                            ds.Tables["Child"].Rows.Add(new object[] { null, r["ParentID"], "c" + i, "d" + i });
                        }
                    }
    
                    daParent.Update(ds,"Parent");
                    daChild.Update(ds,"Child");
    
                }
            }
    


    y las tablas usadas son :

    CREATE TABLE [dbo].[Child](
    	[ChildId] [int] IDENTITY(1,1) NOT NULL,
    	[ParentId] [int] NOT NULL,
    	[c] [nvarchar](20) COLLATE Modern_Spanish_CI_AS NULL,
    	[d] [nvarchar](20) COLLATE Modern_Spanish_CI_AS NULL,
    PRIMARY KEY CLUSTERED 
    (
    	[ChildId] ASC
    )WITH (PAD_INDEX  = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
    ) ON [PRIMARY]
    
    
    
    CREATE TABLE [dbo].[Parent](
    	[ParentId] [int] IDENTITY(1,1) NOT NULL,
    	[a] [nvarchar](20) COLLATE Modern_Spanish_CI_AS NULL,
    	[b] [nvarchar](20) COLLATE Modern_Spanish_CI_AS NULL,
    PRIMARY KEY CLUSTERED 
    (
    	[ParentId] ASC
    )WITH (PAD_INDEX  = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
    ) ON [PRIMARY]
    
    


    • Marcado como respuesta arturn00 martes, 11 de octubre de 2011 7:50
    martes, 11 de octubre de 2011 7:50

Todas las respuestas

  • Hola artun00.

    Te expongo una solución:

    dsFactTemp.Tables["Maestro"].Columns["ID"]..AutoIncrement = True;
    dsFactTemp.Tables["Maestro"].Columns["ID"].AutoIncrementSeed = -1;
    dsFactTemp.Tables["Maestro"].Columns["ID"].AutoIncrementStep = -1;


                        dsFactTemp.Tables["Detalle"].Columns["ID"].AutoIncrementSeed = -1; <-- No es necesario!
                        dsFactTemp.Tables["Detalle"].Columns["ID"].AutoIncrementStep = -1; <--  No es necesario!

     dsFactTemp.Relations.Add("FK_Child_Parent", dsFactTemp.Tables["Maestro"].Columns["ID"],
                            dsFactTemp.Tables["Detalle"].Columns["PerentId"],True); <-- Aquí debe anexar True.

    Luego que Actualice el DataTable Maestro, el Identity devuelto lo debe fija en la Columna ID de Maestro y automaticamente se actualiza los valores de la Columna PerentId del DataTable Detalle entonce despues de esa transición es que Tú procede a invocar el Update de Detalle y Listo.

    Aquí dejo unos link de referencia:

    http://jorgepedraza.wordpress.com/2011/09/04/maestro-detalles-ado-net-en-wpf/  

    http://jorgepedraza.wordpress.com/2011/08/09/sql-server-compact-4-0-y-asp-net-4-0/ (descargar y ver Código Fuente)

     

    Sí esto fue útil, por favor marcarlo como respuesta

     


    Developer .NET


    • Editado Megasoft2 martes, 11 de octubre de 2011 7:56
    lunes, 10 de octubre de 2011 10:05
  • Hola artun00.

    Te expongo una solución:

    dsFactTemp.Tables["Maestro"].Columns["ID"]..AutoIncrement = True;
    dsFactTemp.Tables["Maestro"].Columns["ID"].AutoIncrementSeed = -1;
    dsFactTemp.Tables["Maestro"].Columns["ID"].AutoIncrementStep = -1;


                        dsFactTemp.Tables["Detalle"].Columns["ID"].AutoIncrementSeed = -1; <-- No es necesario!
                        dsFactTemp.Tables["Detalle"].Columns["ID"].AutoIncrementStep = -1; <--  No es necesario!

     dsFactTemp.Relations.Add("FK_Child_Parent", dsFactTemp.Tables["Maestro"].Columns["ID"],
                            dsFactTemp.Tables["Detalle"].Columns["PerentId"],True); <-- Aquí debe anexar True.

    Luego que Actualice el DataTable Maestro, el Identity devuelto lo debe fija en la Columna ID de Maestro y automaticamente se actualiza los valores de la Columna ID del DataTable Detalle entonce despues de esa transición es que Tú procede a invocar el Update de Detalle y Listo.

    Aquí dejo unos link de referencia:

    http://jorgepedraza.wordpress.com/2011/09/04/maestro-detalles-ado-net-en-wpf/  

    http://jorgepedraza.wordpress.com/2011/08/09/sql-server-compact-4-0-y-asp-net-4-0/ (descargar y ver Código Fuente)

     

    Sí esto fue útil, por favor marcarlo como respuesta

     


    Developer .NET

    No me funcionó .. :'(

    veo que el parametro que debo de anexar a la relación por defecto si no se pasa ya es true.

    el Identity devuelto lo debe fija en la Columna ID de Maestro   ¿ te refieres a que debo hacer un da.fill(ds) para actualizar ?

     

    lunes, 10 de octubre de 2011 10:52
  • veo que cuando hago el da.Update de la tabla maestra , los id de los campos ( que en el momento antes de insertar están con valores negativos ) , no se actualizan.

     

    Si hago un fill me carga de la base de datos los datos con los índices correctos pero me mantiene los negativos , osease que los duplica :S

     

    Saludos"!

    lunes, 10 de octubre de 2011 11:22
  • Hola artun00

    Necesito saber, que tipo de base de datos esta utilizando?

    Te voy comentar de ante mano, que la columna PerentId de la tabla Detalle, a nivel de base de datos no debe ser autonumerico! solo debe ser del Maestro!

    Cualquier cosa me avisa, para colocar un ejemplo final!!

     


    Developer .NET

    • Editado Megasoft2 martes, 11 de octubre de 2011 7:57
    lunes, 10 de octubre de 2011 13:55
  • En la tabla detalle tengo la columna ID como autonumérica y la columna PadreID como integer sin que sea autonumérica.

     

    lunes, 10 de octubre de 2011 16:25
  • Solucionado! :D

    Añadiendo una select al final del insert command para que vuelva a cargar el id ;)

    Nota : si se usan triggers hay que usar SCOPE_IDDENTITY()

    Por si alguien tubiera algún problema parecido adjunto un código que funciona.

            public static SqlDataAdapter CreateParentDataAdapter() {
    
                SqlCommand selectcmd = new SqlCommand("SELECT * FROM Parent");
                SqlDataAdapter da = new SqlDataAdapter(selectcmd);
    
                SqlCommandBuilder bld = new SqlCommandBuilder(da);
                SqlCommand insertcmd = new SqlCommand("INSERT INTO Parent(a,b) VALUES(@a,@b);"+
                        " SELECT ParentId,a,b from Parent where ( ParentId = @@IDENTITY)");
                insertcmd.Parameters.Add("@a", SqlDbType.NVarChar, 20, "a");
                insertcmd.Parameters.Add("@b", SqlDbType.NVarChar, 20, "b");
                da.InsertCommand = insertcmd;
                return da;
            }
            public static SqlDataAdapter CreateChildDataAdapter() {
    
                SqlCommand selectcmd = new SqlCommand("SELECT * FROM Child");
                
                SqlDataAdapter da = new SqlDataAdapter(selectcmd);
    
                SqlCommandBuilder bld = new SqlCommandBuilder(da);
                SqlCommand insertcmd = new SqlCommand("INSERT INTO Child(ParentId,c,d) VALUES(@ParentId,@c,@d);"+
                        "SELECT ChildId,ParentId,c,d From Child where ( ChildId = @@IDENTITY)");
                insertcmd.Parameters.Add("@ParentId", SqlDbType.Int, 30, "ParentId");
                insertcmd.Parameters.Add("@c", SqlDbType.NVarChar, 20, "c");
                insertcmd.Parameters.Add("@d", SqlDbType.NVarChar, 20, "d");
                da.InsertCommand = insertcmd;
                return da;
            }
            public static void PruebaInsert() {
    
                using (SqlConnection con = new SqlConnection(CON)) {
    
                    SqlDataAdapter daParent = CreateParentDataAdapter();
                    daParent.SelectCommand.Connection = con;
    
                    SqlDataAdapter daChild = CreateChildDataAdapter();
                    daChild.SelectCommand.Connection = con;
                    
                    DataSet ds = new DataSet();
                    daParent.FillSchema(ds, SchemaType.Source,"Parent");
                    daChild.FillSchema(ds, SchemaType.Source,"Child");
    
                    ds.Tables["Parent"].Columns["ParentId"].AutoIncrementSeed = -1;
                    ds.Tables["Parent"].Columns["ParentId"].AutoIncrementStep = -1;
    
                    ds.Tables["Child"].Columns["ChildId"].AutoIncrementSeed = -1;
                    ds.Tables["Child"].Columns["ChildId"].AutoIncrementStep = -1;
    
                    ds.Relations.Add("FK_Child_parent",
                        ds.Tables["Parent"].Columns["ParentId"],
                        ds.Tables["Child"].Columns["ParentId"],true);
    
                    for (int x = 0; x < 3; x++) {
                        
                        DataRow r = ds.Tables["Parent"].NewRow();
    
                        r["a"] = "a" + x;
                        r["b"] = "b" + x;
    
                        ds.Tables["Parent"].Rows.Add(r);
                        for (int i = 0; i < 5; i++) {
                            ds.Tables["Child"].Rows.Add(new object[] { null, r["ParentID"], "c" + i, "d" + i });
                        }
                    }
    
                    daParent.Update(ds,"Parent");
                    daChild.Update(ds,"Child");
    
                }
            }
    


    y las tablas usadas son :

    CREATE TABLE [dbo].[Child](
    	[ChildId] [int] IDENTITY(1,1) NOT NULL,
    	[ParentId] [int] NOT NULL,
    	[c] [nvarchar](20) COLLATE Modern_Spanish_CI_AS NULL,
    	[d] [nvarchar](20) COLLATE Modern_Spanish_CI_AS NULL,
    PRIMARY KEY CLUSTERED 
    (
    	[ChildId] ASC
    )WITH (PAD_INDEX  = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
    ) ON [PRIMARY]
    
    
    
    CREATE TABLE [dbo].[Parent](
    	[ParentId] [int] IDENTITY(1,1) NOT NULL,
    	[a] [nvarchar](20) COLLATE Modern_Spanish_CI_AS NULL,
    	[b] [nvarchar](20) COLLATE Modern_Spanish_CI_AS NULL,
    PRIMARY KEY CLUSTERED 
    (
    	[ParentId] ASC
    )WITH (PAD_INDEX  = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
    ) ON [PRIMARY]
    
    


    • Marcado como respuesta arturn00 martes, 11 de octubre de 2011 7:50
    martes, 11 de octubre de 2011 7:50
  • Hola artun00.

    Entonce deberia establecer el siguiente escenario es:

    Maestro: Columna ID: Clave Principal: Autonumerica

    Detalle: Columna PerentId: Clave Principal: No Autonumerica

    Según tu ejemplo: la relación debe ser Maestro.ID  ---> Detalle.PerentId

    Cuando actualiza el Maestro, en ese instante debe obtener la identidad Identity correlactivo generado desde base de datos; posteriormente esto debe ser fijado en el DataTable Maestro.ID que a su vez por medio de la relación y restricción debe actualizar la columna PerentId de Detalle; finalmente procede a realizar el Update de Detalle.

    Para Obtener la identidad desde base de datos SQL : return @@IDENTITY o SCOPE_IDENTITY()

    Link referencia: http://msdn.microsoft.com/es-us/library/ms187342.aspx o  http://msdn.microsoft.com/es-us/library/ms190315.aspx 

    Link ADO.NET y Identity: http://msdn.microsoft.com/es-es/library/ks9f57t0(v=VS.90).aspx tambien http://support.microsoft.com/kb/815629

    Finalmente te recomiendo que baje el codigo fuente del libro para que pueda hace prueba y pueda lograr tu objetivo.

    Eso es todo por lo momento, cualquier cosa avisa!

     


    Developer .NET
    • Editado Megasoft2 martes, 11 de octubre de 2011 7:59
    martes, 11 de octubre de 2011 7:55
  • Hola

    Ya tengo el tema solucionado , lo he probado con el código real de la aplicación y ha funcionado :D

    Igualmente ,gracias por la ayuda!

    No entiendo por que dices que la tabla hija debe tener el campo ID no autonumérico. Tanto en el ejemplo como en el código real el campo Id de la tabla hija esta en autonumérico y funciona correctamente. ( Me refiero a el campo id no al campo que tiene la fk a la tabla padre).

     

    Adjunto un enlace muy interesante sobre cómo tratar las columnas de identidad.

    http://msdn.microsoft.com/en-us/library/ms971502.aspx

    • Editado arturn00 martes, 11 de octubre de 2011 8:54 añadir enlace interesante
    martes, 11 de octubre de 2011 8:48