parent/child tables and identity problems: a scenario

D

Dan

I'm having strange errors with a C# app using form controls bound to a typed
dataset, which holds a parent-table with a single record and a number of
children tables with many records. As I'm new to ADO.NET, first of all I'd
like to know if I'm doing something wrong in my general approach. Here's a
simplified scenario, it's a bit long but I hope it can be useful to newbies
like me, facing the identity problems when updating a dataset. Thank you
very much for your patience!

(a) I use MSDE and the corresponding SqlClient namespace objects. All the
identity values in my tables are autonumber; in the dataset, autonumber seed
and step are set to -1 after their construction, so that the records added
in the "in memory" dataset will be granted to have a different identity from
any database record.

(b) All the insert commands use an output parameter (@newID) set to the new
identity value, e.g. the T-SQL code looks like:

ALTER PROCEDURE AnInsertCommand
(
....
@newID int OUTPUT
)
AS
INSERT INTO ... ;
SET @newID = SCOPE_IDENTITY()

The corresponding command objects include this @newID in their params
collection and map it to the table primary key field, so that when the
command is executed the dataset table will be updated to hold the "real"
autonumber value assigned by database (thus replacing the negative value
used in-memory).

(c) After generating the dataset, I have manually added the required
required foreign-key relations to it using the VS dataset designer, so that
the parent/child relationship are set up correctly: each primary key in a
child table is linked to the corresponding foreign key in the parent table.

(d) Let's say the parent table in dataset is "Customer", and the child table
is "Country". Customer has a countryID acting as the foreign key to the
Country child table. In my application, the user selects a single customer
to edit, or wants to enter a new customer; the form shows his data in bound
controls, and the data for child tables are typically shown in combo boxes
(e.g. a list of countries the user can select from). Thus, the dataset will
always contain a single parent record (=1 customer) and all the children
records (=countries list). The user can also add new countries when editing
a single customer record.

(e) When data are to be stored into database, the update policy is:

1) call BindingContext EndCurrentEdit for each table in the dataset, e.g.:

BindingContext[myDataSet, "Table Name"].EndCurrentEdit();

2) open the connection to database and begin a transaction

3) call Update for each CHILD table for NEW records only, e.g.:


countryDataAdapter.Update(myDataSet.TCountry.Select("","",DataViewRowState.A
dded));

this inserts the newly added countries only and updates the dataset Country
table so that the autonumber values reflect the "real" (non-negative) values
assigned by database; thus, a countryID = -10 becomes e.g. 67; also, the
relationships I have setup in the dataset cascade this update to the linked
parent table, so that the foreign key -10 becomes 67 too.

4) call Update for the PARENT table for NEW or MODIFIED records only
(deleted records will be processed later);

5) call Update for each CHILD table for all other records (e.g. MODIFIED or
DELETED);

6) call Update for the PARENT table for DELETED records.

7) commit the transaction and call AcceptChanges for the whole dataset.

8) close the connection anyway (in a finally {} block; in the catch{} block
the transaction is rolled back).

Now, this seems to work fine: I edit and add records, and autonumber values
are treated as expected and their changes propagated to the parent as
required. Here's the problem: when I'm editing an existing parent record and
add a new child record (e.g. a new country) and then store data, all works
fine; when I create a NEW parent record and do the same, the Update method
for the child table raises an "Object reference not set to instance of an
object" exception: as far as I can check with the debugger, all the values
in the dataset are as expected: the parent record has a negative ID, as for
the newly added child record, and both are linked correctly
(parent.countryID = child.countryID). As everything works when updating an
existing parent record, I'd expect the code to work for a new record too, as
the only difference is that a new record has a negative ID in the parent
table (which anyway should not matter for updating the child table, which is
linked via a foreign key).

Could anyone tell me if anything is "philosophically" wrong with this
approach? I cannot understand the reason for such errors...
 
W

William \(Bill\) Vaughn

Take a look at my article on handling identity issues
http://www.betav.com/msdn_magazine.htm. If this answers your question, buy
me a coke next time you see me in a bar... if it doesn't let me know. ;)

--
____________________________________
William (Bill) Vaughn
Author, Mentor, Consultant
MVP, hRD
www.betav.com
Please reply only to the newsgroup so that others can benefit.
This posting is provided "AS IS" with no warranties, and confers no rights.
__________________________________

