Cannot file relation 0

W

Wayne Brantley

I have found what appears to be an error in streaming with Datasets. It
causes an error of 'Cannot find relation 0' when recreating the dataset from
a stream.

Here is how you reproduce it.

Lets assume you have tables like this:
Customers
CustomerName: Varchar(50)
CustomerNumber: Int (Primary Key)
Addresses:
CustomerID: Int (Foreign Key)
Street: varchar(40)



Using the dataset designer and a database with a master-detail relationship
on it:
Drop the 'Master' table (Customers) and 'detail' table (Addresses) and
create the relationship between the CustomerID field between them using drag
and drop in the dataset designer.

Now, add a field to the detail dataset (Addresses) and call it
'CustomerName'. Set the expression of this new field to
'Parent.CustomerName'.

Now using whatever means necessary, load the dataset with data.

Everything will work as expected - no problems.

Using ASP.NET assign the dataset above to the session (causing it to be
streamed):
HttpContext.Current.Session["test"]=dset;

Now load it from the stream:
Mydatasettype dset=(Mydatasettype)Session["test"];

You will get the error when it streams back. If you remove the expression
of 'Parent.CustomerName' from the dataset definition the error will go
away...

Can I please get help/fix/etc?

Wayne

Here is the complete error info...







Source Error:

Line 74: this.CaseSensitive = ds.CaseSensitive;
Line 75: this.EnforceConstraints = ds.EnforceConstraints;
Line 76: this.Merge(ds, false,
System.Data.MissingSchemaAction.Add);
Line 77: this.InitVars();
Line 78: }

Source File: loginandrights.cs Line: 76

Stack Trace:

[IndexOutOfRangeException: Cannot find relation 0.]
System.Data.DataTableRelationCollection.get_Item(Int32 index)
System.Data.LookupNode.Bind(DataTable table, ArrayList list)
System.Data.DataExpression.Bind(DataTable table)
System.Data.DataExpression..ctor(String expression, DataTable table, Type
type)
System.Data.DataColumn.set_Expression(String value)
System.Data.Merger.MergeSchema(DataTable table)
System.Data.Merger.MergeTableData(DataTable src)
System.Data.Merger.MergeDataSet(DataSet source)
System.Data.DataSet.Merge(DataSet dataSet, Boolean preserveChanges,
MissingSchemaAction missingSchemaAction)
LoginAndRights..ctor(SerializationInfo info, StreamingContext context) in
loginandrights.cs:76

[TargetInvocationException: Exception has been thrown by the target of an
invocation.]
System.Reflection.RuntimeConstructorInfo.SerializationInvoke(Object
target, SerializationInfo info, StreamingContext context) +0
System.Runtime.Serialization.ObjectManager.CompleteISerializableObject(Object
obj, SerializationInfo info, StreamingContext context) +149
System.Runtime.Serialization.ObjectManager.FixupSpecialObject(ObjectHolder
holder) +128
System.Runtime.Serialization.ObjectManager.DoFixups() +170
System.Runtime.Serialization.Formatters.Binary.ObjectReader.Deserialize(HeaderHandler
handler, __BinaryParser serParser, Boolean fCheck, IMethodCallMessage
methodCallMessage) +269
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream
serializationStream, HeaderHandler handler, Boolean fCheck,
IMethodCallMessage methodCallMessage) +183
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream
serializationStream) +11
System.Web.Util.AltSerialization.ReadValueFromStream(BinaryReader reader)
System.Web.SessionState.SessionDictionary.Deserialize(BinaryReader
reader)
System.Web.SessionState.StateClientManager.Deserialize(Stream stream,
Int32 lockCookie)
System.Web.SessionState.SqlStateClientManager.DoGet(String id, SqlCommand
cmd)
System.Web.SessionState.SqlStateClientManager.GetExclusive(String id)
System.Web.SessionState.StateClientManager.BeginGetExclusiveSync(String
id, AsyncCallback cb, Object state)
System.Web.SessionState.SqlStateClientManager.System.Web.SessionState.IStateClientManager.BeginGetExclusive(String
id, AsyncCallback cb, Object state)
System.Web.SessionState.SessionStateModule.GetSessionStateItem()
System.Web.SessionState.SessionStateModule.BeginAcquireState(Object
source, EventArgs e, AsyncCallback cb, Object extraData)
System.Web.AsyncEventExecutionStep.System.Web.HttpApplication+IExecutionStep.Execute()
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean&
completedSynchronously)
 
