Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / .NET Framework / New Users / December 2004

Tip: Looking for answers? Try searching our database.

AddNew on empty dataset with relations sets wrong child key.

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
clorentson - 19 Dec 2004 02:31 GMT
I have a dataset with two related tables.  When I add a new parent/child pair
when there are rows in the dataset the Parent Key and Child Key are set
appropriately in the database.  However when I do the same thing but there
are no rows in the dataset, the Parent Key gets set to a new valid Key but
the child row key doesn't get set to the new value.  This results in a
Foreign Key constraint error being returned.

If I update the database with just the parent first, I can then add children
to it.

Any solutions or ways around this?

Thanks,
Carl
Bruce Johnson - 19 Dec 2004 02:59 GMT
You can set the EnforceConstraints property on the DataSet to false.
Then the child rows can be added before the parent, if that is your
bent.  The only trick to avoiding future problems is to set
EnforceConstraints back to true when all of the updates are finished.
This will cause all of the constraints to be evaluated and any
exceptions thrown at that point.

Bruce Johnson [.NET MVP]
http://www.objectsharp.com/blogs/bruce
clorentson - 19 Dec 2004 12:50 GMT
Bruce,

Thank you for the response.  The problem isn't one of order it has to do
with the DataRelation's (or some other object's) ability to set a temporary
Key when a Parent row is created, use that temporary key as the Foreign Key
in the Child row, replace the Parent Key with a permanent key when added to
the database (SQL Server in this case), and finally use the permanent Key as
the permanent Foreign Key in the child.

This process works fine for me as long as there is at least one row in the
parent dataset even if the child dataset is empty.  It fails to work when the
parent dataset is empty.  The Parent's Key is set to the new permanent Key
and the row stores to the database fine.  The Child row fails with a Foreign
Key constraint error because its temporary Key is not updated to the new,
permanent Parent Key.

Carl.

> You can set the EnforceConstraints property on the DataSet to false.
> Then the child rows can be added before the parent, if that is your
[quoted text clipped - 8 lines]
> *** Sent via Developersdex http://www.developersdex.com ***
> Don't just participate in USENET...get rewarded for it!
clorentson - 19 Dec 2004 13:48 GMT
Hi All,

In rereading my description I realized my terminology wasn't quite accruate.
Both tables are in a single dataset linked via appropriate datarelations.  
Hierarchy looks like this:

CustomerTable
   CustomerPrescriptionRelation
       PrescriptionTable
            PrescriptionPrescriptionDetailRelation
                 PrescriptionDetailTable

It's the PrescriptionPrescriptionDetailRelation that doesn't seem to process
the permanent Key from PrescriptionTable when the Prescription table is empty
at the time of adding the Prescription row and PrescriptionDetail row.

Carl

> You can set the EnforceConstraints property on the DataSet to false.
> Then the child rows can be added before the parent, if that is your
[quoted text clipped - 8 lines]
> *** Sent via Developersdex http://www.developersdex.com ***
> Don't just participate in USENET...get rewarded for it!
Bruce Johnson - 19 Dec 2004 16:19 GMT
Carl,

Given you clarification, let me take another stab at it.  First of all,
the propagation of primary keys from parent to child is dependent on a
couple of factors.

First of all, the relation needs to be defined to cascade changes from
parent to child.  This is what allows a change to the parent row's key
to cause the child row's key to be set.  

Second, and the one that I suspect is causing you grief, is that the
Insert statement on the DataAdapter that your using needs to include a
SELECT statement.  This allows the contents of the particular row to be
refreshed with the key that was generated by the INSERT statement.
Which, because of the previously mentioned DataRelation setting, will
then get pushed down to the child row.

Hope that does it for you.

Bruce Johnson [.NET MVP]
http://www.objectsharp.com/blogs/bruce
clorentson - 19 Dec 2004 18:05 GMT
Hi Bruce,

I just tried the cascade at the typed dataset level but it had no affect.  
Is this where it needs to be done or do I need to set cascade update at the
SQL Server table definition level?

