posted @ 2010-10-25 19:55 BruceZhou 阅读(214) 评论(0)
编辑
Oracle数据库调用就是一门复杂的课程啦,我这里只写在WCF中的应用。
我看到有的人在写中间层调用数据库时,单把数据库调用作为一个服务来发布,仅仅是一个远程函数,上传一个Sql+参数列表,返回一个数据集,其他所有逻辑都在客户端编写应用,这就完成一个系统的开发啦。
我觉得这种方式太狭隘啦,完全抛弃了面向对象开发的思想,又回到面向过程的开发啦。我的方式是自己写类,在类中包含属性,在服务端执行Sql结果都写到类中,再返回类,这种方式在下一篇中再介绍,这里只写数据库。
在我刚学习时用的vs2003,那是还有OracleConnection等一些.Net内置的Oracle专用类库,在VS2008时,就找不到啦,我一直用的OleDB类库。
1. 连接字符串Provider=OraOLEDB.Oracle;Password=***;Persist Security Info=true;User ID=***;Data Source=***
2. 前期是自己写的SqlHelp,后来用的EntLib,微软企业库,现在是5版本啦。
3. 调用的基本方式是
public virtual bool boExecuteSql(string strSql, string[] _parameterS, string strDBConString, DataSet dsDBRe)
{
Boolean boResult;
//
int intDiv;
intDiv = _parameterS.Length % 2;
if (intDiv > 0)
{
boResult = false;
return boResult;
};
try
{
using (SFCDBCon = new OleDbConnection(strDBConString))
{
OleDbDataAdapter empS = new OleDbDataAdapter(strSql, SFCDBCon);
int countParameter = _parameterS.Length / 2;
OleDbParameter[] myOleParameterS = new OleDbParameter[countParameter];
for (int i = 0; i < countParameter; i++)
{
if (_parameterS[i * 2 + 1].Trim().Length > 0)
{
myOleParameterS[i] = new OleDbParameter(_parameterS[i * 2], _parameterS[i * 2 + 1]);
empS.SelectCommand.Parameters.Add(myOleParameterS[i]);
}
}
SFCDBCon.Open();
empS.Fill(dsDBRe, "myTB");
}
boResult = true;
}
catch (System.Exception e)
{
string eMsg = e.Message;
boResult = false;
}
//
return boResult;
}
4. 在连接Oralce是一定要牢记,要释放Oracle连接。如果不释放,Oracle就会产生许多空链接,直到连接数满。用Using()语法就简单啦,不用手动释放。
5. 在就是参数的应用。在Delphi中数据库控件可以自动感应Sql中包含的参数,单OldDb不能自动感应,必须手工建立参数,并赋值。
6. 调用存储过程,因为参数是相对固定的,我采用的方法是预先从Oracle系统表中求出参数。
SELECT OBJECT_NAME,ARGUMENT_NAME,POSITION,DATA_TYPE,IN_OUT FROM USER_ARGUMENTS WHERE OBJECT_NAME = :procname ORDER BY POSITION
7. EntLib的数据访问模块,操作Oracle数据库。
8. 别忘异常处理。否则客户端报错,就不知道什么原因啦。其中又包括WCF的异常处理,和Oracle数据库操作的异常处理,分别有不同处理方式。
posted @ 2010-10-25 19:48 BruceZhou 阅读(454) 评论(0)
编辑
在学习WCF之前,就见到过另一种编程思想,叫做面向服务开发,哈哈,当时不甚了了。
前面提到,我之前一直用Delphi开发的,所以是学习的Delphi面向对象开发,记得足足有3个多月,写了22万行代码,才算是明白面向对象开发。对我以后的工作和学习打下了结实的基础。
很长时间,我都是用WCF开发服务,用Delphi开发客户端,其中数据通过WCF交互的部分没有用到面向对象开发的思想,一直到转到C#开发WinForm。
用C#开发了一些客户端期间,学习了一本书《Expret C#2005 Business Objects》,虽然最后也没有用轨道CSLA架构,可是对我的启发很大。
一直到自己摸索到的开发风格。一律用类作为WCF数据交互的参数基础。
略写如下。
1. 先对一个系统要操作的数据进行规划,类化,即相关的数据组成一个类,
2. 对主要数据集部分做成段或类,生成类的泛型列表属性
3. 当然还要完善这个类的其他成员,方法等,才能写成一个有用的类
4. 把类作为ref读写参数,或作为返回值,返回处理后的她自己
例如:
using System;
using System.Collections.Generic;
using System.Data;
using System.Text;
using myHomeLibrary;//这里写了许多基础的东西,写了一些通用的属性,成员等,算是一个基础类
namespace BufferLibrary
{
public class BufLineItem ///主要数据集部分
{
private string _line = "N/A";
private string _onLine = "N/A";
private string _onLineFeederList = "N/A";
private string _linkModel = "N/A";
private string _linkMO = "N/A";
private string _typeF = "F";
public BufLineItem ()
{
//
}
public string LineName
{
get
{
return _line;
}
set
{
if (_line == value)
return;
_line = value;
}
}
public string OnLineModel
{
get
{
return _onLineModel;
}
set
{
if (_onLineSFCModel == value)
return;
_onLineSFCModel = value;
}
}
public string OnLineFeederList
{
get
{
return _onLineFeederList;
}
set
{
if (_onLineFeederList == value)
return;
_onLineFeederList = value;
}
}
public string LinkSFCModel
{
get
{
return _linkSFCModel;
}
set
{
if (_linkSFCModel == value)
return;
_linkSFCModel = value;
}
}
public string LinkMO
{
get
{
return _linkMO;
}
set
{
if (_linkMO == value)
return;
_linkMO = value;
}
}
/// <summary>
/// TypeF的標示
/// </summary>
public string TypeF
{
get
{
return _typeF;
}
set
{
if (_typeF == value)
return;
_typeF = value;
}
}
}
public class BufLineItemS : CHome
{
//
private List<BufLineItem> _lineItemS = new List<BufLineItem>();//泛型列表属性成员
#region 构造函数,析构函数
public CBufLineItemS()
{
ProjectName = "LineItemS";
ProjectType = 0;
}
#endregion
#region 自定義屬性
public List<BufLineItem> LineItemS//泛型列表属性
{
get
{
return _lineItemS;
}
set
{
if (_lineItemS == value)
return;
_lineItemS = value;
}
}
#endregion
#region 自定義方法
public BufLineItem ItemOfLine(string _tmpLine)
{
BufLineItem _tmpItem = new BufLineItem();
//
foreach( BufLineItem _item in _lineItemS )
{
if (_item.LineName == _tmpLine)
{
_tmpItem = _item;
break;
}
}
//
return _tmpItem;
}
#endregion
//
}
还有一个把LineItemS转换成DataTable的方法,就不写详细代码啦,以便于客户端DataGrid或作为数据集操作处理
}
WCF服务端是这么写的
[OperationContract]
bool ExecuteBufLine(ref CBufLineItemS _tmpBufLineItemS, string ftype);
public bool ExecuteBufLine(ref CBufLineItemS _tmpBufLineItemS, string ftype)
{
bool _boRes = false;
try
{
//这里是作为数据库处理,得到数据,就不详细谢啦
_tmpBufLineItemS.LineItemS.Clear();//这里清空原有数据
BufLineItem _tmpBufLineItem = new BufLineItem() { LineName = _tmpLine, LinkMO = _tmpWO, LinkModel = _tmpLinkModel, OnLineSFCModel = _tmpOnLineModel, OnLineFeederList = _tmpOnLineBom, Type34F = _tmp34f };//这里是根据数据集处理结果生成新的
_tmpBufLineItemS.LineItemS.Add(_tmpBufLineItem);//添加数据集
}
_boRes = true;
}
catch
{
_boRes = false;
}
return _boRes;
}
基本操作就是上述的方式,实际工作中,当然比非面向对象的方式快捷多啦。具体代码部分以后再补上。
posted @ 2010-10-25 19:48 BruceZhou 阅读(116) 评论(0)
编辑
在很长一段时间里,我把系统服务作为WCF的宿主正式发布后,工作都很正常。知道一个比较单个功能函数耗时较多的服务发布后,经常会异常退出。很长一段时间里,我都认为是相应时间设置太短,一直客户端接受到异常。我好久时间里,都关注和改进这个函数的执行效率,以便于在更短时间内处理完毕。
一直到我看到了系统服务“事件日志”,有报错信息,才正确分析并找到错误点。因为我在调试的过程中,好长一段时间都是还没调试到错误,就报超时错误啦,一直不能找到错误点。

