第一篇:java 调用webservice的各种方法总结
一、利用jdk web服务api实现,这里使用基于 SOAP message 的 Web 服务
1.首先建立一个Web services EndPoint:
Java代码
package Hello;
import javax.jws.WebService;
import javax.jws.WebMethod;
import javax.xml.ws.Endpoint;
@WebService
public class Hello {
@WebMethod
public String hello(String name){
return “Hello, ” + name + “n”;}
public static void main(String[] args){
// create and publish an endpoint
Hello hello = new Hello();
Endpoint endpoint Endpoint.publish(“http://localhost:8080/hello”, hello);
} }
=
2.使用 apt 编译 Hello.java(例:apt-d [存放编译后的文件目录] Hello.java),会生成 jaws目录
3.使用java Hello.Hello运行,然后将浏览器指向http://localhost:8080/hello?wsdl就会出现下列显示
4.使用wsimport 生成客户端
使用如下:wsimport-p.-keep http://localhost:8080/hello?wsdl
5.客户端程序:
Java代码
1.class HelloClient{ 2.3.public static void main(String args[]){ 4.5.HelloService service = new HelloService();6.7.Hello helloProxy = service.getHelloPort();8.9.String hello = helloProxy.hello(“你好”);10.11.System.out.println(hello);12.13.} 14.15.} 16.二、使用xfire,我这里使用的是myeclipse集成的xfire进行测试的
利用xfire开发WebService,可以有三种方法:
1一种是从javabean 中生成;
一种是从wsdl文件中生成;
还有一种是自己建立webservice
步骤如下:
用myeclipse建立webservice工程,目录结构如下:
首先建立webservice接口,代码如下:
Java代码
1.package com.myeclipse.wsExample;2.3.//Generated by MyEclipse 4.5.6.7.public interface IHelloWorldService { 8.9.10.11.public String example(String message);12.13.14.15.} 16.Java代码
1.package com.myeclipse.wsExample;2.3.//Generated by MyEclipse 4.5.6.7.public class HelloWorldServiceImpl implements IHelloWorldService { 8.9.10.11.public String example(String message){ 12.13.return message;14.15.} 16.17.18.19.} 20.修改service.xml 文件,加入以下代码:
Xml代码
1.2.3.
客户端实现如下:
Java代码
1.package com.myeclipse.wsExample.client;2.3.import java.net.MalformedURLException;4.5.import java.net.URL;6.7.8.9.import org.codehaus.xfire.XFireFactory;10.11.import org.codehaus.xfire.client.Client;12.13.import org.codehaus.xfire.client.XFireProxyFactory;14.15.import org.codehaus.xfire.service.Service;16.17.import org.codehaus.xfire.service.binding.ObjectServiceFactory;18.19.20.21.import com.myeclipse.wsExample.IHelloWorldService;22.23.24.25.public class HelloWorldClient { 26.27.public static void main(String[] args)throws MalformedURLException, Exception { 28.29.// TODO Auto-generated method stub 30.31.Service s=new ObjectServiceFactory().create(IHelloWorldService.class);32.33.XFireProxyFactory xf=new XFireProxyFactory(XFireFactory.newInstance().getXFire());34.35.String url=“http://localhost:8989/HelloWorld/services/HelloWorldService”;36.37.38.39.try 40.41.{ 42.43.44.45.IHelloWorldService hs=(IHelloWorldService)xf.create(s,url);46.47.String st=hs.example(“zhangjin”);48.49.System.out.print(st);50.51.} 52.53.catch(Exception e)54.55.{ 56.57.e.printStackTrace();58.59.} 60.61.} 62.63.64.65.} 66.这里再说点题外话,有时候我们知道一个wsdl地址,比如想用java客户端引用.net 做得webservice,使用myeclipse引用,但是却出现无法通过验证的错误,这时我们可以直接在类中引用,步骤如下:
Java代码
1.public static void main(String[] args)throws MalformedURLException, Exception { 2.3.// TODO Auto-generated method stub 4.5.Service s=new ObjectServiceFactory().create(IHelloWorldService.class);6.7.XFireProxyFactory xf=new XFireProxyFactory(XFireFactory.newInstance().getXFire());8.9.10.11.//远程调用.net开发的webservice 12.13.Client c=new Client(new URL(“http://www.xiexiebang.com/axis2/
同理,也需要将axis2复制到webapp目录中
在axis2中部署webservice有两种方法,第一种是pojo方式,这种方式比较简单,但是有一些限制,例如部署的类不能加上包名
第二种方式是利用xml发布webservice,这种方法比较灵活,不需要限制类的声明
下面分别说明使用方法:
1.pojo方式:在Axis2中不需要进行任何的配置,就可以直接将一个简单的POJO发布成WebService。其中POJO中所有的public方法将被发布成WebService方法。先实现一个pojo类:
Java代码
1.public class HelloWorld{ 2.3.public String getName(String name)4.5.{ 6.7.return ”你好 “ + name;8.9.} 10.11.public int add(int a,int b)12.13.{ 14.15.return a+b;16.17.} 18.19.} 20.由于这两个方法都是public类型,所以都会发布成webservice。编译HelloWorld类后,将HelloWorld.class文件放到%tomcat%webappsaxis2WEB-INFpojo目录中(如果没有pojo目录,则建立该目录),然后打开浏览器进行测试:
输入一下url:
http://localhost:8080/axis2/services/listServices
会列出所有webservice
这是其中的两个webservice列表,接着,在客户端进行测试:
首先可以写一个封装类,减少编码,代码如下:
Java代码
1.package MZ.GetWebService;2.3.import javax.xml.namespace.QName;4.5.6.7.import org.apache.axis2.AxisFault;8.9.import org.apache.axis2.addressing.EndpointReference;10.11.import org.apache.axis2.client.Options;12.13.import org.apache.axis2.rpc.client.RPCServiceClient;14.15.16.17.18.19.public class GetWSByAxis2 { 20.21.private static String EndPointUrl;22.23.private static String QUrl=”http://ws.apache.org/axis2“;
24.25.private QName opAddEntry;26.27.public String WSUrl;28.29.public RPCServiceClient setOption()throws AxisFault 30.31.{ 32.33.RPCServiceClient serviceClient = new RPCServiceClient();34.35.Options options = serviceClient.getOptions();36.37.EndpointReference targetEPR = new EndpointReference(WSUrl);38.39.options.setTo(targetEPR);40.41.return serviceClient;42.43.} 44.45.46.47.public QName getQname(String Option){ 48.49.50.51.return new QName(QUrl,Option);52.53.} 54.55.//返回String 56.57.public String getStr(String Option)throws AxisFault 58.59.{ 60.61.RPCServiceClient serviceClient =this.setOption();62.63.64.65.opAddEntry =this.getQname(Option);66.67.68.69.String str =(String)serviceClient.invokeBlocking(opAddEntry, 70.71.new Object[]{}, new Class[]{String.class })[0];72.73.return str;74.75.} 76.77.// 返回一维String数组 78.79.public String[] getArray(String Option)throws AxisFault
80.81.{ 82.83.RPCServiceClient serviceClient =this.setOption();84.85.86.87.opAddEntry =this.getQname(Option);88.89.90.91.String[] strArray =(String[])serviceClient.invokeBlocking(opAddEntry, 92.93.new Object[]{}, new Class[]{String[].class })[0];94.95.return strArray;96.97.} 98.99.//从WebService中返回一个对象的实例
100.101.public Object getObject(String Option,Object o)throws AxisFault 102.103.{ 104.105.RPCServiceClient serviceClient =this.setOption();106.107.QName qname=this.getQname(Option);108.109.Object object = serviceClient.invokeBlocking(qname, new Object[]{},new Class[]{o.getClass()})[0];110.111.return object;112.113.} 114.115.116.117.///////////////////////////////////////// 读者可以自己封装数据类型,如int,byte,float等数据类型
118.119.} 120.客户端调用方法:
Java代码
1.MZ.GetWebService.GetWSByAxis2 ws=new MZ.GetWebService.GetWSByAxis2();2.3.ws.WSUrl=”http://localhost:8989/axis2/services/HelloWorld“;4.5.HelloWorld hello=(HelloWorld)ws.getObject(”getName“, HelloWorld.class);6.7.8.9.10.11.System.out.println(hello.getName(”zhangjin“));12.2.使用service.xml发布webservice,这种方式和直接放在pojo目录中的POJO类不同。要想将MyService类发布成Web Service,需要一个services.xml文件,这个文件需要放在META-INF目录中,该文件的内容如下:
Xml代码
1.
http://localhost:8080/axis2/services/myService?wsdl
除此之外,还有直接可以在其中制定webservice操作方法:可以这样些service.xml文件
Java代码
1. 10.11.service.HelloWorld 12.13. 14.15.
第二篇:个人对Java构造方法调用的总结(精选)
个人对Java构造方法调用的总结
1.构造方法必须与定义它的类有完全相同的名字。构造方法没有返回类型,也没有void。
2.类可以不声明构造方法,这时类中隐含声明了一个方法体为空的无参构造方法。但当类有明确声明构造方法时,它就不会自动生成。
3.构造方法的调用:子类首先要调用父类的构造方法才能继承父类的属性和方法。如果子类的构造方法中没有显式地调用父类的构造方法,则系统默认调用父类无参数的构造方法。说说3种情况:
①父类和子类都没有显式定义构造方法或者只定义了无参构造方法,这种情况下没有问题,Java 会顺着继承结构往上一直找到 Object,然后从 Object 开始往下依次执行构造函数。以下两个例子效果一样,只是Example2有相关输出: Example1 public class test1 { public static void main(String[] args){
A example =new A();} }
class A extends B{ } class B{ }
Example2:
public class test { public static void main(String[] args){
A example =new A();} }
class A extends B{ public A(){
System.out.println(“A's constructor is invoked.”);} } class B{ public B(){ System.out.println(“B's constructor is invoked.”);} } 输出:B's constructor is invoked
A's constructor is invoked
②父类只定义有参构造方法,那么无论子类如何定义,编译都会报错,因为父类缺少了默认无参构造方法,需要显式定义。
public class test { public static void main(String[] args){
A example =new A(3);} }
class A extends B{ public A(int a){
System.out.println(“A's constructor is invoked.”+“a=”+a);} }
class B{ private int b=0;//public B(){ // System.out.println(“B's constructor is invoked.”);//}
public B(int b){
System.out.println(“B's constructor is invoked.”+“b=”+b);} } 把注释符去掉就可以编译,输出:B's constructor is invoked.A's constructor is invoked.a=3
③在父类只有有参构造方法而没有无参构造方法时,可以用super(参数)来调用父类构造方法,但super无参时需要父类的无参构造方法。public class test { public static void main(String[] args){
A example =new A(3);} }
class A extends B{ public A(int a){
super(a);
System.out.println(“A's constructor is invoked.”+“a=”+a);} } class B{ private int b=0;
public B(int b){
System.out.println(“B's constructor is invoked”+“b=”+b);} } 输出:B's constructor is invoked.b=3 A's constructor is invoked.a=3 此处指定用super(3)调用public B(int b),所以就有如下输出: B's constructor is invoked.b=3 A's constructor is invoked.a=3
第三篇:用java调用oracle存储过程总结
用java调用oracle存储过程总结
分类: PL/SQL系列 2009-09-24 15:08 253人阅读 评论(0)收藏 举报
声明:
以下的例子不一定正确,只是为了演示大概的流程。
一:无返回值的存储过程 存储过程为:
CREATE OR REPLACE PROCEDURE TESTA(PARA1 IN VARCHAR2,PARA2 IN VARCHAR2)AS BEGIN
INSERT INTO HYQ.B_ID(I_ID,I_NAME)VALUES(PARA1, PARA2);END TESTA;
然后呢,在java里调用时就用下面的代码: package com.hyq.src;
import java.sql.*;import java.sql.ResultSet;
public class TestProcedureOne {
public TestProcedureOne(){
}
public static void main(String[] args){
String driver = “oracle.jdbc.driver.OracleDriver”;
String strUrl = “jdbc:oracle:thin:@127.0.0.1:1521: hyq ”;
Statement stmt = null;
ResultSet rs = null;
Connection conn = null;
CallableStatement cstmt = null;
try {
Class.forName(driver);
conn = DriverManager.getConnection(strUrl, “ hyq ”, “ hyq ”);
CallableStatement proc = null;
proc = conn.prepareCall(“{ call HYQ.TESTA(?,?)}”);
proc.setString(1, “100”);
proc.setString(2, “TestOne”);
proc.execute();
}
catch(SQLException ex2){
ex2.printStackTrace();
}
catch(Exception ex2){
ex2.printStackTrace();
}
finally{
try {
if(rs!= null){
rs.close();
if(stmt!=null){
stmt.close();
}
if(conn!=null){
conn.close();
}
}
}
catch(SQLException ex1){
}
}
} }
二:有返回值的存储过程(非列表)
当然了,这就先要求要建张表TESTTB,里面两个字段(I_ID,I_NAME)。
存储过程为: CREATE OR REPLACE PROCEDURE TESTB(PARA1 IN VARCHAR2,PARA2 OUT VARCHAR2)AS BEGIN
SELECT INTO PARA2 FROM TESTTB WHERE I_ID= PARA1;END TESTB;
在java里调用时就用下面的代码: package com.hyq.src;
public class TestProcedureTWO {
public TestProcedureTWO(){
}
public static void main(String[] args){
String driver = “oracle.jdbc.driver.OracleDriver”;
String strUrl = “jdbc:oracle:thin:@127.0.0.1:1521:hyq”;
Statement stmt = null;
ResultSet rs = null;
Connection conn = null;
try {
Class.forName(driver);
conn = DriverManager.getConnection(strUrl, “ hyq ”, “ hyq ”);
CallableStatement proc = null;
proc = conn.prepareCall(“{ call HYQ.TESTB(?,?)}”);
proc.setString(1, “100”);
proc.registerOutParameter(2, Types.VARCHAR);
proc.execute();
String testPrint = proc.getString(2);
System.out.println(“=testPrint=is=”+testPrint);
}
catch(SQLException ex2){
ex2.printStackTrace();
}
catch(Exception ex2){
ex2.printStackTrace();
}
finally{
try {
if(rs!= null){
rs.close();
if(stmt!=null){
stmt.close();
}
if(conn!=null){
conn.close();
}
}
}
catch(SQLException ex1){
}
}
} }
}
注意,这里的proc.getString(2)中的数值2并非任意的,而是和存储过程中的out列对应的,如果out是在第一个位置,那就是proc.getString(1),如果是第三个位置,就是proc.getString(3),当然也可以同时有多个返回值,那就是再多加几个out参数了。
三:返回列表
由于oracle存储过程没有返回值,它的所有返回值都是通过out参数来替代的,列表同样也不例外,但由于是集合,所以不能用一般的参数,必须要用pagkage了.所以要分两部分,1,建一个程序包。如下:
CREATE OR REPLACE PACKAGE TESTPACKAGE AS
TYPE Test_CURSOR IS REF CURSOR;end TESTPACKAGE;
2,建立存储过程,存储过程为:
CREATE OR REPLACE PROCEDURE TESTC(p_CURSOR out TESTPACKAGE.Test_CURSOR)IS BEGIN
OPEN p_CURSOR FOR SELECT * FROM HYQ.TESTTB;END TESTC;
可以看到,它是把游标(可以理解为一个指针),作为一个out 参数来返回值的。在java里调用时就用下面的代码: package com.hyq.src;import java.sql.*;
import java.io.OutputStream;import java.io.Writer;
import java.sql.PreparedStatement;import java.sql.ResultSet;import oracle.jdbc.driver.*;
public class TestProcedureTHREE {
public TestProcedureTHREE(){
}
public static void main(String[] args){
String driver = “oracle.jdbc.driver.OracleDriver”;
String strUrl = “jdbc:oracle:thin:@127.0.0.1:1521:hyq”;
Statement stmt = null;
ResultSet rs = null;
Connection conn = null;
try {
Class.forName(driver);
conn = DriverManager.getConnection(strUrl, “hyq”, “hyq”);
CallableStatement proc = null;
proc = conn.prepareCall(“{ call hyq.testc(?)}”);
proc.registerOutParameter(1,oracle.jdbc.OracleTypes.CURSOR);
proc.execute();
rs =(ResultSet)proc.getObject(1);
while(rs.next())
{
System.out.println(“
}
}
catch(SQLException ex2){
ex2.printStackTrace();
}
catch(Exception ex2){
ex2.printStackTrace();
}
finally{
try {
if(rs!= null){
rs.close();
if(stmt!=null){
stmt.close();
}
if(conn!=null){
conn.close();
}
}
}
catch(SQLException ex1){
}
}
} }
四。Hibernate调用存储过程
Connection con = session.connect();
CallableStatement proc = null;
con = connectionPool.getConnection();
proc = con.prepareCall(“{ call set_death_age(?, ?)}”);proc.setString(1, XXX);
proc.setInt(2, XXx);...proc.execute();
session.close();
在Hibernate中调用存储过程的示范代码--
如果底层数据库(如Oracle)支持存储过程,也可以通过存储过程来执行批量更新。存储过程直接在数据库中运行,速度更加快。在Oracle数据库中可以定义一个名为batchUpdateStudent()的存储过程,代码如下:
create or replace procedure batchUpdateStudent(p_age in number)as begin update STUDENT set AGE=AGE+1 where AGE>p_age;end;以上存储过程有一个参数p_age,代表学生的年龄,应用程序可按照以下方式调用存储过程: tx = session.beginTransaction();Connection con=session.connection();String procedure = “{call batchUpdateStudent(?)}”;CallableStatement cstmt = con.prepareCall(procedure);cstmt.setInt(1,0);//把年龄参数设为0 cstmt.executeUpdate();tx.commit();在以上代码中,我用的是Hibernate的 Transaction接口来声明事务,而不是采用JDBC API来声明事务。
存储过程中有一个参数p_age,代表客户的年龄,应用程序可按照以下方式调用存储过程:
代码内容
tx = session.beginTransaction();Connection con=session.connection();
String procedure = “{call batchUpdateCustomer(?)}”;
CallableStatement cstmt = con.prepareCall(procedure);
cstmt.setInt(1,0);//把年龄参数设为0
cstmt.executeUpdate();
tx.commit();
CREATE procedure selectAllUsers DYNAMIC RESULT SETS 1 BEGIN
DECLARE temp_cursor1 CURSOR WITH RETURN TO CLIENT FOR
SELECT * FROM test;
OPEN temp_cursor1;END;
映射文件中关于存储过程内容如下
............
{ ? = call selectAllUsers()}
{ ? = call selectAllUsers()} 也可以写成{ call selectAllUsers()},如果有参数就写成
{ ? = call selectAllUsers(?,?,?)}
代码中对query设置相应位置上的值就OK Java调用关键代码如下
Session session = HibernateUtil.currentSession();
Query query = session.getNamedQuery(“selectAllUsers”);
List list = query.list();
System.out.println(list);
要求你的存储过程必须能返回记录集,否则要出错
如果你的存储过程是完成非查询任务就应该在配置文件用以下三个标签
setFirstResult(int)和setMaxResults(int)方法来分页
第四篇:webService基础总结
WebService是一种跨编程语言和跨操作系统平台的远程调用技术
所谓跨编程语言和跨操作平台,就是说服务端程序采用java编写,客户端程序则可以采用其他编程语言编写,反之亦然!跨操作系统平台则是指服务端程序和客户端程序可以在不同的操作系统上运行。 除了WebService外,常见的远程调用技术还有RMI(Remote method invoke)和CORBA,由于WebService的跨平台和跨编程语言特点,因此比其他两种技术应用更为广泛,但性能略低。
WebService使用SOAP协议实现跨编程语言和跨操作系统平台
WebService采用HTTP协议传输数据,采用XML格式封装数据(即XML中说明调用远程服务对象的哪个方法,传递的参数是什么,以及服务对象的返回结果是什么)。WebService通过HTTP协议发送请求和接收结果时,发送的请求内容和结果内容都采用XML格式封装,并增加了一些特定的HTTP消息头,以说明HTTP消息的内容格式,这些特定的HTTP消息头和XML内容格式就是SOAP协议(simple object access protocol,简单对象访问协议)。
SOAP协议 = HTTP协议 + XML数据格式
HTTP协议和XML是被广泛使用的通用技术,各种编程语言对HTTP协议和XML这两种技术都提供了很好的支持,WebService客户端与服务器端使用什么编程语言都可以完成SOAP的功能,所以,WebService很容易实现跨编程语言,跨编程语言自然也就跨了操作系统
WebService客户端要调用一个WebService服务,首先要有知道这个服务的地址在哪,以及这个服务里有什么方法可以调用,所以,WebService务器端首先要通过一个WSDL文件来说明自己家里有啥服务可以对外调用,服务是什么(服务中有哪些方法,方法接受的参数是什么,返回值是什么),服务的网络地址用哪个url地址表示,服务通过什么方式来调用。 WSDL(webservice description language)是基于XML格式的,它是WebService客户端和服务器端都能理解的标准格式,其中描述的信息可以分为what,where,how等部分! WSDL文件保存在Web服务器上,通过一个url地址就可以访问到它。客户端要调用一个WebService服务之前,要知道该服务的WSDL文件的地址。WebService服务提供商可以通过两种方式来暴露它的WSDL文件地址:
• 注册到UDDI服务器,以便被人查找 • 直接告诉给客户端调用者,例如,在自己网站给出信息或邮件告诉。
第五篇:调用外部方法及工作流
调用外部方法及工作流
公开一个对象,来从执行的工作流中传给宿主应用程序,或者从宿主应用程序传给工作流不就行了吗?其实,使用现有的串行化技术,如.NET Remoting或者XML Web服务,就可完成这些事。串行化,也叫序列化,它可把数据从原有的形式转换成合适的形式,以在不同进程甚至不同计算机之间进行传输。
学习完本章,你将掌握:
1.创建并调用你的工作流外部的本地数据服务
2.理解怎样使用接口来为宿主进程和你的工作流之间进行通信。
3.使用设计的外部方法在你的工作流和宿主应用程序之间传输数据。
4.在一个正执行的工作流中调用其它工作流
在写前面的章节时,我自己不断地思考,“我不能再等了,我要弄清楚在哪里可把(工作流中的)真实数据返回到宿主应用程序中!”为什么?做了这么多的活动和工作流的演示,但都没有实际返回某些感兴趣的东西给宿主应用程序。我不知写过多少我们感兴趣的工作流的实例和演示,但至多只是仅仅处理过数据的初始化(就像第一章-WF简介中你看过的邮政编码的例子)。但事情变得更加有趣,坦率地说,当我们启动工作流,然后从外部源中寻找并处理数据、返回处理后的数据给我们的主应用程序要更加接近现实。
为什么不这样呢?公开一个对象,来从执行的工作流中传给宿主应用程序,或者从宿主应用程序传给工作流不就行了吗?其实,使用现有的串行化技术,如.NET Remoting或者XML Web服务,就可完成这些事。串行化,也叫序列化,它可把数据从原有的形式转换成合适的形式,以在不同进程甚至不同计算机之间进行传输。
为什么谈到序列化呢?因为你的工作流是在你的宿主进程中的不同线程上执行,不同线程之间传送数据,如不进行适当的序列化,将会引发灾难,具体原因超出了本书的讨论范围。其实,你的工作流能在一个持久化的状态下发送它的数据。这并没有在不同线程上,甚至它不在执行中。
但我们想在我们的工作流和正控制该工作流的宿主进程间传送数据时,使用.NET Remoting或者XML Web服务这样的技术为什么并没有认为是多余的呢?其实这绝对有必要!我们将创建local通信,本章将以此出发。我们将搭建必须的体系来满足线程数据序列化,以进行计算机之间或进程之间的数据传输。
创建ExternalDataService服务
当工作流和它的宿主进行通信时,在它发送和接收数据的时候,工作流要使用队列和消息。WF为我们做的越多,我们就可把重点更多的放到应用中特定任务的解决上。
工作流内部进程通信
对于简单的通信任务,WF使用“abstraction layer”来在工作流和宿主之间进行缓冲。抽象层像一个黑盒,你为它提供输入,它会执行一些神奇的任务,然后信息流出到另一边。但我们不用知道它是如何工作的。
在这种情形下,该黑盒就是一个知名的“local communication”服务。和WF术语中的任何一种服务一样,它也是另一种可插拔服务。区别是它不像WF中的那些已预先创建好的服务,你需要写出这个服务的一部分。为什么呢?因为你在宿主应用程序和你的工作流之间传递的数据有一定的特殊性。更进一步说,你可创建各种各样的数据传输方法,你可使用你设计的各种方法从宿主应用程序发送数据,然后在工作流中接收数据。
备注:这里有些事情你需要进行关注,那就是对象或集合的共享问题。因为宿主应用程序和工作流运行时在同一个应用程序域执行,因此引用类型的对象和集合就是通过引用而不是值进行传递。这意味着宿主应用程序和工作流实例在同一时间会访问和使用同一个对象,多线程环境下这会产生bug,出现数据并发访问错误。因此,对于可能要进行并发访问的对象或集合,你可考虑传递一个对象或集合的副本,或许这可通过实现ICloneable接口,或者考虑亲自序列化该对象或集合并传递序列化后的版本。
你可写这种local service,把它插进工作流,然后打开连接,发送数据。这些数据可以是字符串,DataSet对象,甚至可以是你设计的任何可被序列化的自定义对象。通信可以是双向的,尽管在本章我没有演示它。(这里,我仅仅是把数据从工作流中传回给宿主应用程序。)从工作流的角度来说,我们使用工具生成活动的目的是发送和接收数据。从宿主应用程序的角度来说,接收数据等同于一个事件,而发送数据就是在一个服务对象上的方法的简单调用。
备注:我们在后面几章看到更多的活动后还会重温该双向数据传输的概念。工作流活动从宿主应用程序中接收数据基于一个HandleExternalEvent活动,我们将在第10章“Event活动”中看到。我们也需要更深入地了解这些概念间的相互关系,这在第17章“宿主通信”中将进行介绍。对于当前,我们只是在工作流实例完成它的任务后,简单地返回复合数据给宿主。
我们需要做的还不仅仅是这一点,我们最终需要添加ExternalDataService服务到我们的工作流运行时中。ExternalDataService是一个可插拔的服务,它方便了工作流实例和宿主应用程序之间进行序列化数据的传输。在紧接下来的一节我们将写出的该服务的代码将做很多事(包括序列化数据的传输)。让我们来看看大体的开发过程。