Thanks for the additional information.  Could you add a little more detail
on the Select statement for the Insert?  Which insert needs the select
statement?  The parent or the child?  Shat is the select statement selecting?
Could you give a simple example?

Also, do I need to do an AcceptChanges at the parent level before processing
the child?

Thanks,
Carl

> Carl,
>
[quoted text clipped - 20 lines]
> *** Sent via Developersdex http://www.developersdex.com ***
> Don't just participate in USENET...get rewarded for it!
clorentson - 22 Dec 2004 20:59 GMT
Hi Bruce,

Thanks again for the information.  I found the topic discussion under the
InsertCommand in help and also found the article "Managing an @@Identity
Crisis at
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnadonet/html/m
anidcrisis.asp
which looks very promising.

At this point I think I'm all set.

> Carl,
>
[quoted text clipped - 20 lines]
> *** Sent via Developersdex http://www.developersdex.com ***
> Don't just participate in USENET...get rewarded for it!
clorentson - 23 Dec 2004 11:57 GMT
Ok, here we go with the next layer of the onion.

I've setup the sqlcommands per the recommendations and am now selecting back
the @@Identity column after the Insert.  The Parent row gets added but I get
an exception that the related child FK is read-only.

(Good news is that at least my code is trying to do what is needed <g>)

Any suggestions?

Thanks,
Carl

> I have a dataset with two related tables.  When I add a new parent/child pair
> when there are rows in the dataset the Parent Key and Child Key are set
[quoted text clipped - 10 lines]
> Thanks,
> Carl
Bruce Johnson - 23 Dec 2004 14:09 GMT
That surprises me, since the update to the child FKs should be done
through the cascade when the parent PK is updated.

Just to make sure, the reselect is done as part of the InsertCommand on
the Parent's DataAdapter, right?  

INSERT INTO Parent (F1, F2) VALUES (@F1, @F2);
SELECT Id, F1, F2 FROM Parent                          WHERE (Id =
SCOPE_IDENTITY())

Bruce Johnson [.NET MVP]
http://www.objectsharp.com/blogs/bruce
clorentson - 23 Dec 2004 15:17 GMT
Hi Bruce,

Me too, it seems like the child row is locked or something.

Yes, I'm doing the select on the parent's insert command.  I'm using
@@Identity at this point but plan on changing it to  SCOPE_IDENTITY() once I
get the basic functionality working.  I also have cascade set on for the
relationship (delete, Update, and Accept/Reject) on the typed dataset - but
not in the database.

Here is the .update code I am using:            
          If Not inDS.GetChanges(DataRowState.Added) Is Nothing Then
               'don't allow customer additions here, reject any that come
in                If Not inDS.Customer.GetChanges(DataRowState.Added) Is
Nothing Then
                   
inDS.Customer.GetChanges(DataRowState.Added).RejectChanges()
               End If
               
Me.sdaCustomerPrescription.Update(inDS.GetChanges(DataRowState.Added),
"Prescription")
               
Me.sdaCustomerPrescriptionDtl.Update(inDS.GetChanges(DataRowState.Added),
"PrescriptionDetail")
           End If

I've pasted my select command and insert command code below which is based
on the results of the Dataadapter Configuration Wizard.

Note that my lowest level table, PrescriptionDetail has a two column Primary
Key:  PrescriptionFK and PrescriptionDetailType.  I can change that to have
an identity column as the primary key and then set a uniqueness constraint on
the two columns if you think that is source of the problem.

Thanks,
Carl

Code:

For Customer:
    With CustomerSDA.SelectCommand
           .CommandText = "SELECT CustomerKey, LeftPD, RightPD,
TotalNearPD, TotalDistPD FROM Customer WHERE" & _
           " (CustomerKey = @InputCustomerKey)"
           .Parameters.Add("@InputCustomerKey", System.Data.SqlDbType.Int,
4).Value = strCustomerKey
           .Connection = SqlConnection1
       End With