Dan said:
I'm having strange errors with a C# app using form controls bound to a typed
dataset, which holds a parent-table with a single record and a number of
children tables with many records. As I'm new to ADO.NET, first of all I'd
like to know if I'm doing something wrong in my general approach. Here's a
simplified scenario, it's a bit long but I hope it can be useful to newbies
like me, facing the identity problems when updating a dataset. Thank you
very much for your patience!

(a) I use MSDE and the corresponding SqlClient namespace objects. All the
identity values in my tables are autonumber; in the dataset, autonumber seed
and step are set to -1 after their construction, so that the records added
in the "in memory" dataset will be granted to have a different identity from
any database record.

(b) All the insert commands use an output parameter (@newID) set to the new
identity value, e.g. the T-SQL code looks like:

ALTER PROCEDURE AnInsertCommand
(
...
@newID int OUTPUT
)
AS
INSERT INTO ... ;
SET @newID = SCOPE_IDENTITY()

The corresponding command objects include this @newID in their params
collection and map it to the table primary key field, so that when the
command is executed the dataset table will be updated to hold the "real"
autonumber value assigned by database (thus replacing the negative value
used in-memory).

(c) After generating the dataset, I have manually added the required
required foreign-key relations to it using the VS dataset designer, so that
the parent/child relationship are set up correctly: each primary key in a
child table is linked to the corresponding foreign key in the parent table.

(d) Let's say the parent table in dataset is "Customer", and the child table
is "Country". Customer has a countryID acting as the foreign key to the
Country child table. In my application, the user selects a single customer
to edit, or wants to enter a new customer; the form shows his data in bound
controls, and the data for child tables are typically shown in combo boxes
(e.g. a list of countries the user can select from). Thus, the dataset will
always contain a single parent record (=1 customer) and all the children
records (=countries list). The user can also add new countries when editing
a single customer record.

(e) When data are to be stored into database, the update policy is:

1) call BindingContext EndCurrentEdit for each table in the dataset, e.g.:

BindingContext[myDataSet, "Table Name"].EndCurrentEdit();

2) open the connection to database and begin a transaction

3) call Update for each CHILD table for NEW records only, e.g.:


countryDataAdapter.Update(myDataSet.TCountry.Select("","",DataViewRowState.A
dded));

this inserts the newly added countries only and updates the dataset Country
table so that the autonumber values reflect the "real" (non-negative) values
assigned by database; thus, a countryID = -10 becomes e.g. 67; also, the
relationships I have setup in the dataset cascade this update to the linked
parent table, so that the foreign key -10 becomes 67 too.

4) call Update for the PARENT table for NEW or MODIFIED records only
(deleted records will be processed later);

5) call Update for each CHILD table for all other records (e.g. MODIFIED or
DELETED);

6) call Update for the PARENT table for DELETED records.

7) commit the transaction and call AcceptChanges for the whole dataset.

8) close the connection anyway (in a finally {} block; in the catch{} block
the transaction is rolled back).

Now, this seems to work fine: I edit and add records, and autonumber values
are treated as expected and their changes propagated to the parent as
required. Here's the problem: when I'm editing an existing parent record and
add a new child record (e.g. a new country) and then store data, all works
fine; when I create a NEW parent record and do the same, the Update method
for the child table raises an "Object reference not set to instance of an
object" exception: as far as I can check with the debugger, all the values
in the dataset are as expected: the parent record has a negative ID, as for
the newly added child record, and both are linked correctly
(parent.countryID = child.countryID). As everything works when updating an
existing parent record, I'd expect the code to work for a new record too, as
the only difference is that a new record has a negative ID in the parent
table (which anyway should not matter for updating the child table, which is
linked via a foreign key).

Could anyone tell me if anything is "philosophically" wrong with this
approach? I cannot understand the reason for such errors...
 
K

Kevin Yu [MSFT]

Thanks for Bill's response.

Hi Dan,

One thing that I have to point out, that in your example, Country is the
parent table and the Customer is the child table.

Please also try to check if any reference are set to null before you call
the Update method.

If anything is unclear, please feel free to reply to the post.

Kevin Yu
=======
"This posting is provided "AS IS" with no warranties, and confers no
rights."

