PDA

View Full Version : potentially unrelated question about proxies


smhinsey
01-09-2006, 06:30 PM
Is there anything in Spring.Web.Services that I may've missed that would help out with the problem of wsdl.exe and web reference generated proxies using fields instead of properties, thus preventing them from being databound?

Bruno Baia
01-10-2006, 11:35 AM
hi,

What do you mean by :
(...) the problem of wsdl.exe and web reference generated proxies using fields instead of properties (...)

-Bruno

smhinsey
01-10-2006, 02:53 PM
This is a slightly different "proxy," but if you use a Web Reference or manually run WSDL.exe on a wsdl defintion, you'll get type classes that use public fields for all of their members, instead of properties.

Bruno Baia
01-10-2006, 03:20 PM
I guess you are using Visual Studio 2003 with .NET 1.1, because i heard about that as a new feature in Visual Studio 2005.
Install .NET 2.0 framework and try to use the last wsdl.exe

-Bruno

smhinsey
01-10-2006, 04:04 PM
Yeah, it's fixed in 2.0, but unfortunately I'm stuck with 1.1 for the time being.

Mark Pollack
01-10-2006, 04:39 PM
Hi,

This came up before - see Post: wsdl and code generation (http://forum.springframework.net/viewtopic.php?t=122). We did not incorporate any solution into Spring.NET but there were a few workaround listed there that you may fine useful. Bruno, is this something we can incorporate into the generation of a client side proxy from the wsdl?

Cheers,
Mark

smhinsey
01-10-2006, 08:26 PM
I have ended up going with the WSCF library for the time being, but I am going to look into what you linked to in that other thread, Mark.

This seems like something that it'd be nice for the WebServiceProxyFactory ti implement, but I am going to reserve judgement on that for the time being. I'm still sort of struggling with how best to use generated code with the existing tools.

Bruno Baia
01-10-2006, 08:29 PM
Bruno, is this something we can incorporate into the generation of a client side proxy from the wsdl?


The WebServiceProxyFactory takes the code generated by wsdl.exe and generates a proxy that implement an interface which define his own objects, so you need to change the proxy generated by wsdl to make it work with custom objects. Not appropriate here.
Anyway it should be use when you control both client and server.


A solution can be a factory that will create a proxy which generate a property for each field :

Customer[] customers = myService.GetCustomers();
myDataGrid.DataSource = (new FactoryWithNoNameYet(customers)).GetObject();



-Bruno

Aleks Seovic
01-11-2006, 03:18 AM
I guess my take is that client-side types for parameters and return values of a web service shouldn't be generated based on WSDL -- they should be designed and coded based on the requirements of the client app and values returned by the service methods should be mapped to them.

WebServiceProxyFactory should for the most part ignore WSDL document -- it should take client-side service interface and implement its methods by generating calls that use Invoke to call web service methods.

If client-side service interface is a driving contract, then it follows that types referenced by that interface either as parameters or return values of the methods need to exist before any proxy generation happens.

- Aleks

Bruno Baia
01-11-2006, 09:05 AM
Exactly.

The new WebServiceProxyFactory (called WebServiceClientFactory for now - samples in cvs in src/POC/WebServices/ directory) takes a local service interface and a web service URL and generates a WebService proxy without the need to generate proxy class using wsdl.exe.
Even if the factory should be used only when you control both client and server, you still can make it only with the client and the web service definition (wsdl).

Let's take a exemple with a webservice that returns a list of customer

[WebMethod]
public Customer[] GetCustomers() {
...
}

wsdl definition of the Customer type will be :

<s:complexType name="Customer">
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="ID" type="s:int" />
<s:element minOccurs="0" maxOccurs="1" name="Name" type="s:string" />
<s:element minOccurs="1" maxOccurs="1" name="Birthday" type="s:dateTime" />
</s:sequence>
</s:complexType>

where wsdl.exe should generates something like that :

public class Customer {
public int ID;
public string Name;
public System.DateTime Birthday;
}


To use the new WebServiceProxyFactory you need to define the local interface :

public interface ICustomer
{
Customer[] GetCustomers();
}

and define your own Customer class that match the wsdl defintion. here is an exemple :

public class Customer
{
private int id;
private string name;
private System.DateTime birthday;

public int ID
{
get { return id; }
set { id = value; }
}

public string Name
{
get { return name; }
set { name = value; }
}

public System.DateTime Birthday
{
get { return birthday; }
set { birthday = value; }
}

// You can add/change properties for databinding
public int Age
{
get { return (DateTime.Now.Year - birthday.Year); }
}
}


And You're done, but you need to rewrite a lot of code.

- Bruno.

Mark Pollack
01-11-2006, 12:26 PM
Hi,
The alternative to the code/interface first approach is "contract-first" - with the add-in at thinktecture (http://www.thinktecture.com/Resources/Software/WSContractFirst/default.html) leading the way for practical development with this approach - which is what smhinsey is using. This follows the web service development approach where everything is driven off the wsdl, including parameter/return types - without the goofy problems of the current wsdl generation process (different types for same logical return type/method parameter if used in different wsdls). One can of course say that the person supplying the web service could provide .net/c# dll/.jar with appropriate interfaces/classes but that was the whole point/promise of codegen from wsdl - which it obviously fell short of. If web-services is being used not as a cross-platform approach to supplying a service, say just .net to .net with cross-platofrm calling being a secondary requirement (potentially for internal apps from client-to-middle tier) then it is more realistic to assume that the supplier of the service would also supply the .dll with appropriate objects. Anyway, point being that there are two valid approaches - if we can provide any value to the contract-first approach that would be a plus.
Cheers,
Mark

smhinsey
01-11-2006, 04:31 PM
I'm still trying to get my head around using something other than a WSDL as a service contract, so I'll hold off on commenting on that direction.

Here're a few things I think could be useful to support contract-first design:

Explicitly export an interface as a contract. The configuration should allow you to map an implementation to the interface. This may already be really close to being implemented, because it's pretty similar to what's already there.

For a service consumer, I don't think there's a lot that can be done without code generation, which I don't know to be the goal of Spring.NET. If we decided we wanted to implement some, the reverse of the above process would be nice. Point at a WSDL, get an interface, an implementation (i.e., a proxy) and a mock implementation. This is really close to what WSCF gets you, with the addition of including the mock implementation and the Spring config.

smhinsey
01-11-2006, 04:42 PM
The more I look into it, it seems like what Spring can help with the most is in the area of the Service Gateway implementation. Mapping the DTOs coming over the wire to local domain objects is something that Spring might be particularly well suited for. It seems pretty feasible to come up with some sort of factory object that could consume a DTO and return the equivalent domain object. This would essentially require the implementation of object to object mapping, which I think that the object navigation code is pretty close to already.

Aleks Seovic
01-11-2006, 10:36 PM
I guess my point is that there are two contracts -- service contract and client contract. WSDL is great fo describing service contract, but that doesn't mean that every client should blindly accept and use the same contract.

Client contract should be defined in terms of a regular interface. The actual connection between the client contract (interface) and the service contract (WSDL-defined endpoints) can be implemented using either Spring.NET WebServiceProxyFactory, when simple one-to-one mapping between client contract and service contract exists, or using a custom adapter class for more complex scenarios.

Current implementation of the WebServiceProxyfactory, as Bruno pointed out, expects client-side domain objects to support values returned by the service and mapping is somewhat implicit.

What we would like to have long-term, and what is pretty much what I believe you are proposing, is a more explicit way to map domain model objects to the service schema objects. You will be able to configure mappings between domain types and schema types within WebServiceProxyFactory definition and we will use them to create parameters and return values as necessary.

One way to do it might be to dynamically generate schema types based on WSDL and to use, as you are proposing, expression evaluator a.k.a object navigator functionality to map the two, but I believe better and definitely more performant approach is to allow direct mapping to XML nodes in the SOAP message.

- Aleks

smhinsey
01-11-2006, 10:47 PM
Mapping directly into the objects defined in the schema would probably be the ideal situation. That's probably doable by subclassing HttpSoapClient and overriding the Invoke method. It may even be simpler than that. It seems like it's essentially the same thing as moving the binding attributes outside of the domain objects and into a configuration file.

spmva
01-12-2006, 02:21 PM
As the originator of that thread that Mark referenced (http://forum.springframework.net/viewtopic.php?t=122), I just thought I'd give a little follow-up. Since that post of a final, not great, but working solution, I've made no modifications to the generator and it has worked well for my project and the projects of several other colleagues. It definitely fit my need on the way to improvements in .NET 2.0, an upgrade I have yet to make.

Steve

smhinsey
01-13-2006, 08:03 PM
As a follow up of my own, what I have ended up doing is using something similar to J2EE's Value Object Assembler pattern by treating the generated objects as untrustworthy DTOs and converting them, using assemblers, to local domain objects. It's a little code heavy at the moment, but I am hoping to factor it down as time goes by.

Incidentally, I used spring's service location features to write an adapter class that my gateway uses to interact with the assemblers, and it works really nicely. All the adapter does is get the type of the object to be assembled and grab it out of a specially named context. Since all of the assemblers implement a basic interface that defines ToDto and FromDto, I don't have to hard code any references in that part of the app. It's really nice.

spmva
01-14-2006, 01:54 PM
I tried a similar approach early on converting the generated objects back and forth. I also came to the same position you mentioned that the whole thing was a bit "heavy" and cumbersome.

I do find it interesting that you continued with it and feel that with some refactoring it will be a good solution. How many assemblers did you have to create to cover your entire domain?

Aleks Seovic
01-14-2006, 06:15 PM
I think the ability to define mappings between domain objects and messages would remove the complexity and "heavyness" associated with the manual coding of assemblers.

But either way, I believe that not propagating WSDL-generated types to higher application layers is a good choice as it helps decouple client from the web service.

- Aleks

spmva
01-15-2006, 01:01 PM
Aleks, I think you beat me to my point. In an environment where the object domain is relatively simple and small, manually coding assemblers might not be so awful, which is why I asked the question about the number of assemblers that had to be maintained. I would also think that a domain that is relatively mature and settled, would be a far better candidate for assemblers as opposed to something like mine where I have a large number of domain objects, the domain itself has a lot of complex, composite objects and the entire domain model is relatively young, growing and changing.

As much as I wanted that decoupling of the client from the web service, I deemed it an impossibility at the time, given a number of constraints and limitations, and so far have not had any serious, application stopping regrets. Of course, I've cried myself to sleep at the thought of retrofitting such a decoupling. :-)

You mentioned the "ability to define mappings between domain objects and messages". I read something into that, hence the following:

"Is there anything out there that does that in the .NET environment?", he asked Aleks and the world, fearing the resounding "Yes", followed by "Obviously", making him feel like the worst software architect on the planet. Of course, how bad could he be at his job...he chose to build his entire application around Spring Framework. :-)

smhinsey
01-16-2006, 03:58 AM
I probably have about 35 assembler objects. I used CodeSmith to automatically generate them, so it wasn't a huge problem.

My thoughts about how this might be factored into something more palatable down the road are sort of a mix between hoping that I'll come up with something better - I'd thought about doing something with reflection, for example, but I haven't thought it through very much - and waiting to see how Aleks's idea about direct mapping into the messages pans out. I really prefer the latter approach, but it's important to me that whatever solution I arrive at have as much compile time support as possible (I don't want to lose intellisense on the proxy's methods, etc), and I just haven't been able to reconcile that desire with the idea of mapping the domain objects into messages. I have some thoughts about how that might work, but again, I haven't really thought it through yet.

I figure that, at a minimum, I end up with a unit tested gateway class that abstracts the actual DTO mechanisms and will allow me to experiment pretty freely with a better solution by swapping out the assembler mechanisms at run time. This doesn't seem like a bad place to be.

By the way, this issue is dear to me for a strange reason that I think is worth mentioning. I think this problem is indicative of a trend in .NET, mostly borne out by MSDN and official sources, towards relying on the "good enough" status quo (i.e., web references) without really analyzing their flaws. I am quite honestly a little appalled at the immaturity of the web services platform in .NET, given how integral it is to the larger platform. I realize that WCF/Indigo will go a long way towards resolving a lot of these issues, but quite honestly, I can't really wait, and if Spring, or something else, came in and provided a really solid and well designed approach to using web services for the users that don't rely on DataSets and other less than optimal approaches, I think it would be absolutely huge.

Aleks Seovic
01-16-2006, 08:46 AM
"Is there anything out there that does that in the .NET environment?", he asked Aleks and the world, fearing the resounding "Yes", followed by "Obviously", making him feel like the worst software architect on the planet. Of course, how bad could he be at his job...he chose to build his entire application around Spring Framework. :-)

:lol:

Yes, but it's not so obvious and for all practical purposes it is completely useless.

This whole discussion and your post in particular made me dig a bit deeper into web services code in the BCL and I discovered a few things.

Message serialization and deserialization is performed by dynamically generated XmlSerializer subclasses. These serializer classes are created using both reflection over the proxy class and values from attributes proxy class and its methods are decorated with. Unfortunately, there is a lot of internal classes and private methods that are involved in the whole process, as well as several static methods, so it is pretty much impossible for us to replace the serializers. :(

Afterwards, I looked at the code for WSE 2, hoping that we can plug in custom serialization mechanism there -- there is really no reason not to use WSE, so I wouldn't mind imposing that requirement if it lets us do what we want. Unfortunately, WebServicesClientProtocol class doesn't replace SoapHttpClientPortocol's Invoke method, so same limitations still apply.

The only possible solution I see is to completely disregard everything that deals with client-side proxies and to implement our own client-side proxy class using low-level WSE SoapSender, SoapClient, and SoapEnvelope classes. These classes are very clean, very well-designed, and would give us complete control over message processing. We still need to figure out what's the best way to map objects to messages, but I believe this approach would allow us to do what we need.

I will investigate a bit more when I find some spare time, but in the meantime I'd like to hear from you guys what you think would be the best way to map objects to XML messages -- how would you like to configure such a mapping, both for request and for response messages.

Later,

Aleks

Bruno Baia
01-16-2006, 10:30 AM
There is a nice documentation on TSS.net about code-first ans contract-first support in WCF :

http://www.theserverside.net/articles/showarticle.tss?id=DesignServiceContracts

There are some ideas for contract-first implementation like Raw Messages (un-typed messages) and IXmlSerializable types.


-Bruno

smhinsey
01-16-2006, 04:42 PM
There are some good ideas in that article, but neither the raw messages or the IXmlSerializable approaches meet my requirement of having a strongly typed API. Perhaps it's simply a lack of imagination!

Am I the only one that is so insistent on that? It seems like a lot of people working in this space are okay with that.

Aleks Seovic
01-16-2006, 05:59 PM
:)

You are definitely not the only one -- I very much prefer strongly typed API because that's what allows you to make services location transparent and to plug in different implementations as necessary (mocks or stubs for testing, for example).

We just need to figure out how to map types from the strongly typed API to messages used over the wire.

- Aleks