WCF Service示例

两个月前,写了篇随笔《XML Web Service示例》,当时是基于.NET 2.0实现的,后来有位网友说现在都用WCF了,于是初步学习了一下WCF,将这个示例在.NET 3.5下用WCF实现了一下,现在再写篇随笔,以记录实现的过程。

 

服务示例的目的没变,还是根据客户程序的请求返回一幅图像。不过这次改在Windows 7 64位家庭高级版和IIS 7.5上实现,开发环境仍是VS2008

 

1.       创建WCF Service Library

 

打开VS2008,选择File/New/Project菜单项,在打开的New Project对话框中,依次选择Visual C# -> WCF -> WCF Service Library,然后输入项目名称(Name),存放位置(Location)和解决方案名称(Solution Name),点击“OK”生成项目。此例中我们用AnnotationWCFService作为项目和解决方案的名称(见下图)。

 

image

 

2.       用右键菜单中的“Rename”将缺省生成的IService1.csService1.cs改为IImageService.csImageService.cs(见下图)。在改名过程中,系统会询问是否也同时修改代码中相应的名字,选择“yes”。另外,编辑App.config,将所有的Service1替换为ImageService

 

image

 

3.       WCF Service编码

 

修改IImageService.cs的代码如下:

 

using System.ServiceModel;

 

namespace AnnotationWCFService

{

    [ServiceContract]

    public interface IImageService

    {

        [OperationContract]

        byte[] GetImage(string imageFileName);

    }

}

 

修改ImageService.cs的代码如下:

 

using System.IO;

using System.ServiceModel;

using System.Web.Services.Protocols;

 

namespace AnnotationWCFService

{

    [ServiceBehavior(IncludeExceptionDetailInFaults=true)]

    public class ImageService : IImageService

    {

        public byte[] GetImage(string imageFileName)

        {

            byte[] imageArray = GetBinaryFile(imageFileName);

            if (imageArray.Length < 2)

            {

                throw new SoapException("Could not open image on server.", SoapException.ServerFaultCode);

            }

            else

            {

                return imageArray;

            }

        }

 

        private byte[] GetBinaryFile(string fileName)

        {

            string fullPathFileName = System.AppDomain.CurrentDomain.BaseDirectory + fileName;

            if (File.Exists(fullPathFileName))

            {

                try

                {

                    FileStream fileStream = File.OpenRead(fullPathFileName);

                    return ConvertStreamToByteBuffer(fileStream);

                }

                catch

                {

                    return new byte[0];

                }

            }

            else

            {

                return new byte[0];

            }

        }

 

        public byte[] ConvertStreamToByteBuffer(Stream imageStream)

        {

            int imageByte;

            MemoryStream tempStream = new MemoryStream();

            while ((imageByte = imageStream.ReadByte()) != -1)

            {

                tempStream.WriteByte((byte)imageByte);

            }

            return tempStream.ToArray();

        }

    }

}

 

这里需要注意一下:如果获取文件失败,程序抛出一个SoapException。但是,缺省地,WCF不会暴露异常的细节给客户端,所以客户端要想能获得异常的细节,需要在此再做两件事。

 

首先,将App.configserviceDebug节点的参数includeExceptionDetailInFaults设置改为True,如下:

 

          <serviceDebug includeExceptionDetailInFaults="True" />

 

其次,如上面代码,在class ImageService声明前加ServiceBehavior特性,即:

 

    [ServiceBehavior(IncludeExceptionDetailInFaults=true)]

 

4.       增加svc文件

 

在工程所在路径下(本例为D:\Solutions\AnnotationWCFService\AnnotationWCFService)生成一个ImageService.svc文件,目的是让WCF服务能够寄宿在IIS上。ImageService.svc文件内容如下(与XML Web ServiceASMX文件格式相似):

 

<%@ ServiceHost Service="AnnotationWCFService.ImageService" %>

 

实际上,服务实现代码可以直接内嵌在svc文件中。但本例中没有这么做,这样,WCF就会到svc所在目录的bin子目录下查找程序集来运行服务。但是工程的输出目录缺省为bin\Debug\,所以需要修改一下,否则系统将找不到服务。在Solution Explorer中,右键点击工程AnnotationWCFService,选“Properties”,在属性页中点“Build”标签,将Output Path改为bin\,如下图:

 

image

 

5.       IIS中添加应用程序

 