--------------------
| From: "William \(Bill\) Vaughn" <[email protected]>
| References: <[email protected]>
| Subject: Re: parent/child tables and identity problems: a scenario
| Date: Sat, 8 Nov 2003 20:28:09 -0800
| Lines: 139
| Organization: Beta V Corporation
| X-Priority: 3
| X-MSMail-Priority: Normal
| X-Newsreader: Microsoft Outlook Express 6.00.2800.1158
| X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2800.1165
| Message-ID: <#[email protected]>
| Newsgroups: microsoft.public.dotnet.framework.adonet
| NNTP-Posting-Host: smtp.betav.com 209.20.250.241
| Path: cpmsftngxa06.phx.gbl!TK2MSFTNGP08.phx.gbl!tk2msftngp13.phx.gbl
| Xref: cpmsftngxa06.phx.gbl microsoft.public.dotnet.framework.adonet:65855
| X-Tomcat-NG: microsoft.public.dotnet.framework.adonet
|
| Take a look at my article on handling identity issues
| http://www.betav.com/msdn_magazine.htm. If this answers your question, buy
| me a coke next time you see me in a bar... if it doesn't let me know. ;)
|
| --
| ____________________________________
| William (Bill) Vaughn
| Author, Mentor, Consultant
| MVP, hRD
| www.betav.com
| Please reply only to the newsgroup so that others can benefit.
| This posting is provided "AS IS" with no warranties, and confers no
rights.
| __________________________________
|
| | > I'm having strange errors with a C# app using form controls bound to a
| typed
| > dataset, which holds a parent-table with a single record and a number of
| > children tables with many records. As I'm new to ADO.NET, first of all
I'd
| > like to know if I'm doing something wrong in my general approach.
Here's a
| > simplified scenario, it's a bit long but I hope it can be useful to
| newbies
| > like me, facing the identity problems when updating a dataset. Thank you
| > very much for your patience!
| >
| > (a) I use MSDE and the corresponding SqlClient namespace objects. All
the
| > identity values in my tables are autonumber; in the dataset, autonumber
| seed
| > and step are set to -1 after their construction, so that the records
added
| > in the "in memory" dataset will be granted to have a different identity
| from
| > any database record.
| >
| > (b) All the insert commands use an output parameter (@newID) set to the
| new
| > identity value, e.g. the T-SQL code looks like:
| >
| > ALTER PROCEDURE AnInsertCommand
| > (
| > ...
| > @newID int OUTPUT
| > )
| > AS
| > INSERT INTO ... ;
| > SET @newID = SCOPE_IDENTITY()
| >
| > The corresponding command objects include this @newID in their params
| > collection and map it to the table primary key field, so that when the
| > command is executed the dataset table will be updated to hold the "real"
| > autonumber value assigned by database (thus replacing the negative value
| > used in-memory).
| >
| > (c) After generating the dataset, I have manually added the required
| > required foreign-key relations to it using the VS dataset designer, so
| that
| > the parent/child relationship are set up correctly: each primary key in
a
| > child table is linked to the corresponding foreign key in the parent
| table.
| >
| > (d) Let's say the parent table in dataset is "Customer", and the child
| table
| > is "Country". Customer has a countryID acting as the foreign key to the
| > Country child table. In my application, the user selects a single
customer
| > to edit, or wants to enter a new customer; the form shows his data in
| bound
| > controls, and the data for child tables are typically shown in combo
boxes
| > (e.g. a list of countries the user can select from). Thus, the dataset
| will
| > always contain a single parent record (=1 customer) and all the children
| > records (=countries list). The user can also add new countries when
| editing
| > a single customer record.
| >
| > (e) When data are to be stored into database, the update policy is:
| >
| > 1) call BindingContext EndCurrentEdit for each table in the dataset,
e.g.:
| >
| > BindingContext[myDataSet, "Table Name"].EndCurrentEdit();
| >
| > 2) open the connection to database and begin a transaction
| >
| > 3) call Update for each CHILD table for NEW records only, e.g.:
| >
| >
| >
|
countryDataAdapter.Update(myDataSet.TCountry.Select("","",DataViewRowState.A
| > dded));
| >
| > this inserts the newly added countries only and updates the dataset
| Country
| > table so that the autonumber values reflect the "real" (non-negative)
| values
| > assigned by database; thus, a countryID = -10 becomes e.g. 67; also, the
| > relationships I have setup in the dataset cascade this update to the
| linked
| > parent table, so that the foreign key -10 becomes 67 too.
| >
| > 4) call Update for the PARENT table for NEW or MODIFIED records only
| > (deleted records will be processed later);
| >
| > 5) call Update for each CHILD table for all other records (e.g. MODIFIED
| or
| > DELETED);
| >
| > 6) call Update for the PARENT table for DELETED records.
| >
| > 7) commit the transaction and call AcceptChanges for the whole dataset.
| >
| > 8) close the connection anyway (in a finally {} block; in the catch{}
| block
| > the transaction is rolled back).
| >
| > Now, this seems to work fine: I edit and add records, and autonumber
| values
| > are treated as expected and their changes propagated to the parent as
| > required. Here's the problem: when I'm editing an existing parent record
| and
| > add a new child record (e.g. a new country) and then store data, all
works
| > fine; when I create a NEW parent record and do the same, the Update
method
| > for the child table raises an "Object reference not set to instance of
an
| > object" exception: as far as I can check with the debugger, all the
values
| > in the dataset are as expected: the parent record has a negative ID, as
| for
| > the newly added child record, and both are linked correctly
| > (parent.countryID = child.countryID). As everything works when updating
an
| > existing parent record, I'd expect the code to work for a new record
too,
| as
| > the only difference is that a new record has a negative ID in the parent
| > table (which anyway should not matter for updating the child table,
which
| is
| > linked via a foreign key).
| >
| > Could anyone tell me if anything is "philosophically" wrong with this
| > approach? I cannot understand the reason for such errors...
| >
| >
|
|
|
 