S

Steven Cheng[MSFT]

Hi Wayne,

Thanks for your posting. As for the problem you described, I've done a
simple local test with the following table

Customers
Customers
CustomerID: Int (Primary Key) (auto increase)
CustomerName: Varchar(50)

Addresses:
CustomerID: Int (Foreign Key)
Street: varchar(50)

And I generate a TypedDataSet via draging the two sql tables and also add a
customer property in the

Addresses DataTable which get the parent record's CustomName via :


public string CustomerName
{
get
{
return this.CustomersRow.CustomerName;
}
}

When testing in asp.net page, I can store the TypedDataSet into Session and
retreive back from session correctly. So I'm thinking there maybe some
thing else cause the issue. I've attached my test DataSet and page in this
message, you can test it on your side.

In addition, you can also have a try to see whether the problem also occur
if you didn't add the custom property in Addresses Table, and from the
Error Message, it seems that when deserialize back form the session, the
TypedDataSet fail to retrieve back the Relation. So you can also test to
see whether any relations you add in your typeddataset will fail to be
serialized into session.
Also, what's the sessionState Mode you're using in your asp.net
application, InProc or StateServer or SqlSession?

Please have a check and if there's any further finding, please feel free to
post here. Thanks.

Regards,

Steven Cheng
Microsoft Online Support

Get Secure! www.microsoft.com/security
(This posting is provided "AS IS", with no warranties, and confers no
rights.)
 
W

Wayne Brantley

Steven,
Thanks for your post.

Couple of things:
1) When you add a custom element to the dataset diagram, set its type
to string and set the 'Expression' property of that field to
'Parent.CustomerName'.
After you do this, the MSDataSetGenerator will generate a class
for you - it is that class we are going to stream. I have no idea what or
how you did this:
Addresses DataTable which get the parent record's CustomName via :
public string CustomerName
{
get
{
return this.CustomersRow.CustomerName;
}
}

2) You do not mention if you dragged the CustomerID field from
Customers to the CompanyID field in Addresses. This creates the relation.
If you did, great, if not then I need you to do this part.
3) I am using SQL for session state.
4) There were no attachments to this message.

Having said all of that, I located the problem and modified the source
generated from MSDataSetGenerator so that it WILL stream correctly. The
root of the problem is as the columns are read in, it reads in the
expression for the column - however, at the time it does that, the relations
are not established causing an error. The reason it works when you create
the class with a call to new myTypedDataset is the column expression is not
assigned until after everything is created.

Here is the modification:

There is a method signature (constructor) of:
protected TypeDataSetClassName(SerializationInfo info, StreamingContext
context) {

In the first IF block you will add a line after InitVars (last line in the
if block) as follows:
this.InitExpressions(); //WB

That causes the Expressions for columns to be initialized AFTER everything
is setup. The method InitExpressions() was created by the
MSDataSetGenerator, but will NOT be there unless you have an expression in a
column.

The other change is to REMOVE the expression from the column!!! In the
constructor of the class for the table with the Expression (in this case
Addresses) you will find a constructor that takes a DataTable as an
argument:

internal AddressesDataTable(DataTable table) :

At the bottom of the method, add this:

table.Columns["CustomerName"].Expression=""; //WB


So, the way this works again is this:
When constructed from code, the expression is assigned in the
InitExpressions() function, so the fact that we blanked out the Expression
has no effect. However, when read from a completed dataset that was
streamed out - this Expression is populated and will cause an error - so
when this class is constructed, we blank it out. After everything has been
constructed in the dataset, we set the expression back by calling
InitExpressions() [that is the first part of the modification]

------------------------------------------------------------------------------------------------

BTW, in messing with this, I found a problem with a method. The Copy method
of a dataset...(once the get the above example basics working you can use it
to try this).
//Show it works with original....

MyTypedDataSet dset=ExistingDataSet;

if (dset==null){

return;

}

dset.CustomerNameColumn.Expression="Parent.CustomerName";

//now use CopyOfDset and reference the CustomerName field. It will work as
expected...


//Try to copy the entire dataset

MyTypedDataSet dset=ExistingDataSet;

if (dset==null){

return;

}

MyTypedDataSet CopyOfDset=dset.Copy();

CopyOfDset.CustomerNameColumn.Expression="Parent.CustomerName";

//now use CopyOfDset and reference the CustomerName field. It will always be
blank.
 
S

Steven Cheng[MSFT]

HI Wayne,

Thanks for your response. I've read the modified TypedDataSet and seen the
things you mentioned. I'm sorry for my mistakes that I didn't add a new
Element in the AddressTAble's schema (which has a expression). I just
manually add a public property named "CustomerName" in the AddressesRow
class in the dataset's .cs source file. After test your code, I've found
the problem you mentioned, yes, as you said, that's because the dataset's
constructing order when it is deserialized from Session. And it is also
because the Session Mode is SqlSErver mode so that the

OrderDS(SerializationInfo info, StreamingContext context)

constructor will be called. I used to use InProc Session mode so that
haven't found the problem.

In addition, since the problem is caused by the DataRelation hasn't been
created before the TypedDataSet Merge data from the Session deserialized
data, we can also workaround it via only modifying the

OrderDS(SerializationInfo info, StreamingContext context)

constructor as below:

=====================
protected OrderDS(SerializationInfo info, StreamingContext context) {

// always call the InitClass here so as to reconstruct the TypeDS's
structure
this.InitClass();

string strSchema = temp = ((string)(info.GetValue("XmlSchema",
typeof(string))));

if ((strSchema != null))
{
DataSet ds = new DataSet();
ds.ReadXmlSchema(new XmlTextReader(new
System.IO.StringReader(strSchema)));

// since the two tables have been added into the Tables
collectoin in "InitClass", we don't
// need to readd them
// change to use if ((ds.Tables["Addresses"] != null &&
this.tableAddresses == null))

if ((ds.Tables["Addresses"] != null && this.tableAddresses
== null)) {
this.Tables.Add(new
AddressesDataTable(ds.Tables["Addresses"]));
}
if ((ds.Tables["Customers"] != null && this.tableCustomers
== null)) {
this.Tables.Add(new
CustomersDataTable(ds.Tables["Customers"]));
}

==========================================

Thus, we don't need to change other places in the DataSet's code file.

I've also checked some former issues dicussing on the similiar problem when
deserializing the TypedDataSet. Currently it seems that we can only
manually do some modification on the ds's generated source code to overcome
the problem. If you still have any questions on this, please feel free to
post here. Also, I'm sorry for the inconvenience it has brought you.

Regards,

Steven Cheng
Microsoft Online Support

Get Secure! www.microsoft.com/security
(This posting is provided "AS IS", with no warranties, and confers no
rights.)
 
W

Wayne Brantley

Steven,
I think it is NOT an acceptable workaround to change the code everytime
your dataset changes.

1) Did you submit this as a bug so the team will correct it?
2) Is it possible to get an updated MSDataSetGenerator tool?

Do I have any other alternatives?




Steven Cheng said:
HI Wayne,

Thanks for your response. I've read the modified TypedDataSet and seen the
things you mentioned. I'm sorry for my mistakes that I didn't add a new
Element in the AddressTAble's schema (which has a expression). I just
manually add a public property named "CustomerName" in the AddressesRow
class in the dataset's .cs source file. After test your code, I've found
the problem you mentioned, yes, as you said, that's because the dataset's
constructing order when it is deserialized from Session. And it is also
because the Session Mode is SqlSErver mode so that the

OrderDS(SerializationInfo info, StreamingContext context)

constructor will be called. I used to use InProc Session mode so that
haven't found the problem.

In addition, since the problem is caused by the DataRelation hasn't been
created before the TypedDataSet Merge data from the Session deserialized
data, we can also workaround it via only modifying the

OrderDS(SerializationInfo info, StreamingContext context)

