Identical classes in Web Service methods

Posts   
 
    
brhal
User
Posts: 3
Joined: 06-Jul-2005
# Posted on: 06-Jul-2005 15:47:12   

I have the following code snippet:

RevisionEntity revisionEntity = new RevisionEntity();
CompanyEntity companyEntity = new CompanyEntity();
companyEntity.Revision = revisionEntity;

Then I have a web service method with this signature;

public CompanyEntity UpdateCompany(CompanyEntity companyEntity, RevisionEntity revisionEntity)

invoked like this;

webservice.UpdateCompany(companyEntity, revisionEntity);

I get this error message client side;

{"Server was unable to read request. --> There is an error in XML document (1, 52072). --> This document already has a DocumentElement node." }
webservice.UpdateCompany(companyEntity, new RevisionEntity()); 

doesn’t work either…

I’ve tried to wrap the two objects in a container class, but that didn’t solve the problem. What to do?

Thanks in advance, Brian

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 07-Jul-2005 10:14:16   

Webservices use the WSDL tool shipped with .net to create stub classes. Could you please check if you got stubclasses generated in your project? They're in the .cs/vb file generated for your service.

Frans Bouma | Lead developer LLBLGen Pro
brhal
User
Posts: 3
Joined: 06-Jul-2005
# Posted on: 07-Jul-2005 10:23:17   

The references are modified (from DataSet) and are OK.

But if I null one of the Revision-objects like this;

companyEntity.Revision = null;

or

webservice.UpdateCompany(companyEntity, null);

it works fine.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 07-Jul-2005 11:39:50   

Ok I'll check it out. Seems like an issue with the xml document being send to the server then, I'll try to setup a testcase here to see what the XML is that's being send.

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 07-Jul-2005 18:20:21   

Reproduced. I'll see what the cause is.

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 07-Jul-2005 19:22:08   

I can't solve it I think...

The main reason is that the XmlWriter passed into the IXmlSerializable implementation doesn't have a start-element set. So when you pass back 2 or more entities, the first entity XML element is the root node of the xml and the second is written into the same stream, which clashes as it will then make 2 root nodes appear in the xml stream.

I can't detect in the routine if some other entity has already been written into the stream. I peeked into the dataset code, and there they do exactly the same, so I wonder if it's possible to pass back 2 or more datasets to a webmethod. That would cause the same error, though I'm not sure. I can't find any other person having the same error...

As a workaround, you could add hte entities to an EntityCollection and pass that to the webmethod.

(not nice, I know, though I can't detect if the XmlWriter already has a startelement or not, so I can't write a startelement if that's not already been written...)

disappointed If someone knows an answer to this, please let me know.

Frans Bouma | Lead developer LLBLGen Pro
brhal
User
Posts: 3
Joined: 06-Jul-2005
# Posted on: 08-Jul-2005 13:07:31   

OK - I'll try to work around it...

Thanks a lot you for your time!

simple_smile

Backslash
User
Posts: 21
Joined: 21-Jun-2005
# Posted on: 30-Jul-2005 02:02:54   

I just ran into this issue today. I'm using the Adapter model with ASP .NET 2.0 Beta 2. I am trying to return a container class from a webservice that contains a couple string, integer, and boolean fields, plus an entity and an entity collection. I have a little more detail on the cause of the error. The XML that is automatically generated and sent across the wire is 100% valid, but the error occurs when trying to deserialize the XML on the client. Using .NET 1.1 on the client, I get the same error as brhal, but using .NET 2.0 for the client I get a more useful error message. The exact error I get is:

IXmlSerializable CustomerEntity performed an illegal operation: ReadXml() method attempted to read past the closing tag 'Customer' from namespace 'http://tempuri.org/'.

From this is seems like the ReadXml method is trying to load all the data passed back from the web service, when it should (ideally) first try to find the parent node for its own type. Can this be fixed by upding the ReadXml methods?

Backslash
User
Posts: 21
Joined: 21-Jun-2005
# Posted on: 02-Aug-2005 21:08:58   

I looked into this some more, and I've found that when I have two objects to return (say an entity and an entity collection), a custom container class should be created. The container class simply contains public variables, such as a Customer entity and a Group entity collection. The objects are then wrapped according to the container class that will be returned. This makes the XML look more like:

<Customer> <entity> ... </entity> </Customer> <Groups> <entityCollection> ... </entityCollection> </Groups>

I took a closer look at the ReadXml methods myself, and I found that the XmlReader has already been initialized so that it only contains content for the given entity or entity collection. I played around with the code a bit, and found that changing

doc.Load(reader);

to

doc.LoadXml(reader.ReadOuterXml());

fixed the 'This document already has a DocumentElement' error. So now I can create my own container class that has many entites and entity collections, and it will be deserialized properly, after fixing the reference map file. Unfortunately, I have no idea why it works. Perhaps the LoadXml method is simply better suited to the task at hand.

Backslash
User
Posts: 21
Joined: 21-Jun-2005
# Posted on: 02-Aug-2005 22:03:32   

Update: I created a test webmethod that takes in two different entities, as in the original post by brhal. I found that there is no need to create a container class for input parameters. It works as expected with the above code changes.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 02-Aug-2005 22:17:22   

Cool! I searched high and low for a solution for this! thanks! smile . I'll check it out first thing tomorrow!

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 03-Aug-2005 18:23:37   

OK!

I first did doc.Load(reader.ReadOuterXml()); which gave vague URI crap but after some swearing on everyone who cooked up these .NET webservice classes, I read the posting again, and I saw I had to use LoadXml(). It then worked! magnificent! smile

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 03-Aug-2005 18:41:32   

Ok, a hotfix for the runtime libraries is now available, in the runtime libraries section.

Please let me know if these give any problems.

Frans Bouma | Lead developer LLBLGen Pro
Backslash
User
Posts: 21
Joined: 21-Jun-2005
# Posted on: 03-Aug-2005 20:36:40   

I tried out the hotfix, and it works great! Thanks for your quick support.

On a side note, I figured out a bit more on the difference between the Load and LoadXml methods inside of the XmlDocument class. LoadXml simply creates an XmlValidatingReader based on the given string, and then calls its own Load method based on an XmlReader. In the interest of efficiency, I tried the following code in your ReadXml method, which works just as well:

public virtual void ReadXml(XmlReader reader)
{
    XmlDocument doc = new XmlDocument();
    XmlValidatingReader valReader = new XmlValidatingReader(reader);
    valReader.EntityHandling = EntityHandling.ExpandCharEntities;
    valReader.ValidationType = ValidationType.None;

    doc.Load(valReader);
    ReadXml(doc.DocumentElement);
} 

The main difference is setting the entity handling property to ExpandCharEntities. Thanks to Lutz Roeder's .NET Reflector for the insight!

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 04-Aug-2005 10:56:30   

Thanks for the extra info! simple_smile

I think I'll stick with the one liner wink . I'm glad it finally works. I already couldn't figure out why it wouldn't work, as with datasets it's not a problem. Now I know simple_smile

Frans Bouma | Lead developer LLBLGen Pro