For Prescription:
       With PrescritptionSDA.SelectCommand
           .CommandText = _
               "SELECT PrescriptionKey, CustomerFK, IssuedDate,
ExpirationDate, PrescriberFK, Special_Instr " & _
               "FROM Prescription " & _
               "WHERE (CustomerFK = @InputCustomerKey)"
           .Parameters.Add("@InputCustomerKey", System.Data.SqlDbType.Int,
4).Value = strCustomerKey
           .Connection = SqlConnection1
       End With
       '
       'SqlInsertCommand1
       '
       With PrescritptionSDA.InsertCommand
           .CommandText = "INSERT INTO Prescription(CustomerFK, IssuedDate,
ExpirationDate, PrescriberFK, Sp" & _
           "ecial_Instr) VALUES (@CustomerFK, @IssuedDate, @ExpirationDate,
@PrescriberFK, @" & _
           "Special_Instr); SELECT PrescriptionKey, CustomerFK, IssuedDate,
ExpirationDate, " & _
           "PrescriberFK, Special_Instr FROM Prescription WHERE
(PrescriptionKey = @@IDENTITY)"
           .Connection = SqlConnection1
           .Parameters.Add(New
System.Data.SqlClient.SqlParameter("@CustomerFK", System.Data.SqlDbType.Int,
4, "CustomerFK"))
           .Parameters.Add(New
System.Data.SqlClient.SqlParameter("@IssuedDate",
System.Data.SqlDbType.DateTime, 4, "IssuedDate"))
           .Parameters.Add(New
System.Data.SqlClient.SqlParameter("@ExpirationDate",
System.Data.SqlDbType.DateTime, 4, "ExpirationDate"))
           .Parameters.Add(New
System.Data.SqlClient.SqlParameter("@PrescriberFK",
System.Data.SqlDbType.Int, 4, "PrescriberFK"))
           .Parameters.Add(New
System.Data.SqlClient.SqlParameter("@Special_Instr",
System.Data.SqlDbType.NVarChar, 250, "Special_Instr"))
       End With

For PrescriptionDetail (Note that the PK here is the PrescriptionFK plus the
PrescriptionDetailType):
       With PrescriptionDetailSDA.SelectCommand
           .CommandText = "SELECT PrescriptionFK, PrescriptionDetailType,
RDSpherical, RNSpherical, " & _
               "RCylindrical, RAxis, RPrism1, RBase1, RPrism2, RBase2,
LDSpherical, LNSpherical, " & _
               "LCylindrical, LAxis, LPrism1, LBase1, LPrism2, LBase2 " & _
               "FROM PrescriptionDetail " & _
               "WHERE (PrescriptionFK IN " & _
                   "(SELECT PrescriptionKey FROM Prescription WHERE
CustomerFK = @InputCustomerKey))"
           .Parameters.Add("@InputCustomerKey", System.Data.SqlDbType.Int,
4).Value = strCustomerKey
           .Connection = SqlConnection1
       End With
       '
       'SqlInsertCommand1
       '
       With PrescriptionDetailSDA.InsertCommand
           .CommandText = "INSERT INTO PrescriptionDetail(PrescriptionFK,
PrescriptionDetailType, RDSpherica" & _
           "l, RNSpherical, RCylindrical, RAxis, RPrism1, RBase1, RPrism2,
RBase2, LDSpheric" & _
           "al, LNSpherical, LCylindrical, LAxis, LPrism1, LBase1, LPrism2,