其中错误的详细如下
Application: *****.WCFService.WINHost.exe
Framework Version: v4.0.30319
Description: The process was terminated due to an unhandled exception.
Exception Info: System.AccessViolationException
Stack:
at System.Data.Common.UnsafeNativeMethods+ICommandText.Execute(IntPtr, System.Guid ByRef, System.Data.OleDb.tagDBPARAMS, IntPtr ByRef, System.Object ByRef)
at System.Data.OleDb.OleDbCommand.ExecuteCommandTextForMultpleResults(System.Data.OleDb.tagDBPARAMS, System.Object ByRef)
at System.Data.OleDb.OleDbCommand.ExecuteCommandText(System.Object ByRef)
at System.Data.OleDb.OleDbCommand.ExecuteCommand(System.Data.CommandBehavior, System.Object ByRef)
at System.Data.OleDb.OleDbCommand.ExecuteReaderInternal(System.Data.CommandBehavior, System.String)
at System.Data.OleDb.OleDbCommand.ExecuteReader(System.Data.CommandBehavior)
at System.Data.OleDb.OleDbCommand.System.Data.IDbCommand.ExecuteReader(System.Data.CommandBehavior)
at System.Data.Common.DbDataAdapter.FillInternal(System.Data.DataSet, System.Data.DataTable[], Int32, Int32, System.String, System.Data.IDbCommand, System.Data.CommandBehavior)
at System.Data.Common.DbDataAdapter.Fill(System.Data.DataSet, Int32, Int32, System.String, System.Data.IDbCommand, System.Data.CommandBehavior)
at System.Data.Common.DbDataAdapter.Fill(System.Data.DataSet, System.String)
at SFC.BasicClass4.SFCOraHelp.boExecuteSql(System.String, System.String[], System.String, System.Data.DataSet)
at SFC.BasicClass4.SFCBasic.dsExecuteSql(System.String, System.String[], System.String)
at SFC.Buffernet.WCFService.Service.CBufferNet.GetFeederListOfLine(System.String, System.String, System.String)
at DynamicClass.SyncInvoke GetFeederListOfLine (System.Object, System.Object[], System.Object[])
at System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(System.Object, System.Object[], System.Object[] ByRef)
at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(System.ServiceModel.Dispatcher.MessageRpc ByRef)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(System.ServiceModel.Dispatcher.MessageRpc ByRef)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage41(System.ServiceModel.Dispatcher.MessageRpc ByRef)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(System.ServiceModel.Dispatcher.MessageRpc ByRef)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(System.ServiceModel.Dispatcher.MessageRpc ByRef)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage3(System.ServiceModel.Dispatcher.MessageRpc ByRef)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage2(System.ServiceModel.Dispatcher.MessageRpc ByRef)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage11(System.ServiceModel.Dispatcher.MessageRpc ByRef)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage1(System.ServiceModel.Dispatcher.MessageRpc ByRef)
at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean)
at System.ServiceModel.Dispatcher.ChannelHandler.DispatchAndReleasePump(System.ServiceModel.Channels.RequestContext, Boolean, System.ServiceModel.OperationContext)
at System.ServiceModel.Dispatcher.ChannelHandler.HandleRequest(System.ServiceModel.Channels.RequestContext, System.ServiceModel.OperationContext)
at System.ServiceModel.Dispatcher.ChannelHandler.AsyncMessagePump(System.IAsyncResult)
at System.ServiceModel.Dispatcher.ChannelHandler.OnAsyncReceiveComplete(System.IAsyncResult)
at System.Runtime.Fx+AsyncThunk.UnhandledExceptionFrame(System.IAsyncResult)
at System.Runtime.AsyncResult.Complete(Boolean)
at System.Runtime.AsyncResult.Complete(Boolean, System.Exception)
at System.ServiceModel.Channels.FramingDuplexSessionChannel+TryReceiveAsyncResult.OnReceive(System.IAsyncResult)
at System.Runtime.Fx+AsyncThunk.UnhandledExceptionFrame(System.IAsyncResult)
at System.Runtime.AsyncResult.Complete(Boolean)
at System.Runtime.AsyncResult.Complete(Boolean, System.Exception)
at System.ServiceModel.Channels.SynchronizedMessageSource+ReceiveAsyncResult.OnReceiveComplete(System.Object)
at System.ServiceModel.Channels.SessionConnectionReader.OnAsyncReadComplete(System.Object)
at System.ServiceModel.Channels.SocketConnection.FinishRead()
at System.ServiceModel.Channels.SocketConnection.AsyncReadCallback(Boolean, Int32, Int32)
at System.ServiceModel.Channels.OverlappedContext.CompleteCallback(UInt32, UInt32, System.Threading.NativeOverlapped*)
at System.Runtime.Fx+IOCompletionThunk.UnhandledExceptionFrame(UInt32, UInt32, System.Threading.NativeOverlapped*)
at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32, UInt32, System.Threading.NativeOverlapped*)
我仔细查找,找到了其中的函数GetFeederListOfLine,当我一眼看到这个函数的时候,我就知道是什么原因啦,肯定是数据库操作Sql语句的原因。
我仔细调试分析,找到原因。在Sql中要有不等的几个参数,当其中一个参数的值为空时,我写的处理函数就不会加在这个参数。前面的文章有介绍,在OleDb操作Oracle时,参数必须手动添加并赋值,我这里如果值是空,我写的处理函数就不会添加这个参数,致使报错。我在调试时,可以找到这个报错,也有Try处理过程,单在WCF中如果遇到这个问题,会直接退出,不会有任何报错。如果宿主是exe可执行程序,这个程序会直接退出,如果是系统服务,这个服务也会推出,但是状态显示时运行的,致使上面的运行按钮式可操作的,停止按钮式灰色的(正常运行后,运行按钮式灰色的,停止按钮式可操作的),但会在事件日志中记录这一异常错误。
posted @ 2010-10-25 19:48 BruceZhou 阅读(256) 评论(0)
编辑
这里我不写WCF怎么写代码的,网上有好多入门和提高的教程,我写的是怎么用,或者说我用到了WCF的什么。
刚开始写的时候,因为是为了Delphi开发而写的,所以算是一个远程函数调用,和COM+没有什么本质区别。就是在服务器上发布一个或多个函数,在客户端调用远程的函数,返回执行结果,当然包括数据集。1.我可以发布成网站形式的,和WebService差不多。单是开发起来,比WebService要可快速和简便,这是当时给我的感觉。这种发布方式没有正式应用的项目中。2.可以发布成一半exe可执行程式。这种发布方式当时给我的感觉很是惊艳。后来这种方式一至被我作为调试时的发布方式。3.可以注册成Windows系统服务,我学习这种方式的初衷是因为可以开机启动,不用登陆进系统。现在是我的WCF正式发布的方式。
因为最初的开发目的是为了Delphi可以调用,所以“远程函数调用”这种开发方式一致应用了好久,知道转到.netwinform开发,才转变了这种思想。
Delphi7是不支持直接调用WCF,生成WSDL中间语言的,我有到Delphi官方网站,找到最新的更新包WSDLImp.exe,和调用WCF的简介,当时还没有中文资料,因为Delphi7不能直接用可视化界面生成WSDL,只能用命令行调用,我还专门写了一个工具,省去了命令行的麻烦。其实Delphi已经更新到2005啦,可是我对Delphi7之后的所有版本都不敢兴趣,因为7之后的版本多是学习NET的IDE开发界面的,用着很不习惯,所以到现在,如果用到Delphi还是Delphi7,哈哈。在Win7出来后,才下的Delphi2010,因为Win7不支持Delphi7啦。
在讲些WCF初级应用所用到的发布方式。
1. 是IIS发布,和网站差不多,我问什么没用呢?、
还是因为Delphi调用的问题。Delphi生成WSDL后,只能识别IP地址的WSDL格式,不能识别 电脑名的WSDL格式,用IIS发布后,Delphi调用,中间一个地址一直是电脑名,不能转换成IP地址,所以才用到了exe发布和系统服务发布,因为她们都是用IP地址发布的,不会有电脑名的问题。虽然很久后找到了解决电脑名问题的方法,可是用系统服务发布挺好的,再也没有用IIS发布过WCF。
2. Exe可执行文件发布。只是在学习和调试的时候用。
3. 系统服务发布。在不用IIS发布这种方式后,我也没有打算用可执行文件发布作为正式的系统来用,因为考虑到开机登录问题,决定用系统服务来作为正式的发布。这三种方法几乎是同时学习的,因为在WCF的入门知识中就介绍啦。哈哈
4. 系统服务的应用。按照教程写的,WCF发布只能算是系统服务的一个简单应用,中间只要注意系统服务的名字就好啦,不是WCF服务的名字。因为默认的都是“Service1”,要改成自己用到的名字。只是在VS2010出来后,才有遇到了一个严重问题。在.Net3.5/.Net3.5SP1时,注册系统服务,都是用的.Net2.0的东西,到.Net4.0后才是用的.Net4.0的注册程序。用.Net3.5写的只能用.Net2.0的注册程序,同样用.Net4.0写的只能用.Net4.0的注册程序。不能混着用。
5. 在高级应用篇再介绍用到的 系统服务的日志排查WCF问题。
posted @ 2010-10-25 19:47 BruceZhou 阅读(359) 评论(0)
编辑
1. “maxReceivedMessageSize”
在用系统服务正式发布WCF后,很长一段时间,都很正常。直到一个比较复杂的功能开发。我的系统主要是和Oracle数据库开发,其中写了一个比较复杂的功能模块,返回很大的数据集,在客户端调用时一直报错,大约是“范围超过了65536”,从字面意思就很容易理解啦,maxReceivedMessageSize的默认值是65536,肯定某个方面超过了这个预设值。从网上查资料,果不其然,maxReceivedMessageSize这个选项就是表示中间传输的消息大小的。如果要传输的数据太大的话,就要增加这个值啦。我加了三个0,变成65536000,就不会出现开始的那个问题啦。当然,根据具体需要,你也可以改成其他更大或更小的值。
2. “<security mode="None" />”
在用TCP端口发布时,必须用这个选项。
3. “maxBufferSize”
修改了上述两个选项后,又在很长的一段时间,都很正常。直到一个问题出现。哈哈,这个问题的错误代码我没记住,主要的原因就是上传的数据太大啦,必须增大这个值。哈哈
4. “DataTable”作为返回值,这个没成功,后来改用DataSet作为返回值
5. “数组”作为返回值,这个正是用到啦,一些旧的系统还在用。现在当然不要啦。
6. “泛型”作为返回值,这个还要修改一些地方,得不偿失,我就改成自己写的类(包括数组或泛型 属性)作为返回值啦,
7. “自己写的类”做回返回值,现在主要的方式,也可以说是新开发应用的唯一的方式。
8. 千万别忘引用“System.ServiceModel”
posted @ 2010-10-25 19:47 BruceZhou 阅读(284) 评论(0)
编辑
这里的第一个问题应该是为什么选择三层或多层架构?
1. 起初我选择三层架构的原因仅仅是不用安装Oracle客户端。、
我们这里许多系统都是要连接Oralce数据库的,又都是直接连接的Oracle,也就是2层架构。Oracle客户端约有180M,每台要用到我们系统的都要安装,又牵扯到用户的电脑权限管理,我们没有管理员权限,每次安装都要联系其他有权限的同事帮忙,所以想不用安装Oracle客户端该多好啊。
2. 当开始学习了一段三层架构后,才知道三层远不是不用安装Oracle客户端这么低级,她是一种非常庞大和精深的开发思想,多以又有进一步要求,想把我们的系统全部转换到三层架构的。
3. 又有进步后,才知道,三层和两层,各有利弊,并不是三层一定优于两层。不过这和我们的系统关系不大啦,因为我分析多次,我们的系统还是三层的好处要大于两层的。
当开发了一些三层架构的系统并应用后,这时才到问什么选择WCF的问题
1. 这里要谈一下我所用到三层架构的技术,主要包括DCOM(没有正式用到项目中,仅测试)。COM+,这个用到不少系统中,有些至今还在用,不过早已不是我负责的啦。WebService,就是指asp.net WebService,和架设网站一样。CORBA, 这个用到了正式的系统中,不过后来都改啦,CORBA是我在用WCF之前所用到的最好的功能,知道现在我还是觉的CORBA某些特点要优于WCF.
之上的4种三层技术除了WebService使用ASP.NET开发的外,其他都是Delphi开发的,因为当时的系统都是Delphi地。
2. DCOM和COM+,为什么不选择DCOM,而选择COM+?
因为我又用到了COM+,COM+的部署和开发比DCOM好多啦。DCOM的选择很快,约有不到一周的时间,因为找的一些例子都是COM+的,李维《Delphi5.x ADO/MTS/COM+高级程序设计篇》,是我的三层架构入门和提高最重要的书。不过很抱歉开始学习的是盗版书,一直到后来一位前辈离职的时候,把他的一本正版书送给了我,我才视为经典收藏起来,在后来几年不时拿出瞻仰一番,知道去年搬家,把一些不用的书放在朋友家里,才一年多没有拿来看看。
COM+开发已经用的挺成熟啦,用到了一些ODM系统中(多是同事开发的),但是她的客户端部署时对权限要求交高,所以不是很想用她开发整个系统,不可否认的是,通过COM+的学习在三层架构学习中,给我的很大帮助,和奠定了基础,才成有后来的WCF的快速入门。
3. COBRA,因为当时一直用的Delphi开发,所以一直想找一种比COM+部署更简便的三层构(仅仅是部署方便的问题,不是技术问题),才找到了COBRA,开发了一些东西,对大的部署简单很是上心。当时还写了2篇文章发到Delphibbs大富翁上,主要写的是怎么用Delphi6+7组合来开发COBRA并部署,还有几位大富翁发信来问。因为她开发来比COM+要复杂,还要受到中间语言的限制,所以没有正式用到项目中。不过到现在还是觉得她的部署简单和自动负载平衡是很棒的。
4. Asp.net WebService,这是在COM+后接触的,比CORBA家要早一段时间。当时还是VS2003, 算是.net入门吧。我一直觉得我学习.net是以开发Asp.netWebService开始的,当时的客户端还是Delphi开发地。因为主要是和Oracle数据库开发,还写了一篇怎么用XML格式传输数据集,并Delphi调用处理。一直到WCF,还开发了几个用XML格式传输数据集,并Delphi客户端调用的服务。
不选择她还是因为部署问题,到不算是真的部署问题。她的部署也是非常简单的,和ASP.NET网站一样。主要是因为她只能HTTP调用,我所开发的系统客户端,还是前面的问题,权限限制,许多时候要用代理才能上网,并许多客户机的HTTP代理是固定的,这就杯具啦,有代理就不能连接WebService啦。当然,还有许多流程或其他可以解决这个问题,不过们部署起来越简单岂不越好。
5. 一直到我遇到WCF, 才发现这就是我要的东西。当时因为是看到了VS2008的算是广告吧,一直在关注2008,才发现的这个WCF。知道学习VS2008+WCF好久,才知道VS2005+net3.0也是可以开发WCF的。当时要用WCF的主要原因是1.她能TCP端口发布,不受http代理的影响,2.她是新技术,我觉得第2点倒是占主要的,哈哈。
6. 再简单介绍一些我.NET的学习历史。我是因为Asp.net WebService才学习.Net的,学习用到VS2003,当时客户端都是Delphi,对.net的WinForm开发没怎么上心。VS2005几乎没用过。到需要.NET或者说转到.Net开发就是VS2008啦。
posted @ 2010-10-25 19:45 BruceZhou 阅读(665) 评论(0)
编辑