Friday, December 22, 2006

How to: Expose WCF service also as ASMX web-service

In my last post, I wrote about my endeavors to consume a WCF service from an ASMX client to support non-WCF clients such as .NET 2 clients. The WCF service is exposed using a BasicHttpBinding endpoint and our main consumer is a Flex 2 web-application.

As the support for WS-I Basic Profile 1.x compliant web-services is quite limited in Flex, I had to make ASMX-style clients works. What made it worse, is that there is a bug in Flex regarding the support for xsd:import: Flex will repeatedly request the XSD files on .LoadWSDL(), causing bad performance for the web-service and the client. Use Fiddler to watch all the HTTP GETs issued by Flex. The solution is to ensure that the generated WSDL is a single file, without any xsd:import, and this is just what a classic .NET 2 ASMX web-service provides.

WCF has good support for migrating ASMX web-services to WCF, and this feature can also be used to actually expose a native WCF service also as an ASMX web-service. Start by adding the classic [WebService] and [WebMethod] attributes to your service interface:

[ServiceContractAttribute(Namespace = "http://kjellsj.blogspot.com/2006/11"
Name = "IProjectDocumentServices")]
[ServiceKnownTypeAttribute(
typeof(DNVS.DNVX.eApproval.FaultContracts.DefaultFaultContract))]
[WebService(Name = "ProjectDocumentServices")]
[WebServiceBinding(Name = "ProjectDocumentServices"
ConformsTo = WsiProfiles.BasicProfile1_1, EmitConformanceClaims = true)]
public interface IProjectDocumentServices
{
[OperationContractAttribute(Action = "GetDocumentCard")]
[FaultContractAttribute(
typeof(DNVS.DNVX.eApproval.FaultContracts.DefaultFaultContract))]
[WebMethod]
DocumentCardResponse GetDocumentCard(KeyCriteriaMessage request);
. . .

Note that there is no need for adding [XmlSerializerFormat] to the [ServiceContract] attribute, just leave the WCF service as-is. Note also that you must specify the web-service binding Name parameter to avoid getting duplicate wsdl:port elements in the generated WSDL file. You should apply the [WebService] attribute using the same Name and Namespace parameters as for the [WebServiceBinding], to the class implementing the service. If not, you might get a wsdl:include and and xsd:import in the generated WSDL file.

Then add a new .ASMX file to the IIS host used for your WCF service, using the service implementation assembly to provide the web-services. I added a file called "ProjectDocumentWebService.asmx" with this content:


<%@ WebService Language="C#" Class="DNVS.DNVX.eApproval.ServiceImplementation
.ProjectDocumentServices, DNVS.DNVX.eApproval.ServiceImplementation" %>

That's it. You now have a classic ASMX web-service at your disposal. WCF supports running WCF services and ASMX web-services side-by-side in the same IIS web-application: Hosting WCF Side-by-Side with ASP.NET.

Most likely you must also apply [XmlElement], [XmlArray] and [XmlIgnore] at relevant places in your message and data contracts to ensure correct serialization (XmlSerializer) and ensure consistent naming for both WCF and ASMX clients/proxies. Remember that the XmlSerializer works only with public members and that it does not support read-only properties. Do not mark message and data contract classes as [Serializable] as this changes the wire-format of the XML, which might cause problems in Flex 2.Note that the ASMX-style WSDL will not respect the [DataMember (IsRequired=true)] setting or any other WCF settings because it uses the XmlSerializer and not the DataContractSerializer. You will get minOccurs=0 in the generated WSDL for string fields, as string is a reference type. This is because the XmlSerializer by default makes reference types optional, while value types by default are mandatory. Read more about XmlSerializer 'MinOccurs Attribute Binding Support' at MSDN.

Note the difference between optional elements (minOccurs) and non-required data (nillable) in XSD schemas. This is especially useful in query specification objects, make all the elements optional and apply only the specified elements as criteria - even "is null" criteria.


The unsolved WSDL problem described in my last post is no longer an issue for ASMX-clients, as the WSDL generated by the classic ASMX web-service of course natively supports .NET 2 proxies generated by WSDL.EXE. The issue with the Name parameter for the [CollectionDataContract] attribute is still a problem, it must be removed.

Thanks to Mattavi for the "Migrating .NET Remoting to WCF (and even ASMX!)" article that got me started.

15 comments:

Anonymous said...

Thanks for this post. Just the right information we were looking for.

SharpSmith said...

hi, i'm with same challenge (WCF + FLEX).
our services are hosted with window service and we found a way for flex to work with it ( can share a solution if you interested ) . how did you solve authorization issues between flex client and WCF ? basically what i need to implement is 1) resolve user id from each request to wcf service
2) ensure that only my flex clients can call the web service and not every one on internet can call them

what we did for now is creating some security token on first call to web service with userId encrypted into it and then passing it from flex via SOAP headers

would appriciate if you can share better solution.

Kjell-Sverre Jerijærvi said...

We have a custom STS-token provider for single-sign-on, and all our request messages takes a token as a required [MessageHeader] (i.e. SOAP headers). The token is then resolved against the STS to deduce the identity of the caller. All trafic is encrypted using SSL and certificates.

If you had used IIS as the host, you could have restricted the requests to be from specific IP-ranges. This is only viable when the IP-addresses of the service clients are wellknown.

SharpSmith said...

Impressive solution, basically for now i only need to support flex clients and single-sign-on isn't a requirement, but I'll sure use your approach when the site evolves .

Can you recommend open sour STS solution or you used custom approach ?
Thanks a lot, with your directions my problems now seems almost solvable :)

Kjell-Sverre Jerijærvi said...

We use a custom, legacy "STS" service.

Microsoft is working on making it easy to implement an STS server (see sep-07 issue of MSDN Mag). Also check Implementing claims-based security for WCF by Michele Leroux Bustamante.

You can also have a look at Active Directory Federation Services (ADFS) on WinServer2003 from Microsoft.

Finally, if you Google for +WCF +CardSpace you will find a lot of info about using CardSpace/STS for identity management and authN in your WCF solution. There are some samples available with the .NET 3.5 beta.

SharpSmith said...

thanks much

ni said...

would anyone please show me how to talk to WCF from Flex application,
I used to work with webservice and flex but when I get a string (parsed from a dataset from webservice) or I want to add data from flex through webservice -->problem is it need time(i mean slow because flex->webservice->com+ -> oracle database), I hope WCF will work better but I dont know how Flex work with it??

ni said...

SharpSmith would you please share me your solution , and tell me your opinion

Kjell-Sverre Jerijærvi said...

We used the Caingorm framework in Flex to talk to the WCF services. See this link for more info about Caingorm + WCF: Tutorial: Talking to WCF with Flex 3 via JSON or XML

ni said...

thx for answering kjell, by the way you know how to speak Vietnamese?!

Venkat said...

Thanks for your valuable article. I benn fighting to invoke a wcf service from adobe Lifecycle. Now I exposed wcf service also as asmx which able to invoke from adobe forms.

Anonymous said...

This is EXACTLY what I was looking for.

Thank you!

ARE A EM said...

Any plans to rewrite this article with respect to .Net 4?

Kjell-Sverre Jerijærvi said...

Nope, unless the need arise in one of my ongoing projects, sorry.

Anonymous said...

Thank you!!! Even after over 5 years, this article was incredibly helpful!

And it worked just fine in 4.0, so no need to rewrite!