LBase2) VALUES (" & _
           "@PrescriptionFK, @PrescriptionDetailType, @RDSpherical,
@RNSpherical, @RCylindri" & _
           "cal, @RAxis, @RPrism1, @RBase1, @RPrism2, @RBase2,
@LDSpherical, @LNSpherical, @" & _
           "LCylindrical, @LAxis, @LPrism1, @LBase1, @LPrism2, @LBase2);
SELECT Prescription" & _
           "FK, PrescriptionDetailType, RDSpherical, RNSpherical,
RCylindrical, RAxis, RPris" & _
           "m1, RBase1, RPrism2, RBase2, LDSpherical, LNSpherical,
LCylindrical, LAxis, LPri" & _
           "sm1, LBase1, LPrism2, LBase2 FROM PrescriptionDetail WHERE
(PrescriptionDetailTy" & _
           "pe = @PrescriptionDetailType) AND (PrescriptionFK =
@PrescriptionFK)"
           .Connection = SqlConnection1
           .Parameters.Add(New
System.Data.SqlClient.SqlParameter("@PrescriptionFK",
System.Data.SqlDbType.Int, 4, "PrescriptionFK"))
           .Parameters.Add(New
System.Data.SqlClient.SqlParameter("@PrescriptionDetailType",
System.Data.SqlDbType.NVarChar, 15, "PrescriptionDetailType"))
           .Parameters.Add(New
System.Data.SqlClient.SqlParameter("@RDSpherical",
System.Data.SqlDbType.NVarChar, 10, "RDSpherical"))
           .Parameters.Add(New
System.Data.SqlClient.SqlParameter("@RNSpherical",
System.Data.SqlDbType.NVarChar, 10, "RNSpherical"))
           .Parameters.Add(New
System.Data.SqlClient.SqlParameter("@RCylindrical",
System.Data.SqlDbType.NVarChar, 10, "RCylindrical"))
           .Parameters.Add(New System.Data.SqlClient.SqlParameter("@RAxis",
System.Data.SqlDbType.NVarChar, 10, "RAxis"))
           .Parameters.Add(New
System.Data.SqlClient.SqlParameter("@RPrism1",
System.Data.SqlDbType.NVarChar, 2, "RPrism1"))
           .Parameters.Add(New
System.Data.SqlClient.SqlParameter("@RBase1", System.Data.SqlDbType.NVarChar,
2, "RBase1"))
           .Parameters.Add(New
System.Data.SqlClient.SqlParameter("@RPrism2",
System.Data.SqlDbType.NVarChar, 2, "RPrism2"))
           .Parameters.Add(New
System.Data.SqlClient.SqlParameter("@RBase2", System.Data.SqlDbType.NVarChar,
2, "RBase2"))
           .Parameters.Add(New
System.Data.SqlClient.SqlParameter("@LDSpherical",
System.Data.SqlDbType.NVarChar, 10, "LDSpherical"))
           .Parameters.Add(New
System.Data.SqlClient.SqlParameter("@LNSpherical",
System.Data.SqlDbType.NVarChar, 10, "LNSpherical"))
           .Parameters.Add(New
System.Data.SqlClient.SqlParameter("@LCylindrical",
System.Data.SqlDbType.NVarChar, 10, "LCylindrical"))
           .Parameters.Add(New System.Data.SqlClient.SqlParameter("@LAxis",
System.Data.SqlDbType.NVarChar, 10, "LAxis"))
           .Parameters.Add(New
System.Data.SqlClient.SqlParameter("@LPrism1",
System.Data.SqlDbType.NVarChar, 2, "LPrism1"))
           .Parameters.Add(New
System.Data.SqlClient.SqlParameter("@LBase1", System.Data.SqlDbType.NVarChar,
2, "LBase1"))
           .Parameters.Add(New
System.Data.SqlClient.SqlParameter("@LPrism2",
System.Data.SqlDbType.NVarChar, 2, "LPrism2"))
           .Parameters.Add(New
System.Data.SqlClient.SqlParameter("@LBase2", System.Data.SqlDbType.NVarChar,
2, "LBase2"))
       End With

> That surprises me, since the update to the child FKs should be done
> through the cascade when the parent PK is updated.
[quoted text clipped - 11 lines]
> *** Sent via Developersdex http://www.developersdex.com ***
> Don't just participate in USENET...get rewarded for it!
clorentson - 30 Dec 2004 20:17 GMT
Hi Bruce,

I'm at my wits end on this problem.  this is what i've done:
1. Added the Select Statement to the InsertCommand as recommended.
Result was that the child's FK was reporting that it was read-only.
2. Changed it to read-only = false
Result - FK constraint error again.
3. Added a separate PK field to the child instead of using the child's FK
and record type as the PK; added a unique constraint on the FK and record
type fields.
Result - FK constraint error still.
4. Checked UpdatedRowSource property - it is set to Both.
5. Set Autoincrement Seed and Step to -1 on both parent and child.
6. Set MissingSchemaAction.AddWithKey.
7. Set parent PK column to read-only = false.

Still getting the FK constraint error.  Now, as I monitor the Parent PK
value the dataset does not seem to be updating with the new values although I
can see the new parent row in the database with the correct PK value.

Here is the InsertCommand text I am using.  Maybe you can see something that
I can't.

SqlDataAdapter1.InsertCommand = New SqlCommand
With SqlDataAdapter1.InsertCommand
           .CommandText = _
               "INSERT INTO Prescription(CustomerFK, IssuedDate,
ExpirationDate, PrescriberFK, " & _
                   "Special_Instr) " & _
               "VALUES (@CustomerFK, @IssuedDate, @ExpirationDate,
@PrescriberFK, @Special_Instr); " & _
               "SELECT PrescriptionKey, CustomerFK, IssuedDate,