constructor as below:

=====================
protected OrderDS(SerializationInfo info, StreamingContext context) {

// always call the InitClass here so as to reconstruct the TypeDS's
structure
this.InitClass();

string strSchema = temp = ((string)(info.GetValue("XmlSchema",
typeof(string))));

if ((strSchema != null))
{
DataSet ds = new DataSet();
ds.ReadXmlSchema(new XmlTextReader(new
System.IO.StringReader(strSchema)));

// since the two tables have been added into the Tables
collectoin in "InitClass", we don't
// need to readd them
// change to use if ((ds.Tables["Addresses"] != null &&
this.tableAddresses == null))

if ((ds.Tables["Addresses"] != null && this.tableAddresses
== null)) {
this.Tables.Add(new
AddressesDataTable(ds.Tables["Addresses"]));
}
if ((ds.Tables["Customers"] != null && this.tableCustomers
== null)) {
this.Tables.Add(new
CustomersDataTable(ds.Tables["Customers"]));
}

==========================================

Thus, we don't need to change other places in the DataSet's code file.

I've also checked some former issues dicussing on the similiar problem
when
deserializing the TypedDataSet. Currently it seems that we can only
manually do some modification on the ds's generated source code to
overcome
the problem. If you still have any questions on this, please feel free to
post here. Also, I'm sorry for the inconvenience it has brought you.

Regards,

Steven Cheng
Microsoft Online Support

Get Secure! www.microsoft.com/security
(This posting is provided "AS IS", with no warranties, and confers no
rights.)
 
S

Steven Cheng[MSFT]

Hi Wayne,

Thanks for the followup. Yes, I've contacted the dev team and they've
already filed this issue in the existing known issue lists for the
currently version of ADO.NET. Also, currently it seems that we could only
manually do some modification in the code file to workaround it. In fact,
the problem is concerned with any columns which has a Expression in a
TypedDataSet , such column will cause the Merge of the Dataset(when
deserialize from persistence stream).
This issue is going to be fixed in the next version of ADO.NET ( Whidbey
version of .net ). Thanks.

Regards,

Steven Cheng
Microsoft Online Support

Get Secure! www.microsoft.com/security
(This posting is provided "AS IS", with no warranties, and confers no
rights.)
 
W

Wayne Brantley

Does microsoft realize what a pain this is? All they have to do is ship the
SOURCE to that one tool. Or publish a fix to that one tool. Empower
developers to help themselves. Imagine the mods that would have already
been created on this MSDataSetGenerator class for enhanced functionality.

Please pass my suggestion along. www.adoguy.com made one for his book - I
am going to look into using it -as it comes with source (my gosh this is not
a super secret you need to protect). I guess I could disassemble it and
try to repair it...
 
S

Steven Cheng[MSFT]

Hi Wayne,

Thanks for your sincere advice. Yes, I agree with you that it's a good idea
that the .NET involve all the communitry , developers to enhance it. And
actually most of the new features or updates in the new verions of .net are
contributed by the feedbacks from our customers.

In addition, I also recommend that you forward the recommendation to the
Microsoft Wish Program:

Microsoft offers several ways for you to send comments or suggestions about
Microsoft products.
World Wide Web - To send a comment or suggestion via the Web, use one of
the following methods:

" In Internet Explorer 6, click Send Feedback on the Help menu and then
click the link in the Product Suggestion section of the page that appears.
" In Windows XP, click Help and Support on the Start menu. Click Send your
feedback to Microsoft, and then fill out the Product Suggestion page that
appears.
" Visit the following Microsoft Web site: http://www.microsoft.com/ms.htm
" Click Microsoft.com Guide in the upper-right corner of the page and then
click Contact Us . Click the link in the Product Suggestion section of the
page that appears.
" Visit the following Microsoft Product Feedback Web site:
"http://register.microsoft.com/mswish/suggestion.asp" and then complete and
submit the form.

Thanks again for your understanding.

Regards,

Steven Cheng
Microsoft Online Support

Get Secure! www.microsoft.com/security
(This posting is provided "AS IS", with no warranties, and confers no
rights.)
 

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

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top