打开“Internet信息服务(IIS)管理器”,右键点击Default Web Site,在弹出的菜单上选择“添加应用程序”,在打开的对话框中输入别名和物理路径(见下图),此例中我们输入AnnotationWCFService以及ImageService.svc所在物理路径。

 

image      image

 

此时,如果通过浏览器访问http://localhost/AnnotationWCFService/ImageService.svc,会出现以下内容,说明服务创建成功。但还不够,我们还需要在客户程序中成功调用这个服务。

 

image

 

6.       在客户程序中为WCF Service创建代理

 

VS2008中,打开一个Windows应用程序解决方案(.sln),此例中我们打开一个叫做AnnotationApp的解决方案。在要调用WCF Service的项目上(比如此例中我们选择用DataLib)点击右键,选择Add Service Reference菜单项。”),在弹出的Add Service Reference对话框中,输入我们要调用的WCF Service的地址,此例中我们输入:

 

http://localhost/AnnotationWCFService/ImageService.svc

 

然后点击“Go”,ImageService就会显示在下面的Services框里,在Namespace编辑框输入服务引用的命名空间名,为了避免再用ImageService这个名字,这里我们输入WCFImageService(见下图),然后点击“OK”来添加服务引用。

 

image

 

这会在Solution Explorer中增加一个Service Reference(见下图)。

 

image

 

添加的引用是Image Service的代理代码,其中包括一个ImageServiceClient的类,派生于System.ServiceModel.ClientBase<DataLib.WCFImageService.IImageService>,并实现DataLib.WCFImageService.IImageService接口。这样在客户代码中就可以像调用自己的Assembly里的方法一样调用ImageServiceClientGetImage方法。

 

7.       客户程序调用WCF Service

 

在客户程序中需要调取图像的地方增加如下代码,之后使用Bitmap对象可以将图像显示出来:

 

            ImageServiceClient client = null;

            Bitmap bitmap = null;

            try

            {

                client = new ImageServiceClient();

                byte[] imageBuffer = client.GetImage(imageFileName);

                MemoryStream memoryStream = new MemoryStream(imageBuffer);

                bitmap = new Bitmap(memoryStream);

            }

            catch (FaultException e)

            {

                Console.WriteLine("Fault exception - {0}", e.Message);

            }

            catch (InvalidOperationException e)

            {

                Console.WriteLine("Invalid operation exception - {0}", e.Message);

            }

            catch (CommunicationException e)

            {

                Console.WriteLine("Communication exception - {0}", e.Message);

            }

            catch (Exception e)

            {

                Console.WriteLine("Unexpected exception - {0}", e.Message);

            }

            finally

            {

                if (client != null)

                {

                    client.Close();

                }

            }

 

这时候会捕获InvalidOperationException,异常的消息是:

 

ServiceModel 客户端配置部分中,找不到引用协定“WCFImageService.IImageService”的默认终结点元素。这可能是因为未找到应用程序的配置文件,或者是因为客户端元素中找不到与此协定匹配的终结点元素。

 

造成这个问题的原因是当添加服务引用时,WCF只是更新了DataLib工程的App.config文件,作为客户端程序的AnnotationApp工程的App.config并没有更新,所以程序无法获得服务的配置信息。消除这个问题需要将DataLibApp.config文件中system.serviceModel节点及其所有子节点复制到AnnotationAppApp.configConfiguration节点中。

 

之后仍会捕获CommunicationException,异常的消息是:

 

反序列化操作“GetImage”的响应消息的正文时出现错误。读取 XML 数据时,超出最大数组长度配额(16384)。通过更改在创建 XML 读取器时所使用的 XmlDictionaryReaderQuotas 对象的 MaxArrayLength 属性,可增加此配额。 1 行,位置为 29931

 

造成这个问题的原因是WCF生成配置文件时,最大数据长度缺省设置为16384,因为我们需要获取的图像文件大于这个值。消除这个问题需要将App.config中的readerQuotas节点的maxArrayLength参数从16384改为合适的值,这里我们改成65536

 

如果服务端读取图像文件失败抛出异常,在客户端会捕获FaultException。所以代码中还catchFaultException

 

8.       运行客户程序来测试WCF Service调用

 

编译运行客户程序并运行,如果一切正常,WCF Service被成功调用并返回所调用的图像(见下图)。

 

image

 

本文参考了Steve Resnick等编著的《Essential WCF》(WCF核心技术)。

posted @ 2011-06-10 13:33  wanghui  阅读(4013)  评论(5编辑  收藏  举报