ExpirationDate, " & _
                   "PrescriberFK, Special_Instr FROM Prescription " & _
               "WHERE (PrescriptionKey = SCOPE_IDENTITY())"
           .Connection = SqlConnection1
           .Parameters.Add(New
System.Data.SqlClient.SqlParameter("@CustomerFK", System.Data.SqlDbType.Int,
4, "CustomerFK"))
           .Parameters.Add(New
System.Data.SqlClient.SqlParameter("@IssuedDate",
System.Data.SqlDbType.DateTime, 4, "IssuedDate"))
           .Parameters.Add(New
System.Data.SqlClient.SqlParameter("@ExpirationDate",
System.Data.SqlDbType.DateTime, 4, "ExpirationDate"))
           .Parameters.Add(New
System.Data.SqlClient.SqlParameter("@PrescriberFK",
System.Data.SqlDbType.Int, 4, "PrescriberFK"))
           .Parameters.Add(New
System.Data.SqlClient.SqlParameter("@Special_Instr",
System.Data.SqlDbType.NVarChar, 250, "Special_Instr"))
       End With

As I said, I don't know where to go from here.  I feel like I've checked out
every path available at this point.

Hopefully you can see/find the problem,
Carl

> That surprises me, since the update to the child FKs should be done
> through the cascade when the parent PK is updated.
[quoted text clipped - 11 lines]
> *** Sent via Developersdex http://www.developersdex.com ***
> Don't just participate in USENET...get rewarded for it!
clorentson - 30 Dec 2004 21:19 GMT
Hi Bruce,

I've nailed it!  Always darkest before the dawn as they say.

The problem was with the way I was selecting my rows for the adapter update.
I was using:
Me.sdaCustomerPrescription.Update(inDS.GetChange (DataRowState.Added),
"Prescription")

Which accroding to the docmentation GetChange "makes a copy of the dataset".
I narrowed it donw to here by looking at the data coming back withthe
RowUpdated event and found that it had the correct value.  When I changed the
update to:
Me.sdaCustomerPrescription.Update(inDS.Prescription.Select(Nothing,
Nothing, DataViewRowState.Added))

the select statement merely filtered the rows and posted the updated values
back to the original dataset as needed.

Carl

> That surprises me, since the update to the child FKs should be done
> through the cascade when the parent PK is updated.
[quoted text clipped - 11 lines]
> *** Sent via Developersdex http://www.developersdex.com ***
> Don't just participate in USENET...get rewarded for it!

Rate this thread:







Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.