D

Dan

Thanks to both... I've read the (great! :) article on identity values, but
the only true difference I noticed between my sample code and the article
code is that I used a single output parameter instead of requerying the
whole row. Sure, in the post I inverted the terms for parent/row, sorry...
Anyway, it stil not works. To be sure, I changed my INSERT commands and the
corresponding data adapter commands to requery the whole row after an
insert. I debug my code until Update is executed: I can see the new customer
child row with all its fields correctly set, and I can "expand" the tree in
the debugger variable inspector to see the related parent tables (Country
parent of Customer, etc): everything looks ok, no null value, the ID's have
the expected (negative) value. I'm getting mad with such ADO behaviours...
Anyway, if this is the correct way of working in such scenarios, I'll try to
recreate a new test project from scratch, hoping that something will change
:). Thanks again...
 
K

Kevin Yu [MSFT]

Hi Dan,

Thanks for sharing you experience here with the whole community. If you
still cannot resolve it, please feel free to let me know. Or you can make a
new post in this queue. I'll try my best to help you.

Kevin Yu
=======
"This posting is provided "AS IS" with no warranties, and confers no
rights."

--------------------
| From: "Dan" <[email protected]>
| References: <[email protected]>
<#[email protected]>
<[email protected]>
| Subject: Re: parent/child tables and identity problems: a scenario
| Date: Mon, 10 Nov 2003 16:19:15 +0100
| Lines: 16
| X-Priority: 3
| X-MSMail-Priority: Normal
| X-Newsreader: Microsoft Outlook Express 6.00.2800.1158
| X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2800.1165
| Message-ID: <[email protected]>
| Newsgroups: microsoft.public.dotnet.framework.adonet
| NNTP-Posting-Host: ppp-217-133-157-90.cust-adsl.tiscali.it 217.133.157.90
| Path: cpmsftngxa06.phx.gbl!TK2MSFTNGP08.phx.gbl!TK2MSFTNGP11.phx.gbl
| Xref: cpmsftngxa06.phx.gbl microsoft.public.dotnet.framework.adonet:65923
| X-Tomcat-NG: microsoft.public.dotnet.framework.adonet
|
| Thanks to both... I've read the (great! :) article on identity values,
but
| the only true difference I noticed between my sample code and the article
| code is that I used a single output parameter instead of requerying the
| whole row. Sure, in the post I inverted the terms for parent/row, sorry...
| Anyway, it stil not works. To be sure, I changed my INSERT commands and
the
| corresponding data adapter commands to requery the whole row after an
| insert. I debug my code until Update is executed: I can see the new
customer
| child row with all its fields correctly set, and I can "expand" the tree
in
| the debugger variable inspector to see the related parent tables (Country
| parent of Customer, etc): everything looks ok, no null value, the ID's
have
| the expected (negative) value. I'm getting mad with such ADO behaviours...
| Anyway, if this is the correct way of working in such scenarios, I'll try
to
| recreate a new test project from scratch, hoping that something will
change
| :). Thanks again...
|
|
|
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top