Web services with Visual FoxPro (VFP) have never been easy.
The most common Web service tool for FoxPro is the SOAP Toolkit, which has been discontinued and which had a host of problems when dealing with complex types passed over Web Services. In this article I'll show how you can leverage the powerful Web service features of .NET and the new Windows Communication Foundation in your FoxPro application through COM Interop.
Today more and more applications interact and communicate via Web services either as clients or as publishers. It's becoming quite common for many application development scenarios to include Web service functionality as an integral part of the development process. The good news is that Web service technology has stabilized and today interoperability is much better. It's much easier to call a Java Web service from .NET or Visual FoxPro than it was in the early days of constantly moving standards and incompatible Web service platform implementations. Over time Web services have also become more complex, especially in regards to the data that is sent over the wire. It's very common today to have Web services that send complex messages that contain many nested types of information in single messages.
The State of FoxPro Web Services
For Visual FoxPro developers, dealing with complex Web services has always been problematic because the default tool that is natively available through COM-the SOAP Toolkit-is limited. Whether you're building or consuming Web services in Visual FoxPro, your first stop likely takes you to the Soap Toolkit. Visual FoxPro ships and installs this COM-based tool. FoxPro's internal Web service client and server Wizards both rely on it to publish and consume Web services. The SOAP Toolkit is a pretty crude tool by today's standards-it provides only the bare basics of Web service interoperability and can't easily deal with Web services that need to consume complex types or need to use extended Web service protocols like the WS-* Web service specifications.
The SOAP Toolkit is officially discontinued and no longer supported by Microsoft.
If you're using the SOAP Toolkit to consume Web services that are returning anything but simple type values you will quickly find that it's pretty tedious to deal with the data that is returned, as you end up having to parse the XML messages on your own. Alternately you can also resort to implementing convoluted type interfaces using the SOAP Toolkit's extension interfaces that allow mapping of classes. However, this process is almost more work than parsing the XML data. In my experience this lack of support for complex types is a major stumbling block as almost all Web services that are published by providers commercially are based on complex message types using
objects, arrays or collections, and enumerations, none of which are handled natively by the SOAP Toolkit.
For publishing Web services the SOAP Toolkit fares no better-it provides the ability to use either an ASP or ISAPI listener to publish COM objects as Web services. Although Visual FoxPro's Web service Wizard does a decent job of publishing simple Web services, the services published are limited in that you can't easily publish anything but simple types from your exposed service methods. Add to that some limitations in Visual FoxPro to expose nested types to COM and it becomes very difficult to publish any content that requires anything but single hierarchy
objects. This may be workable in simple scenarios or in FoxPro-to-FoxPro calling scenarios where you can often use raw XML strings to pass data across applications, but for many Web service and Service Oriented Architecture (SOA) scenarios that need to interact with non-FoxPro applications, this limited
functionality is not adequate.
The last straw for the SOAP Toolkit, however, is the fact that it is no longer officially updated or supported by Microsoft. All new development on it has stopped so there won't be any future improvements or bug fixes (other than critical hotfixes for security), so it won't keep up with the latest standards should they change.
This makes the SOAP Toolkit a somewhat volatile solution, especially if you are interoperating with Web services from the Java and .NET platforms, which are constantly changing and updating to the latest standards. Currently the SOAP Toolkit is still in line with the latest SOAP 1.2 specification, but it doesn't deal with any of the WS-* specifications or any of the upcoming SOAP 2.0 specifications.
Using .NET to Provide a Web Service Bridge
Microsoft's official recommendation for Web services is to use .NET to access and publish Web services. .NET is Microsoft's preferred Web services platform where all future development and support for new technologies is implemented, so Microsoft is recommending that developers use .NET in combination with COM for non- .NET technologies like Visual FoxPro. While this may seem arrogant at first it makes sense in that the .NET 2.0 Web services stack and Windows Communications Foundation (WCF) are .NET-only technologies.
Web Service Client
For FoxPro developers, creating a .NET Web services client means that you can create a .NET Web service client and use COM Interop to interact with this generated proxy object from FoxPro.
Microsoft's official Web service platform is .NET.
This process is not difficult. The process actually makes the experience of consuming Web services easier than with the SOAP Toolkit because .NET deals much better with complex Web services and provides a strongly typed interface to them, including automatic message type creation (parameters and return values) and full IntelliSense support. In many cases you can simply pass the complex result messages back to Visual FoxPro and access them directly over COM.
You can drive the Web service proxy either directly from FoxPro by passing the proxy back to FoxPro over COM or by creating a shim (methods in .NET code that act as front ends to the Web service). The latter is more work, but provides more flexibility through abstracting the Web service with a client interface that can handle errors, perform data conversions, and protect client code from future implementation changes of the service.
Web Service Publishing
.NET also supports easy publishing of Web services through the ASP.NET ASMX framework. ASMX Web services-named for the file extension that is used-are a special ASP.NET handler that can execute Web service classes and expose these classes to the Web. Like ASP.NET you can use COM Interop to access FoxPro code from these ASMX Web services.
The process to do this is straightforward as you simply create a FoxPro COM object and call it from the Web service methods. The actual Web service class uses .NET code; typically, it only uses a little bit of code to call the FoxPro business logic to generate the result for the Web service methods. .NET manages all the type serialization as well as automatic generation of the service metadata, which is the WSDL definition for the service.
It's easy to create FoxPro COM objects for use in .NET; however, the administrative aspects of going to COM Interop from ASP.NET are tricky as you have to set proper permissions for COM components and any files that need to be accessed. Debugging is also difficult as FoxPro COM components run inside of IIS and can't be easily debugged or shut down. If you're new to COM and dealing with COM in a Web Server environment, this process can be daunting to work with at first. In the end, it's just a mechanical process that you have to remember and follow-there's nothing difficult about the process, it's only tedious.
The big benefit over the SOAP Toolkit is that you get a rich, mature, and efficient Web service framework that makes it fairly easy to create complex Web services.
Windows Communication Foundation
In addition to native .NET Web services, Microsoft recently released .NET Framework 3.0, which includes Windows Communication Foundation (WCF). WCF provides a service-based architecture (SOA) for .NET that, among other things, provides both Web service client and service support. WCF expands on the base Web service functionality by providing extended support for the WS-* extended Web service specifications that provide encryption, authentication, transaction management, binary transports and attachments, and much more.
For plain HTTP-based Web services, ASMX services and the .NET 2.0 Web service client are easier to use than WCF, but WCF provides a unified architecture for creating services for inter-application communication. The same service architecture that can publish and access plain HTTP-based Web services can also work for more high performance protocols like raw TCP/IP, Named Pipes, and Message Queues among others. Essentially by building a service once you can expose the service to a variety of different endpoint protocols with a single code base and even have all of the protocols be accessible at the same time.
The .NET Web services client creates a proxy object that maps the service interface and return values into classes.
A variety of different applications can host WCF. IIS can host WCF using a mechanism similar to ASMX services through ASP.NET. But there's also a new Windows Activation Service (WAS), which is a service that can publish services without having to have IIS running. Any application that can run .NET can also self-host WCF services, which includes Visual FoxPro because it can host WCF services through COM Interop.
WCF is very powerful and given what it accomplishes it's actually relatively easy to use.
Calling Web Services through .NET
For the following examples you should be familiar with the basics of .NET COM Interop. If you're new to creating and calling .NET COM components and calling them from Visual FoxPro, I recommend you read through the following documents:
.NET calls Web services through a generated proxy object that maps methods and message types to classes.
In this first example I'll use a .NET Web service client to call a Web service. This Web service provides a front end to a standard SOAP-based Web service that returns some complex types. The Web service is also a partial, public front end to an e-commerce application that provides access to items and pricing information. You can find the service here:
http://www.west-wind.com/webstoresandbox/service/WebStoreConsumerService.asmx
The service has a few methods that manage inventory information: One method, DownloadInventoryItem, returns a single inventory object while DownloadInventoryItems returns a list of items for a given category and UploadInventoryItem allows uploading and echoing back for an inventory item created on the client. These three methods use complex types as parameters and return values to demonstrate functionality that isn't easily accomplished with the SOAP Toolkit.
So I'll create a Web service client project in .NET that allows you to access the Web service directly from Visual FoxPro. I'll start by creating a new .NET Class Library Project:
![Figure 1: Add a Web service reference by specifying the WSDL
file.](https://codemag.com/Article/Image/0703062/Figure 1 -AddServiceReference.png)
Visual Studio generates a class file that contains a client proxy of the Web service from the Web server. The class file has basically mapped all the methods, parameters, and return types of the Web service to a set of client objects and interfaces. Although I'm calling a .NET Web service in this case, keep in mind that this process works the same with any Web service, whether it's .NET, Java, PHP, or FoxPro.
Behind the scenes WSDL.exe (also available from the command line) parses the service and any dependent message types. WSDL.exe creates a source code file that contains this class that compiles into the .NET client assembly. To use this generated proxy you can now create a small .NET wrapper class (Listing 1) that you can then call from FoxPro via COM Interop.
Listing 1: A COM-accessible wrapper that calls the Web service
namespace WebServiceClient
{
[ClassInterface(ClassInterfaceType.AutoDual)]
[ProgId("WebServiceClient.WebStoreClient")]
public class WebStoreClient
{
public string ErrorMessage
{
get { return _ErrorMessage; }
set { _ErrorMessage = value; }
}
private string _ErrorMessage = "";
public WebStoreService.WebStoreService Proxy
{
get
{
if (this._Proxy == null)
{
this._Proxy = new WebServiceClient.WebStoreService.WebStoreService();
this._Proxy.Timeout = 10000;
}
return _Proxy;
}
set { _Proxy = value; }
}
private WebStoreService.WebStoreService _Proxy = null;
public wws_itemsRow GetInventoryItem(string Sku)
{
wws_itemsRow Item = null;
try
{
Item=this.Proxy.DownloadInventoryItem(Sku);
}
catch (Exception ex)
{
this.ErrorMessage = ex.Message;
}
return Item;
}
public wws_itemsRow[] GetInventoryItems(string Category)
{
wws_itemsRow[] Items = null;
try
{
Items = this.Proxy.DownloadInventoryItems(Category);
}
catch (Exception ex)
{
this.ErrorMessage = ex.Message;
}
return Items;
}
}
}
The code is a very thin wrapper around the WebStoreService proxy class, which Visual Studio and WSDL.exe generated. The class exposes a Proxy property, which is a reference to the actual WebService proxy. This reference is directly accessible over COM, and while you can directly call service methods off this property, I recommend you create explicit wrapper methods, as shown in Listing 1, as this gives you more control to handle errors and return them in a more meaningful way than through COM exceptions. The reference also shields you from possible future proxy implementation changes. Sometimes simple changes in the service can result in the format or naming of proxy classes to change-using a wrapper method allows you to abstract the Web service related logic in one place.
Before you can call the class over COM, you still need to compile the project and register the generated assembly to COM by setting the Register for COM Interop Project option as shown in Figure 2.
![Figure 2: You must register the interop assembly for COM Interop. You set this option in the Project options under the Build tab.](https://codemag.com/Article/Image/0703062/Figure 2 -RegisterForComInterop.png)
You also need to add a couple of assembly-level attributes in the AssemblyInfo.cs
file:
[assembly: ComVisible(true)]
[assembly: ClassInterface(ClassInterfaceType.AutoDual)]
Setting these attributes publishes the types to COM with dual interface semantics so you get IntelliSense on most
objects returned from this assembly. Finally, compile the project. To instantiate the .NET COM object and access any of the proxy methods, you can use the following FoxPro code:
loService = CREATEOBJECT("WebServiceClient.WebStoreClient")
loItem = loService.GetInventoryItem("WWHELP40")
? loItem.Descript
? loItem.Price
You can also retrieve the item by using the proxy directly:
loItem = loService.Proxy.DownloadInventoryItem("WWSTORE20")
Using the proxy directly gives you direct access to any of the methods available on the proxy. As you can see in Figure 3, you even get IntelliSense on the service and most of the result objects like the loItem result object.
![Figure 3: Accessing the Web service COM client in Visual FoxPro provides IntelliSense to service methods and returned result objects.](https://codemag.com/Article/Image/0703062/Figure 3 -IntellisenseFoxPro.png)
Reading from the service is easy, because unlike the SOAP Toolkit that requires XML parsing for this sort of thing, you simply get a COM object returned that you can reference and access properties on. This return of a COM object works even for more complex objects like the result from the GetInventoryItems
method, which returns an array of Inventory items.
loService = CREATEOBJECT("WebServiceClient.WebStoreClient")
loItems = loService.DownloadInventoryItems("")
? loItems[1].Descript
? loItems[1].Price
loItem = loItems[1]
? loItem.Abstract
Most types returned from .NET will just work in FoxPro and you can access objects and arrays. There are some exceptions-specifically complex .NET value types (structures) and some collections can't be marshaled properly over COM, but those exceptions are relatively rare. When you run into result types that aren't directly accessible in FoxPro you can create wrapper classes for these types in .NET, pull out the values from the incompatible types, store them into the wrapper class members, and then return the wrapper class instead.
The example methods above return complex values, which is pretty straightforward, because FoxPro can access the COM objects directly. It gets a little more complicated if you need to pass data to a Web service, because the .NET interface is strongly typed. Take the following method that posts an item to the Web service as an example:
public string UploadInventoryItem(wws_itemsRow Item)
{
string Result = null;
try
{
Result = this.Proxy.UploadInventoryItem(Item);
}
catch (Exception ex)
{
this.ErrorMessage = ex.Message;
}
return Result;
}
The COM object takes a strongly typed inventory item object as a parameter and you can't just create a FoxPro object to map the members of the item object and pass it. Instead the object you pass has to be of the exact type, which means the object has to originate from .NET through COM. The easiest way to accomplish this is to simply instantiate the target type via COM in FoxPro:
*** Create the Item Object through COM
oItem=CREATEOBJECT("WebServiceClient.WebStoreService.wws_ItemsRow")
oItem.Descript = "My New Item"
oItem.Sku="New Item"
oItem.Price=405.00
*** Pass the Item to the Service Wrapper
loService = CREATEOBJECT("WebServiceClient.WebStoreServiceClient")
? loService.UploadInventoryItem(oItem)
The code creates the item as a COM object and then assigns and passes it back to the .NET COM object, which in turn calls the Web service and submits the object. If the type in question is not directly COM accessible in .NET (no COM attributes set on the type), you can use a factory approach instead by creating a method that returns an instance of the class you need to work with.
Although it is possible for FoxPro to pass pure FoxPro objects to .NET via COM, .NET will only be able to access these objects by using the variant object type and effectively using late binding via Reflection, which is inefficient and difficult to code. You should avoid this practice whenever possible though and use .NET COM objects to pass parameters into the .NET COM object.
Publishing a Web Service through .NET
Next I'll publish an ASMX Web service that calls FoxPro code through COM Interop. Although this is a bit more work than using the FoxPro Web Service Wizard (which uses the SOAP Toolkit), this process actually gives you a lot more control over how the Web service is published. ASMX Web services have much richer type support for Web service responses. By using ASMX Web services you get a platform that is a lot more customizable for building your service logic. Because you are responsible for creating each of the service methods on your own, you are dealing with code that you can run a debugger against. The debugger helps you see what's happening inside of your service, detect any errors, and see the server status. ASP.NET is much richer in providing error reporting, diagnostics, and dynamic generation of Web service metadata automatically (the WSDL for the service). There are no more generation steps involved in updating the WSDL; instead it's dynamically generated as needed and directly tied to the service.
Visual FoxPro can access .NET Web service functionality through COM Interop.
I'll start by creating a .NET Web service:
I prefer to create my projects for use with IIS rather than use the built-in Visual Studio Web server because the built-in server switches ports whenever it's restarted. This switching of ports makes it hard to keep the Web service address consistent. The sample project includes a _WebConfiguration utility you can use to quickly create a Virtual directory and set up base permissions for IIS, or you can use the IIS Management Console to do this manually. Make sure your site is accessible before continuing on. Once the site works, go into FoxPro and create a new COM server.
If you're unfamiliar with accessing FoxPro COM servers from .NET I recommend reading the following article, which covers creating COM objects for use with ASP.NET: http://tinyurl.com/ykd7jc
The article describes ASP.NET operation and .NET Web services essentially based on ASP.NET. I'll create a FoxPro class that will be exposed to COM and includes a few housekeeping features as shown in Listing 2.
Listing 2: A sample FoxPro COM server to be accessed from a Web service
#DEFINE CRLF CHR(13) + CHR(10)
DEFINE CLASS FoxServer as SESSION OLEPUBLIC
cAppStartPath = ""
lError = .F.
cErrorMsg = ""
******************************************************
* FoxServer :: Init
******************************************************
FUNCTION INIT
SET RESOURCE OFF && Best to compile into a CONFIG.FPW
SET EXCLUSIVE OFF
SET REPROCESS TO 2 SECONDS
SET CPDIALOG OFF
SET DELETED ON
SET EXACT OFF
SET SAFETY OFF
*** IMPORTANT: Figure out your DLL startup path
IF Application.Startmode = 3 OR
Application.StartMode = 5
THIS.cAppStartPath = ADDBS(JUSTPATH(Application.ServerName))
ELSE
THIS.cAppStartPath = SYS(5) + ADDBS(CURDIR())
ENDIF
SET PATH TO (THIS.cAppStartpath) ADDITIVE
ENDFUNC
******************************************************
* FoxServer :: GetRequestInfo
******************************************************
Function GetRequestInfo() as String
DECLARE Integer GetUserName IN WIN32API AS GUserName STRING @nBuffer, INTEGER @nBufferSize
lcUserName=space(255)
lnLength=len(lcUserName)
lnError=GUserName(@lcUserName,@lnLength)
lcUserName = SUBSTR(lcUsername,1,lnLength-1)
lcPath = SYS(5) + CURDIR()
lcOutput = "Current Path: " + lcPath + CRLF + " DLL Path: " + this.cAppStartPath + CRLF + "Current User: " + lcUserName + " - " + SYS(0) + CRLF + "Current Thread: " + TRANSFORM(Application.ThreadId)+CRLF
RETURN lcOutput
ENDFUNC
******************************************************
* FoxServer :: Error
******************************************************
PROTECTED FUNCTION ERROR
LPARAMETER nError, cMethod, nLine
this.lError = .t.
THIS.cErrorMsg=THIS.cErrorMsg + CRLF +
"Error No: " + STR(nError) + CRLF +;
" Method: " + cMethod + CRLF +;
" LineNo: " +STR(nLine) + CRLF + ;
" Message: "+ message() + Message(1) + CRLF + CRLF
ENDFUNC
ENDDEFINE
The server is created as Session to limit the public properties exposed to COM, and it includes some initialization code to determine the startup path and set the environment. I also prefer to capture errors in the server itself and echo the output back or access it while debugging vs. firing a COM exception.
For testing, the GetRequestInfo()
method serves as a HelloWorld
method that returns some status information about the COM server, which is useful for debugging purposes. To create the server, create a project called FoxService and compile it into a COM object with:
BUILD MTDLL foxservice FROM foxservice
RECOMPILE
Test it from the FoxPro Command window with:
o=CREATEOBJECT("FoxService.FoxServer")
? o.GetRequestInfo()
If all of that works, go back into Visual Studio. The next step is to import the COM object into .NET:
![Figure 4: Adding a reference to the FoxPro COM server creates a .NET interop assembly that wraps the COM object with a .NET class.](https://codemag.com/Article/Image/0703062/Figure 4 -AddComReference.png)
This procedure creates a .NET wrapper object for the FoxService COM object that is accessible in .NET code and which allows you to access the object in the Web service. Visual Studio creates a separate assembly, Interop.FoxService.dll, that contains .NET types for each of the COM types exposed in the type library. Next, I'll create the actual Web service class in .NET.
Open the generated Web service class and replace the code in the service with the code shown in Listing 3.
Listing 3: A basic C# Web service that calls the FoxPro COM object
[WebService]
public class FoxWebService : System.Web.Services.WebService
{
[WebMethod]
public string FoxServerStatus()
{
foxservice.FoxServer Service = new foxservice.FoxServer();
string Result = Service.GetRequestInfo(false);
return Result;
}
[WebMethod]
public DateTime GetServerTime()
{
return DateTime.Now;
}
[WebMethod]
public string ThreadingMode()
{
return Thread.CurrentThread.ApartmentState.ToString();
}
}
A .NET Web service is basically a class that is marked up with a few attributes that identify the class and methods that are to be exposed through the Web service. For now the FoxServerStatus
method is the only one that accesses the FoxPro COM object. Notice that I can simply create an instance of the FoxServer
class and then use that class like a stock .NET class. I get strong typing based on the types I specified in the FoxPro server and IntelliSense shows the methods available on the server. In this case I simply return the result from the GetRequestInfo()
method call.
Next I'll test the server. Right-click the ASMX
file in the Solution Explorer and select View in Browser. You'll see a Web service sample page that looks like Figure 5.
![Figure 5: ASMX Web services let you test Web services interactively through a Web page. ](https://codemag.com/Article/Image/0703062/Figure 5 - WebServiceWebPage.png)
You can click any of the links of the test page and check to see if the service works. The test page runs your service method and displays the result as an XML document. It isn't pretty, but it's quite useful for quickly checking operation of the service.
Calling the Web Service
I'll call the service using the same mechanism I used earlier in the article-by adding a new wrapper COM class to the WebServiceClient project that I created earlier. I'll simply add another Web Reference and create another COM wrapper class for this service. I'll start by importing the Web service with this URL through Add Web Reference: http://localhost/foxWebService/FoxWebService.asmx?WSDL.
This URL points at the dynamically generated WSDL for the Web service; you can import and generate the new reference from it. Once the Web Reference exists, you can use it to build a wrapper class that is exposed to COM as shown in Listing 4.
Publishing complex Visual FoxPro objects to Web services typically requires creation of .NET wrapper types.
Listing 4: The client wrapper for the FoxWebService for COM Interop
[ClassInterface(ClassInterfaceType.AutoDual)]
[ProgId("WebServiceClient.FoxWebServiceClient")]
public class FoxWebServiceClient
{
public string ErrorMessage
{
get { return _ErrorMessage; }
set { _ErrorMessage = value; }
}
private string _ErrorMessage = "";
public FoxWebService.FoxWebService Proxy
{
get
{
if (this._Proxy == null)
{
this._Proxy = new FoxWebService.FoxWebService();
this._Proxy.Timeout = 10000;
}
return _Proxy;
}
set { _Proxy = value; }
}
private FoxWebService.FoxWebService _Proxy = null;
public string FoxServerStatus()
{
string Result = "";
try
{
Result = this.Proxy.FoxServerStatus();
}
catch (Exception ex)
{
this.ErrorMessage = ex.Message;
}
return Result;
}
public DateTime GetServerTime()
{
return this.Proxy.GetServerTime();
}
}
Now just compile the code and then go into Visual FoxPro to try it out using this code:
O = CREATEOBJECT("WebServiceCLient.FoxWebServiceClient")
? o.FoxServerStatus()
? o.GetServerTime()
? o.Proxy.ThreadingMode()
This code uses FoxPro code to call a .NET COM Interop component, which in turn calls a .NET Web service, which in turn calls a FoxPro COM component, and then returns this result back to the caller. Congratulations. You've come full circle creating a Web service and calling it with Visual FoxPro on both ends.
Complex Objects and COM Interop
The .NET Web service is quite capable of returning complex objects. Returning a complex object is as simple as returning the type as a result of the method. As long as the type is XML serializable in .NET, the type will be automatically serialized into XML; the ASMX service automatically provides the required schema metadata to generate a WSDL file.
COM Interop gives FoxPro access to strongly typed objects returned from Web services.
You can also publish COM objects to the Web service to some degree. There are some limitations but I'll start with an example that works. Create a class in the FoxService.prg
file and publish it through COM as shown in Listing 5.
Listing 5: A complex FoxPro class
DEFINE CLASS Customer as Session OlePublic
cName = "Rick Strahl"
cCompany = "West Wind Technologies"
cAddress = "32 Kaiea Place"
tEntered = DATETIME()
DIMENSION cName_COMATTRIB[4]
cName_COMATTRIB[1] = 0 && Full Access
cName_COMATTRIB[2] = "Customer Name"
cName_COMATTRIB[3] = "cName" && Proper capitalization.
cName_COMATTRIB[4] = "string" && Data type
... more COMATTRIB definitions for each prop
ENDDEFINE
Note the use of the _COMATTRIB array to enforce explicit type names in the generated type library. Rebuild the COM object (BUILD MTDLL FoxService from FoxService RECOMPILE after you've stopped and restarted the IIS service). Now that you've made a change to the COM type library you'll need to reload the COM reference-remove the reference, and then add it back into the project (note this has changed in Visual Studio 2005, which no longer autorefreshes COM references-see the sidebar, COM Object in ASP.NET). Next create a method in the Web service to expose the Customer
object:
[WebMethod]
public foxservice.CustomerClass GetCustomer()
{
foxservice.CustomerClass Customer = new foxservice.CustomerClass();
return Customer;
}
You can now use the Web service test page to access this method; you should see the object returned when you run the ASMX test page through the browser.
Go back into the .NET WebServiceClient project, Update Web Reference on the FoxWebService, and then rebuild the project. I'm going to cheat here and not create a wrapper method in the class. Instead, I'll use the Proxy directly from FoxPro so I have nothing else to do but to instantiate the object:
loService = CREATEOBJECT("WebServiceClient.FoxWebServiceClient")
loCust = loService.Proxy.GetCustomer()
? loCust.cCompany
? loCust.tEntered
You've just accessed a Web service that publishes a FoxPro object. That's cool, but unfortunately this only works as long as your FoxPro object is a single hierarchy deep. As soon as you have nested objects on the published object, serialization in .NET no longer works because .NET knows nothing about the generic types. For example, add an object property like the following to the Customer
class in the FoxService.prg
file:
oPhoneNumbers = null
FUNCTION INIT
*** Note: Create as COM object so we
*** can cast in .NET
This.oPhoneNumbers=CREATEOBJECT("FoxService.PhoneNumbers")
Add the PhoneNumbers
class:
DEFINE CLASS PhoneNumbers as Session OLEPUBLIC
cTelephone = "808 123-1231"
cFax = "808 332-1231"
cMobile = "808 312-1231"
... COMAttrib definitions
ENDDEFINE
Stop and restart IIS, recompile the COM object and reimport the COM reference in the .NET Web service project, and then recompile the full solution. If you now return this object as a result type (FoxService.Customer), the Web service actually fails to compile. .NET is trying to create a serialization assembly for the Web service class and can't because the type information on the child PhoneNumbers
object is not published by FoxPro's COM compilation.
So what's the alternative if you need to publish complex types as part for your Web service? You can create a .NET wrapper type, use that type as a return value, and then assign the COM object's values to this wrapper type. While this seems like a lot of extra work, it's still a lot easier than doing XML parsing and type conversion. Start by creating a wrapper type that matches the FoxPro type:
[Serializable]
public class cCustomer
{
public string cName = "";
public string cCompany = "";
public string cAddress = "";
public DateTime tEntered = DateTime.Now;
public cPhoneNumbersoPhoneNumbers = new cPhoneNumbers();
}
[Serializable]
public class cPhoneNumbers
{
public string cTelephone = "";
public string cMobile = "";
public string cFax = "";
}
I'm using the same field names as the FoxPro class but that's not really required-the names are up to you. This type can now be used for parameters or return values and be returned from a method like this:
[WebMethod]
public cCustomer GetCustomer()
{
foxservice.CustomerClass Customer = new foxservice.CustomerClass();
cCustomer Cust = new cCustomer();
Cust.cName = Customer.cName;
Cust.cCompany = Customer.cCompany;
Cust.tEntered = (DateTime)Customer.tEntered;
// *** Cast Phone to COM type foxservice.PhoneNumbers
PhoneNumbers = Customer.oPhoneNumbers as foxservice.PhoneNumbers;
cPhoneNumbers Phone = Cust.oPhoneNumbers;
Phone.cTelephone = PhoneNumbers.cTelephone;
return Cust;
}
This approach allows for maximum flexibility as you can now take advantage of the full range of types that .NET can publish to Web services.
ASMX Threading Models
If you look back at the code in Listing 3, I included a method in the Web service class to return the threading mode of the Web service:
return Thread.CurrentThread.ApartmentState.ToString();
If you run this method in a Web service, you will get a value of MTA (MultiThreaded Apartment) returned, which means it's running in free-threaded mode. This MTA is a problem for Visual FoxPro COM objects, which require Single Thread Apartment (STA) threads in order to run reliably. Unlike ASP.NET pages, ASMX Web services do not support an ASPCOMPAT mode, which can automatically switch the service into STA mode. This inability can cause big performance and stability issues for FoxPro COM objects called off ASMX pages.
There are two ways to work around this issue. One is to use COM+ to host your COM components. COM+ manages the thread marshalling and makes it possible to handle VFP COM objects in an MTA environment; however, COM+ can be a little inconvenient to deal with during development and it incurs significant overhead for COM object access.
The other mechanism is to use the ASP.NET page handler to provide the ASPCOMPAT compatibility for your ASMX Web services. You can accomplish this by creating a custom HTTP Handler, which I have provided in the WebServiceStaHandler.cs
class. You hook up the handler needs in the web.config
file like this:
<configuration>
<system.web>
<httpHandlers>
<add verb="*" path="FoxWebService.asmx" type="WebServiceStaHandler" />
</httpHandlers>
</system.web>
</configuration>
With this handler enabled the ThreadingMode Web service method now returns STA mode, which is compatible with FoxPro COM objects. For more info on this issue see http://tinyurl.com/y9vq32.
Web Services through Windows Communication Foundation
Microsoft recently released the .NET Framework 3.0 and WCF is a key component of this release.
Publishing of Visual FoxPro code Web services through .NET requires COM Interop.
Let me show you how you can provide similar functionality with WCF to accomplish the task I showed above with ASMX and the stock .NET proxy. I'll create a new WCF service that mimics the functionality I created earlier for the ASMX service.
In order to follow along make sure that you have installed the .NET Framework 3.0 (http://www.microsoft.com/downloads/details.aspx?FamilyId=10CC340B-F857-4A14-83F5-25634C3BF043&displaylang=en) and the Visual Studio 2005 Extensions for the .NET 3.0 Framework (http://www.microsoft.com/downloads/details.aspx?FamilyId=F54F5537-CC86-4BF5-AE44-F5A1E805680D&displaylang=en).
Creating a WCF Web Service in ASP.NET
The process of creating a WCF Web service is not very different from the process with ASMX-you can simply add a WCF service to an ASP.NET application. To do so, start with the FoxWebService project you used earlier and follow these steps:
This procedure creates a .svc
file in your Web project with a code-behind link to a source file that contains a service contract interface and class. The SVC file looks like this:
<%@ ServiceHost Language="C#" Debug="true" CodeBehind="~/App_Code/FoxWcfService.cs" Service="FoxWcfService" %>
IIS maps the .svc extension and routes the call to the code-behind class file. This file implements an interface that acts as a service contract and an implementation class that provides the logic. I'll use the same service logic used in the ASMX server I used to call the FoxPro COM server I created earlier. Listing 6 shows the ServiceContract interface and the implementation class.
Listing 6: A WCF-based Web service calling a Fox server
[ServiceContract]
{
[OperationContract]
string FoxServerStatus();
[OperationContract]
cCustomer GetCustomer();
}
public class FoxWcfService : IFoxWcfService
{
public string FoxServerStatus()
{
foxservice.FoxServer Service = new foxservice.FoxServer();
return Service.GetRequestInfo(false);
}
public cCustomer GetCustomer()
{
foxservice.CustomerClass Customer = new foxservice.CustomerClass();
cCustomer Cust = new cCustomer();
Cust.cName = Customer.cName;
Cust.cCompany = Customer.cCompany;
Cust.cAddress = Customer.cAddress;
Cust.tEntered = (DateTime)Customer.tEntered;
foxservice.PhoneNumbers PhoneNumbers = Customer.oPhoneNumbers as foxservice.PhoneNumbers;
foxservice.Iphonenumbers PhoneNumbersx = Customer.oPhoneNumbers as foxservice.Iphonenumbers;
cPhoneNumbers Phone = Cust.oPhoneNumbers;
Phone.cTelephone = PhoneNumbers.cTelephone;
Phone.cMobile = PhoneNumbers.cMobile;
Phone.cFax = PhoneNumbers.cFax;
return Cust;
}
}
The main difference in the WCF implementation is that the service contract and implementation are separated into an interface and implementation class. WCF uses ServiceContracts to map service endpoints and looks in a compiled assembly for an implementation of the ServiceContract interface. In this case there's little benefit to this separation but the separation is a more formal SOA approach where the interface should be modeled before any implementation is done. In addition, WCF also allows setting up of explicit DataContract
objects, which define how to format and map messages to underlying objects. DataContracts are optional though and it's not required here since you can use WCF's default serialization semantics to return our results (triggered by the [Serializable]
attribute on the cCustomer
class).
The GetCustomer()
method returns a complex object that is mapped from a FoxPro object returned over COM. If you recall, I created a couple .NET wrapper types for this FoxPro object so it can be properly serialized. The same approach works here.
In addition to the service implementation, WCF also requires some configuration settings in a .config
file (actually you can also do this programmatically, but only if you self-host, not inside of ASP.NET). These settings tell WCF which endpoints to listen on and what protocols to serve on those endpoints as shown in Listing 7.
Listing 7: WCF web.config settings are required to configure a service endpoint
<configuration>
<system.serviceModel>
<services>
<service name="FoxWcfService" behaviorConfiguration="FoxWcfServiceBehaviors">
<endpoint contract="IFoxWcfService" binding="basicHttpBinding"/>
<endpoint contract="IFoxWcfService" binding="wsHttpBinding" address="ws" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="FoxWcfServiceBehaviors" >
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceMetadata httpGetEnabled="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
A service is mapped by its full type name-FoxWcfService in this case. Each service has one or more endpoints and the endpoint is mapped to a specific service contract interface (IFoxWcfService). The endpoint is associated with a binding that determines the protocol and other connection-related settings. Here I'm using the BasicHttp binding, which is the SOAP 1.1/1.2 format over HTTP or HTTPS. The beauty of WCF is that you can also use other bindings. For example, I added an WsHttpBinding which the service handles with the WS-* related SOAP protocols. IIS is limited to serving HTTP protocols, but a WCF service in other host containers can also service plain TCP/IP, Named Pipes, and MSMQ using a single service interface.
There's also a “Mex” endpoint-this endpoint provides the MetaDataExchange, which is required in order for the service to publish its WSDL for discovery. WCF always uses the SOAP and WSDL regardless of connection protocol, but you must explicitly specify the Mex endpoint in order to access the WSDL.
ASMX Web services run in MTA threading mode, which can cause problems for FoxPro STA components.
At this point the service is configured and you can use a browser to navigate to the following URL to see if the service is active:
http://localhost/FoxWebService/foxwcfservice.svc
Like ASMX, a WCF service has a service HTML page that describes the service along with a link to the WSDL. There's no test page though, so you can't test the service as easily as an ASMX service. The service is published using standard SOAP 1.2 and WSDL, so this service is directly accessible from any Web service client.
Calling a Web Service with WCF
Since you can't interactively test the service, I'll call it with a WCF client. I'll add another class to the WebServiceClient .NET project, add a WCF Service Proxy, and then expose that object via COM to FoxPro. First add a new Service Reference to the project WebServiceClient project:
Visual Studio creates a service reference for you, which consists of a generated source file that contains a generated service contract, a proxy class, and all the message (parameter and return value) objects. It also creates an app.config
file with client configuration settings. I'm going to skip over the config file here (see sidebar, WCF Configuration Files in FoxPro) and configure the client completely via code. With the generated proxy in place, you can now create a wrapper class for COM Interop (Listing 8).
Listing 8: A WCF client wrapper class for use with COM Interop
[ClassInterface(ClassInterfaceType.AutoDual)]
[ProgId("WebServiceClient.FoxWcfServiceClient")]
public class FoxWcfServiceClient
{
public string ErrorMessage
{
get { return _ErrorMessage; }
set { _ErrorMessage = value; }
}
private string _ErrorMessage = "";
public FoxWcfService.FoxWcfServiceClient Proxy
{
get
{
if (this._Proxy == null)
this.SetProxy(null);
return _Proxy;
}
set { _Proxy = value; }
}
private FoxWcfService.FoxWcfServiceClient _Proxy = null;
public void SetProxy(string EndPointUrl, bool UseWs)
{
Binding binding = null;
if (!UseWs)
{
if (string.IsNullOrEmpty(EndPointUrl))
EndPointUrl = "http://rasvista/FoxWebService/FoxWcfService.svc";
binding = new BasicHttpBinding();
}
else
{
if (string.IsNullOrEmpty(EndPointUrl))
EndPointUrl = "http://rasvista/FoxWebService/FoxWcfService.svc/ws";
binding = new WSHttpBinding();
}
binding.ReceiveTimeout = TimeSpan.FromSeconds(30F);
this._Proxy = new FoxWcfService.FoxWcfServiceClient(binding, new EndpointAddress(EndPointUrl));
}
public string FoxServerStatus()
{
string Result = "";
try
{
Result = this.Proxy.FoxServerStatus();
}
catch (Exception ex)
{
this.ErrorMessage = ex.Message;
}
return Result;
}
public cCustomer GetCustomer()
{
cCustomer Customer = null;
Customer = this.Proxy.GetCustomer();
return Customer;
}
}
This code is similar to the ASMX client code that I wrote earlier. What's different is the client proxy property configuration, which is a little more complex as you have to specify the endpoint and binding to use explicitly. WCF provides much more configurability than the ASMX client did with most of the settings on the Binding
object. You can configure authentication security with the Proxy.ClientCredential
property. Although I won't show it here, you can also set most of these settings in a .config
file for the application.
Since I previously configured the WebServiceClient project, all I have to do is recompile the project to generate the .NET COM assembly that includes this class. I can now use the following code in Visual FoxPro:
loService=CREATEOBJECT("WebServiceClient.FoxWcfServiceClient")
Cust = loService.GetCustomer()
? Cust.cCompany
? Cust.tEntered
? Cust.oPhoneNumbers
? Cust.oPhoneNumbers.cFax
It looks identical to the ASMX client code that I used previously and it works roughly the same. Functionally there's very little difference between this service and the ASMX service, although from my limited tests with WCF services so far it feels like WCF is much faster accessing Web services-especially for the first time.
The real value of WCF on the server and client though is that you can switch protocols with very little effort. For example, if you decide that you want to support the WS-* SOAP messaging over wsHttpBinding, all you have to do is create another endpoint for the service on the service end in web.config. You can then add the endpoint into the web.config service configuration (shown previously in Listing 7) like this:
<service name="FoxWcfService">
<endpoint contract="IFoxWcfService" binding="wsHttpBinding" address="ws" />
<endpoint contract="IFoxWcfService" binding="basicHttpBinding" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
You now have a service that can communicate with multiple protocols-all that changes is the endpoint address for the Web service call with a relative path of “ws”. Both bindings are listed in the WSDL so the WSDL URL stays the same and you can switch between them at will. On the client the change is even easier-you need to re-import the Service Reference to make the new binding available on the client. Then you can use the code in the SetProxy
method in Listing 8 to switch between the two bindings.
COM Interop gives FoxPro access to strongly typed objects returned from Web services.
With this code you can switch between the two protocols with a simple logical parameter change. Now that's pretty powerful.
Choosing between ASMX and WCF
There's a lot of overlap between ASMX and the stock .NET 2.0 Web service client and WCF-based services and clients. It's a valid question to ask which of the two approaches to use.
If your goal is to use pure HTTP-based standard SOAP 1.x Web services and you don't foresee a need to support WS-* style service, I recommend you stick with ASMX Web services and the stock .NET 2.0 proxy. These “old” mechanisms are easier to use. Even if you use them now and you later decide to move to WCF, it's relatively straightforward to upgrade existing services and clients.
WCF is more powerful, more flexible, and more configurable than ASMX/stock Web service Proxies; however, it requires more configuration and because of that requirement is more complex to build and use. WCF exposes a lot of extra power for that extra complexity though, and while it is only slightly more difficult to use for basic functionality, it takes some time to get familiar with the multitude of configuration settings you can tweak.
The real value of WCF services comes in non- Web-based applications. Today we often think of anything that requires a distributed architecture as requiring Web/HTTP-based connectivity. Other mechanisms exist but they have been notoriously difficult to implement. WCF changes all that by bringing the Web service model to various different protocols. If you need to build two or more applications that need to communicate with each other and that can use WCF, WCF offers many advantages. For example, WCF offers the ability to utilize optimized binary data, which performs much better than the more verbose XML text data. You can use complex authentication and session and transaction management, which allow creating complex distributed applications with relative ease. WCF solves many complex problems in a consistent and reusable fashion. Before WCF you had to build these sorts of architectures and protocols on your own from scratch.
Although I don't show it in this article, WCF also allows hosting of services in other non-IIS service containers. It's possible to host WCF services in a new auto-activating WAS service that provides full support for all of the protocols that WCF offers (unlike IIS which only supports HTTP protocols). And you can even host WCF in your own applications including your FoxPro applications, which can host WCF via .NET COM Interop code that you create to host a WCF service. The beauty is that WCF manages all the complex protocol navigation, thread management, WSDL publishing and message cracking so your code implementation can focus on the application logic.
Because WCF is an abstraction-layer technology it is also forward-looking technology. The framework is open and extensible and is capable of growing as new protocols and technologies become available, as they likely are going to be in the future. Moving forward developers will be moving away from ASMX Web services and .NET 2.0 Web service clients towards using WCF exclusively. The tools to do this are likely to get more user friendly as well.
I think WCF strikes a great balance between complexity, ease of use, and configurability. You get a lot of functionality for very little additional complexity over stock .NET Web service functionality.
Summary
Web services communication with FoxPro has never been easy and this article probably hasn't dispelled that notion for you. What I've shown here involves multiple technologies that must be bridged and interoperate with each other in order to utilize .NET's Web service functionality. If you're not familiar with .NET and doing FoxPro to .NET COM Interop, much of this content may seem very complex.
But running on a non-.NET platform FoxPro developers don't have much of a choice for a quality Web services platform. By interoperating with .NET, FoxPro developers gain access to a truly full featured Web services platform that the SOAP Toolkit never really provided. Both plain and WCF-based Web services allow access to a full featured and fully supported Web services and SOA platform for FoxPro developers to take advantage of.
WCF is more powerful than ASMX but it's also slightly more complex to implement and work with.
If you have any questions, comments, or just would like to discuss the content of this article please visit: http://www.west-wind.com/wwThreads/default.asp?Forum=White+Papers and post your questions there.