C# 打印输出的 错误 求大牛们解决!!!!

wangzhonglong456 2012-10-18 05:44:10
这是一个打印输出的 主要操作 函数 我不理解 new SvfrClient(strHost) 实例化的这个函数
还有他随后的 .VrInit() 等一系列方法 原理到底是什么 有用过这个方法打印输出账单的 解决下 谢谢啦!!!
protected void Output_Report(string strSpoolFile)
{

int code = 0;

try
{

svf = new SvfrClient(strHost);


code = svf.VrInit();
if ( code < 0 )
{
StringBuilder StbLogMsg = new StringBuilder();
StbLogMsg.Append(Util.GetLogMessage(httSession,"\r\nErrorMessage : "));
StbLogMsg.Append("\r\nErrorMethod : Output_Report");
log.Error(StbLogMsg.ToString());


string strMessage = Util.GetMessage(messageManager, strWorkName,
MessageId_Report.EREP0001);
throw new ReportException(strMessage);
}


code = svf.VrSetPrinter("EMF","EMF");
if ( code < 0 )
{

StringBuilder StbLogMsg = new StringBuilder();
StbLogMsg.Append(Util.GetLogMessage(httSession,"\r\nErrorMessage :"));
StbLogMsg.Append("\r\nErrorMethod : Output_Report");
log.Error(StbLogMsg.ToString());


string strMessage = Util.GetMessage(messageManager, strWorkName,
MessageId_Report.EREP0002);
throw new ReportException(strMessage);
}


code = svf.VrSetSpoolFileName2(strPdfPath + strSpoolFile + ".wdp");
if (code < 0)
{

StringBuilder StbLogMsg = new StringBuilder();
StbLogMsg.Append(Util.GetLogMessage(httSession,
"\r\nErrorMessage : "));
StbLogMsg.Append("\r\nErrorMethod : Output_Report");
log.Error(StbLogMsg.ToString());


string strMessage = Util.GetMessage(messageManager,strWorkName,
MessageId_Report.EREP0003);
throw new ReportException(strMessage);
}


code = svf.VrComout("/{SPLM 3}/");
if (code < 0)
{

StringBuilder StbLogMsg = new StringBuilder();
StbLogMsg.Append(Util.GetLogMessage(httSession,
"\r\nErrorMessage : "));
StbLogMsg.Append("\r\nErrorMethod : Output_Report");
log.Error(StbLogMsg.ToString());


string strMessage = Util.GetMessage(messageManager, strWorkName,
MessageId_Report.EREP0004);
throw new ReportException(strMessage);
}


PrintFormData();


code = svf.VrPrint();
if (code < 0)
{

StringBuilder StbLogMsg = new StringBuilder();
StbLogMsg.Append(Util.GetLogMessage(httSession,"\r\nErrorMessage : "));
StbLogMsg.Append("\r\nErrorMethod : Output_Report");
log.Error(StbLogMsg.ToString());


string strMessage = Util.GetMessage(messageManager, strWorkName,
MessageId_Report.EREP0005);
throw new ReportException(strMessage);
}


code = svf.VrQuit();//出错的地方,直接抛异常!!
if (code < 0)
{

StringBuilder StbLogMsg = new StringBuilder();
StbLogMsg.Append(Util.GetLogMessage(httSession,
"\r\nErrorMessage : "));
StbLogMsg.Append("\r\nErrorMethod : Output_Report");
log.Error(StbLogMsg.ToString());


string strMessage = Util.GetMessage(messageManager, strWorkName,
MessageId_Report.EREP0006);
throw new ReportException(strMessage);
}
}
catch(DException)
{
throw;
}
catch(Exception ex)
{

StringBuilder StbLogMsg = new StringBuilder();
StbLogMsg.Append(Util.GetLogMessage(httSession,"\r\nErrorMessage : " + ex.ToString()));
StbLogMsg.Append("\r\nErrorMethod : Output_Report");
StbLogMsg.Append("\r\n ");
log.Error(StbLogMsg.ToString());


string strMessage = Util.GetMessage(messageManager, strWorkName,
MessageId_Report.EREP0007);
throw new ReportException(strMessage, ex);
}
finally
{
svf.Close();
}
}
...全文
757 12 打赏 收藏 转发到动态 举报
写回复
用AI写文章
12 条回复
切换为时间正序
请发表友善的回复…
发表回复
shaftstop 2012-10-19
  • 打赏
  • 举报
回复
new SvfrClient(strHost); 里面的 strHost一般写成localhost或者是IP地址

你多看看帮助文档。
shaftstop 2012-10-19
  • 打赏
  • 举报
回复
看你的代码应该没有什么问题,
可能出现问题的地方有三个。
1. svf = new SvfrClient(strHost);中定义的strHost定义错了?
2. code = svf.VrSetPrinter("EMF","EMF"); 环境变量里面是不是没有EMF?
改成code = svf.VrSetPrinter("",@""+"EMF");最好,即使没有也不会出错。另外确认下是否跟本地打印机的名字一致的。
3. PrintFormData();里面的打印要素是否齐全?
wangzhonglong456 2012-10-19
  • 打赏
  • 举报
回复
还有 这个类在的文件 只是项目的分支文件 里边封装了打印输出的代码 调试时不能 debug 进去 咋弄才能调试进去呢??
wangzhonglong456 2012-10-19
  • 打赏
  • 举报
回复
[Quote=引用 6 楼 的回复:]

自己去看 SvfrClient代码,调试
[/Quote]

调试到 code = svf.VrQuit(); 直接抛异常!!!

这是SvfrClient 代码

using System;
using System.IO;
using System.Text;

namespace jp.co.fit.vfreport
{
public class SvfrClient : Svf
{
public SvfrClient();
public SvfrClient(string host);
public SvfrClient(string host, int port);

public static string BuildNumber { get; }
public static string FileVersion { get; }
public static string ProductName { get; }

public void Close();
protected internal static bool GetProperty(string key, bool def);
protected internal static int GetProperty(string key, int def);
protected internal static string GetProperty(string key, string def);
public int RpcFlush();
public int VrAbortDoc();
public int VrAdjust(int x, int y);
public int VrAdjustd(string formName, int x, int y);
public int VrAdjustd2(int x, int y);
public int VrAdjustm(string formName, int x, int y);
public int VrAdjustm2(int x, int y);
public int VrAttribute(string fieldName, string data);
public int VrAttributen(string fieldName, int index, string data);
public int VrComout(string data);
public int VrCondition(string condition, int mode);
public int VrCopy(int copies);
public int VrDebugPrint2(string msg, int level);
public int VrEnablePreview(int enable, int input);
public int VrEnablePrintBTN(int enable);
public int VrEndDoc();
public int VrEndPage();
public int VrEndRecord();
public int VrEndSendFax();
public int VrExecQuery();
public int VrFeed(int nUnit, int nLine);
public int VrGetAttribute(string fieldName, string attrName, StringBuilder buffer);
public int VrGetManageNum();
public int VrGetPID();
public int VrGetPrintFlag(StringBuilder buffer);
public string VrGetSpoolFileName();
public int VrGetStatus();
public int VrInit();
public int VrInit(string enc);
public int VriOut(string fieldName, int data);
public int VriOutn(string fieldName, int index, int data);
public int VrlOut(string fieldName, long data);
public int VrlOutn(string fieldName, int index, long data);
public int VrNotUseServer();
public int VrOutputQuery(string fileName);
public int VrOutputQueryStream(Stream output);
public int VrPage(string flags);
public int VrPaperEject();
public int VrPreLoadForm(string fileName, int cache);
public int VrPreviewEndPage();
public int VrPreviewStartPage();
public int VrPrint();
public int VrQuit(); 抛异常
public int VrReport();
public int VrrOut(string fieldName, double data);
public int VrrOutn(string fieldName, int index, double data);
public int VrSendFaxPress(string sendInfo);
public int VrSeqOut(string data);
public int VrSetCalcMode(int mode);
public int VrSetClientInfo2(string hostName, string userName);
public int VrSetComputerName(string computerName);
public int VrSetCSVFileName(string fileName, int nLevel, int nMode);
public int VrSetCSVFileStream(Stream output, int nLevel, int nMode);
public int VrSetDocName2(string docName);
public int VrSetDocumentSaveMode(int mode, int data, string dirName, int compress);
public int VrSetDuplex(int nMode, int nMargin);
public int VrSetForm(string formName, int mode);
public string VrSetLang(string category, string charSet);
public int VrSetLocale(string locale);
public int VrSetMachineKind(int nMachine);
public int VrSetOutputBin(int nBin);
public int VrSetOutputPrinter(string printerName, int preferedPageCount);
public int VrSetOutputVPrinter(string printerName);
public int VrSetOutputVPrinter2(string groupName);
public int VrSetPageCount(int pageNo, int incValue);
public int VrSetPreviewAndSpool(int fUse);
public int VrSetPrinter(string printerName, string portName);
public int VrSetPrinterGroup(string groupName);
public int VrSetPrinterKind(int kind);
public int VrSetPunch(int nLocation);
public int VrSetQuery(string connectStr, string queryName, int mode);
public int VrSetServerName(string serverName);
public int VrSetSort(int nCondition);
public int VrSetSpoolFileName2(string fileName);
public int VrSetSpoolFileStream(Stream output);
public int VrSetSpoolMode(int mode);
public int VrSetStaple(int nLocation);
public int VrSetTray(int nTray);
public int VrSetUseReportDirector(int fUse);
public int VrSetUserName(string userName);
public int VrSetUseSaveMode2();
public int VrsOut(string fieldName, string data);
public int VrsOutn(string fieldName, int index, string data);
public int VrStack();
public int VrStackOut();
public int VrStartSendFax(string sndInfo, string rcvInfo);
public int VrTest();
public int VrTestPalette();
}
}

里边都没写函数内容 不懂啊!!!
wangzhonglong456 2012-10-19
  • 打赏
  • 举报
回复
顶下 大哥大姐们 帮忙看看吧!!!
bdmh 2012-10-19
  • 打赏
  • 举报
回复
自己去看 SvfrClient代码,调试
wangzhonglong456 2012-10-19
  • 打赏
  • 举报
回复
'jp.co.fit.vfreport.SvfrException' の初回例外が jp.co.fit.vfreport.SvfrClient.DLL で発生しました。
'Isid.RiskTaker.Report.ReportException' の初回例外が Isid.RiskTaker.Common.DLL で発生しました。
'Isid.RiskTaker.Report.ReportException' の初回例外が Isid.RiskTaker.Common.DLL で発生しました。


三个dll 错了 好像!!!
wangzhonglong456 2012-10-19
  • 打赏
  • 举报
回复
Microsoft.JScript, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
VJSharpCodeProvider, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
CppCodeProvider, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
approval, Version=1.0.4675.20099, Culture=neutral, PublicKeyToken=null
baddebtprovision, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
common_info, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
customer, Version=1.0.4675.20100, Culture=neutral, PublicKeyToken=null
file, Version=1.0.4675.20098, Culture=neutral, PublicKeyToken=null
grading, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
guarantee, Version=1.0.4675.20099, Culture=neutral, PublicKeyToken=null
ICSharpCode.SharpZipLib, Version=0.84.0.0, Culture=neutral, PublicKeyToken=1b03e6acf1164f73
Isid.RiskTaker.Approval.Bank, Version=1.0.4671.29380, Culture=neutral, PublicKeyToken=null
Isid.RiskTaker.Approval.Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Isid.RiskTaker.Approval.DataAccess, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Isid.RiskTaker.Approval.Logic, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Isid.RiskTaker.Approval.Persistence, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Isid.RiskTaker.Common, Version=1.0.4675.20077, Culture=neutral, PublicKeyToken=null
Isid.RiskTaker.Customer.Common, Version=1.0.4671.29354, Culture=neutral, PublicKeyToken=null
Isid.RiskTaker.Customer.Logic, Version=1.0.4671.29355, Culture=neutral, PublicKeyToken=null
Isid.RiskTaker.Customer.LogicXml, Version=1.0.4671.29354, Culture=neutral, PublicKeyToken=null
Isid.RiskTaker.Guarantee.Common, Version=1.0.4671.29351, Culture=neutral, PublicKeyToken=null
Isid.RiskTaker.Guarantee.DataAccess, Version=1.0.4671.29352, Culture=neutral, PublicKeyToken=null
Isid.RiskTaker.Guarantee.Logic, Version=1.0.4671.29353, Culture=neutral, PublicKeyToken=null
Isid.RiskTaker.Guarantee.Persistence, Version=1.0.4671.29351, Culture=neutral, PublicKeyToken=null
Isid.RiskTaker.Portal.Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Isid.RiskTaker.Portal.DataAccess, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Isid.RiskTaker.Portal.Logic, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Isid.RiskTaker.Portal.Persistence, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Isid.RiskTaker.SalesForce.Common, Version=1.0.4671.29359, Culture=neutral, PublicKeyToken=null
Microsoft.JScript, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
VJSharpCodeProvider, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
CppCodeProvider, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
approval, Version=1.0.4675.20099, Culture=neutral, PublicKeyToken=null
baddebtprovision, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
common_info, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
customer, Version=1.0.4675.20100, Culture=neutral, PublicKeyToken=null
file, Version=1.0.4675.20098, Culture=neutral, PublicKeyToken=null
grading, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
guarantee, Version=1.0.4675.20099, Culture=neutral, PublicKeyToken=null
ICSharpCode.SharpZipLib, Version=0.84.0.0, Culture=neutral, PublicKeyToken=1b03e6acf1164f73
Isid.RiskTaker.Approval.Bank, Version=1.0.4671.29380, Culture=neutral, PublicKeyToken=null
Isid.RiskTaker.Approval.Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Isid.RiskTaker.Approval.DataAccess, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Isid.RiskTaker.Approval.Logic, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Isid.RiskTaker.Approval.Persistence, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Isid.RiskTaker.Common, Version=1.0.4675.20077, Culture=neutral, PublicKeyToken=null
Isid.RiskTaker.Customer.Common, Version=1.0.4671.29354, Culture=neutral, PublicKeyToken=null
Isid.RiskTaker.Customer.Logic, Version=1.0.4671.29355, Culture=neutral, PublicKeyToken=null
Isid.RiskTaker.Customer.LogicXml, Version=1.0.4671.29354, Culture=neutral, PublicKeyToken=null
Isid.RiskTaker.Guarantee.Common, Version=1.0.4671.29351, Culture=neutral, PublicKeyToken=null
Isid.RiskTaker.Guarantee.DataAccess, Version=1.0.4671.29352, Culture=neutral, PublicKeyToken=null
Isid.RiskTaker.Guarantee.Logic, Version=1.0.4671.29353, Culture=neutral, PublicKeyToken=null
Isid.RiskTaker.Guarantee.Persistence, Version=1.0.4671.29351, Culture=neutral, PublicKeyToken=null
Isid.RiskTaker.Portal.Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Isid.RiskTaker.Portal.DataAccess, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Isid.RiskTaker.Portal.Logic, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Isid.RiskTaker.Portal.Persistence, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Isid.RiskTaker.SalesForce.Common, Version=1.0.4671.29359, Culture=neutral, PublicKeyToken=null
'jp.co.fit.vfreport.SvfrException' の初回例外が jp.co.fit.vfreport.SvfrClient.DLL で発生しました。
'Isid.RiskTaker.Report.ReportException' の初回例外が Isid.RiskTaker.Common.DLL で発生しました。
'Isid.RiskTaker.Report.ReportException' の初回例外が Isid.RiskTaker.Common.DLL で発生しました。
six-years 2012-10-19
  • 打赏
  • 举报
回复
代码这么多 咋看啊 还是发错误信息吧
IEEE_China 2012-10-19
  • 打赏
  • 举报
回复
提示什么错误?
wangzhonglong456 2012-10-19
  • 打赏
  • 举报
回复
大哥们 都帮帮忙啊!!!!!
wangzhonglong456 2012-10-19
  • 打赏
  • 举报
回复
[Quote=引用 11 楼 的回复:]

new SvfrClient(strHost); 里面的 strHost一般写成localhost或者是IP地址

你多看看帮助文档。
[/Quote]

谢谢 回复啊!! 已经解决 了 我用的是本地虚拟打印机 少创建了一个 对应的文件夹
呵呵 谢谢 各位大牛们 呵呵!!

接分吧!!
你真的了解Ioc与AOP吗? 收藏 你真的了解Ioc与AOP吗?我现在还不是很了解,而且越学习越发现自己了解的很少,Ioc与AOP中蕴涵了大量的能量等待我们去开发。在这个系列 中,我仅仅利用Sping.net这个框架向大家展示一下Ioc与AOP的强大功能(呵呵,其实写这段话的目的是因为“文章题目”牛皮吹得有点大了,给自 己个台阶下罢了)。 在这个系列中一共包含6个案例,从简单到复杂,也是对问题分解、思考和解决的一个过程,它们分别是: (1)类之间的依赖; 降低 (2)接口依赖; (3)基 于配置文件和Reflection的工厂模式; (4)使用Spring.net实现Ioc; (5)Romoting; (6)利用Ioc在不动一行代码的情 况下实现Remoting。为了更好的理解文中的内容,最好顺序阅读。 作为一个应用系统,代码复用至关重要。如果在你的设计中,类与类存在很强的相互关联,那么你会发现在重用这些组件时就存在很严重的问题。在 Step1到Step3-Reflection的例子中,我们试图 利用“针对接口编程”以及自己设计的Ioc对系统进行解耦。在Step3到Step5的例子中,我们将利用Spring.net提供的Ioc框架,轻松完 成解耦以及系统改造等工作。 一、类之间的依赖 我们的第一个例子主要用于说明程序的基本构造,并且作为一个反面典型,引出为什么要解耦,以及如何下手。在这个例子中,我们将创建三个程序集,分别是MainApp.exe、HelloGenerator.dll以及SayHello.dll。它们之间的关系如下图所示: HelloGenerator类根据提供的姓名产生一个问候字符串,代码如下: using System; namespace IocInCSharp { public class EnHelloGenerator { public string GetHelloString(string name) { return String.Format("Hello, {0}", name); } } } SayHello类持有一个对EnHelloGenerator的引用,并负责将生成出来的问候字符串打印出来。 using System; namespace IocInCSharp { public class SayHello { private EnHelloGenerator _helloGen; public EnHelloGenerator HelloGenerator { get { return _helloGen; } set { _helloGen = value; } } public void SayHelloTo(string name) { if(_helloGen != null) Console.WriteLine(_helloGen.GetHelloString(name)); else Console.WriteLine("Client.hello is not initialized"); } } } MainApp.exe负责完成对象的创建、组装以及调用工作: using System; namespace IocInCSharp { public class MainApp { public static void Main() { SayHello sayHello = new SayHello(); sayHello.HelloGenerator = new EnHelloGenerator(); sayHello.SayHelloTo("zhenyulu"); } } } 在这个设计中,组件与组件之间、类与类之间存在着紧密的耦合关系。SayHello类中的_helloGen字段类型为 EnHelloGenerator,这将导致我们很难给它赋予一个其它的HelloGenerator(例如CnHelloGenerator,用于生成 中文问候语)。另外MainApp也严重依赖于SayHello.dll以及HelloGenerator.dll,在程序中我们可以看到类似new SayHello();new EnHelloGenerator();的命令。 这种紧密的耦合关系导致组件的复用性降低。试想,如果想复用SayHello组件,那么我们不得不连同HelloGenerator一同拷贝过去, 因为SayHello.dll是依赖与HelloGenerator.dll的。解决这个问题的办法就是“针对抽象(接口)”编程 (依赖倒置原则)。这里的抽象既包括抽象类也包括接口。我不想过多的去谈抽象类和接口的区别,在后续的例子中我们将使用接口。由于接口在进行“动态代理” 时仍能保持类型信息,而抽象类可能由于代理的原因导致继承关系的“截断”(如MixIn等)。除此之外,对于单继承的C#语言而言,使用接口可以拥有更大 的弹性。 二、接口依赖 既然类之间的依赖导致耦合过于紧密,按照《设计模式》的理论,我们要依赖于接口。但是人们往往发现,仅仅依赖于接口似乎并不能完全解决问题。我们从上面的例子中抽象出接口后,组件间的依赖关系可能变成如下图所示: 经过改造后,SayHello不再依赖于具体的HelloGenerator,而是依赖于IHelloGenerator接口,如此一来,我们可以 动态的将EnHelloGenerator或是CnHelloGenerator赋给SayHello,其打印行为也随之发生改变。接口的定义以及改造后 的SayHello代码如下(为了节省空间,将代码合并书写): using System; namespace IocInCSharp { public interface IHelloGenerator { string GetHelloString(string name); } public interface ISayHello { IHelloGenerator HelloGenerator{ get; set; } void SayHelloTo(string name); } public class SayHello : ISayHello { private IHelloGenerator _helloGen; public IHelloGenerator HelloGenerator { get { return _helloGen; } set { _helloGen = value; } } public void SayHelloTo(string name) { if(_helloGen != null) Console.WriteLine(_helloGen.GetHelloString(name)); else Console.WriteLine("Client.hello is not initialized"); } } } 但是我们的MainApp似乎并没有从接口抽象中得到什么好处,从图中看,MainApp居然依赖于三个组件:ICommon.dll、 HelloGenerator.dll以及SayHello.dll。这是由于MainApp在这里负责整体的“装配”工作。如果这三个组件中的任何一个 发生变化,都将导致MainApp.exe的重新编译和部署。从这个角度来看,似乎“针对接口编程”并没有为我们带来太多的好处。 如果能够将“组件装配”工作抽象出来,我们就可以将MainApp的复杂依赖关系加以简化,从而 进一步实现解耦。为此,我们引入“工厂”模式,并利用配置文件和反射技术,动态加载和装配相关组件。 三、基于配置文件和Reflection的工厂模式 为了消除MainApp对其它组件的依赖性,我们引入工厂模式,并且根据配置文件指定的装配规程,利用.net提供的反射技术完成对象的组装工作。 本部分代码仅仅提供一种功能演示,如果实际应用仍需进一步完善(建议使用一些成型的Ioc框架,例如Spring.net或Castle等)。经过改造后 的系统,组件间依赖关系如下图: 可以看出这次实现了真正的“针对接口编程”。所有的组件只依赖于接口。MainApp所需的对象是由工厂根据配置文件动态创建并组装起来的。当系统 需求发生变化时,只需要修改一下配置文件就可以了。而且MainApp、SayHello和HelloGenerator之间不存在任何的依赖关系,实现 了松耦合。 这是如何实现的呢?我们首先要能够解析配置文件中的信息,然后建立包含相关信息的对象。最后根据这些信息利用反射机制完成对象的创建。首先我们看一下配置文件所包含的内容: 从中我们可以看出,我们实现了一个IocInCSharp.ConfigHandler类,用来处理配置文件中IocInCSharp\ objects结点中的内容。ConfigHandler类将根据该结点下的内容处理并创建一ConfigInfo对象(关于ConfigInfo、 ObjectInfo以及PropertyInfo的代码可自行查看源代码,这里就不再赘述)。ConfigHandler类的代码实现如下: using System; using System.Configuration; using System.Xml; namespace IocInCSharp { public class ConfigHandler:IConfigurationSectionHandler { public object Create(object parent, object configContext, System.Xml.XmlNode section) { ObjectInfo info; PropertyInfo propInfo; ConfigInfo cfgInfo = new ConfigInfo(); foreach(XmlNode node in section.ChildNodes) { info = new ObjectInfo(); info.name = node.Attributes["name"].Value; info.assemblyName = node.Attributes["assembly"].Value; info.typeName = node.Attributes["typeName"].Value; foreach(XmlNode prop in node) { propInfo = new PropertyInfo(); propInfo.propertyName = prop.Attributes["name"].Value; propInfo.assemblyName = prop.Attributes["assembly"].Value; propInfo.typeName = prop.Attributes["typeName"].Value; info.properties.Add(propInfo); } cfgInfo.Objects.Add(info); } return cfgInfo; } } } 通过ConfigHandler的解析,我们最终得到一个ConfigInfo实例,Factory就是根据这个实例中所包含的配置信息,利用反射技术对所需对象生成并组装的。SayHelloFactory的代码如下: using System; using System.IO; using System.Configuration; using System.Reflection; namespace IocInCSharp { public class SayHelloFactory { public static object Create(string name) { Assembly assembly; object o = null; object p; string rootPath = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + Path.DirectorySeparatorChar; ConfigInfo cfgInfo = (ConfigInfo)ConfigurationSettings.GetConfig("IocInCSharp/objects"); ObjectInfo info = cfgInfo.FindByName(name); if(info != null) { assembly = Assembly.LoadFile(rootPath + info.assemblyName); o = assembly.CreateInstance(info.typeName); Type t = o.GetType(); for(int i=0; i错误校验、编写效率比较低。另外它还存在安全隐患 ,恶意用户可以借助修改配置文件将恶意代码注入系统。因此,Spring.net在Web开发中应当更具优势),但这并不能掩盖Spring.net的光 芒(据说Castle比Spring.net要好,但目前我还没有尝试过使用Castle)。使用Spring.net,我们只需修改两三行代码,并提供 相应配置文件,就可以轻松实现Ioc。应用Spring.net后,我们的系统依赖关系如下图所示:   从图中可以看出,MainApp、SayHello、HelloGenerator之间并不存在任何依赖关系,它们都依赖于抽象出来的接口文件。除 此之外,MainApp还依赖于Spring.net,这使得MainApp可以借助Spring.net实现组件动态创建和组装。 对于原有代码,我们几乎不用作任何调整,唯一需要修改的就是MainApp中的调用方法,代码如下: using System; using System.Configuration; using Spring.Context; namespace IocInCSharp { public class MainApp { public static void Main() { try { IApplicationContext ctx = ConfigurationSettings.GetConfig("spring/context") as IApplicationContext; ISayHello sayHello = (ISayHello)ctx.GetObject("mySayHello"); sayHello.SayHelloTo("zhenyulu"); } catch (Exception e) { Console.WriteLine(e); } } } } 首先我们要添加对Spring.Context命名空间的引用,然后解析配置文件的"spring/context"结点,得到一 IApplicationContext对象(就好比在上一个例子中我们得到的ConfigInfo对象一样),剩下的事情就是向该Context索要相 关的对象了(ISayHello)ctx.GetObject("mySayHello"),其中"mySayHello"由配置文件指定生成方式和注入 方式。 配置文件的内容如下: 注意观察结点,就是由这里定义对象间相互依赖关系的。其中的结点定义了对什么属性执行注入,以及注入的内容是什么()。大家可以尝试将改为,看一看程序执行结果有什么变化来体会Spring.net的Ioc功能。 如果读者读到这里仍然觉得Ioc没有什么的话,那让我们再来看一个更为复杂的例子。在当前例子中,MainApp通过依赖注入调用了 HelloGenerator的功能,但所有的调用都发生在本地。当前程序是一个地地道道的本地应用程序。现在如果要求在不更改一行代码的情况下,将 HelloGenerator.dll放到另外一台计算机上,MainApp通过远程调用(Remoting)来访问HelloGenerator的功 能。这似乎就有一定的难度了。 这么作并不是没有任何依据,其实Ioc除了可以实现依赖注入外,我们还应当看到它可以将我们从复杂的物理架构中解脱出来,专心于业务代码的开发。系 统开发中关键是逻辑分层。在一个系统不需要Remoting时,开发的系统就是本地应用程序;当需要Remoting时,不用修改任何代码就可以将系统移 植为分布式系统。Ioc使这一切成为可能。Rod Johnson在他的《J2EE without EJB》一书中有着详细的论述,很值得一读。据说该书的中文译本今年九月份出版(呵呵,就是这个月,不过我还没有看到市面上有卖的)。 为了能够更深入的分析在“Remoting”改造过程中我们可能遇到的麻烦,在后续两部分内容中,我们将分别介绍不使用Ioc的Remoting改造以及使用Ioc的改造,并比较两者之间的区别。 五、使用Remoting对原有系统进行改造 如果使用Remoting技术对HelloGenerator进行改造,使其具有分布式远程访问能力,那么在不使用Ioc技术的情况下,我们将会作出如下调整: (1)让HelloGenerator继承自MarshalByRefObject类 如果要让某个对象具有分布式的功能,必须使其继承自MarshalByRefObject,这样才可以具有远程访问的能力。因此我们需要调整EnHelloGenerator和CnHelloGenerator的代码。这里以EnHelloGenerator为例: using System; namespace IocInCSharp { public class EnHelloGenerator : MarshalByRefObject, IHelloGenerator { public string GetHelloString(string name) { return String.Format("Hello, {0}", name); } } } (2)将修改后的HelloGenerator发布出去 在这一步中,我们创建了一个新的Console应用程序RemotingServer,并在其中注册了一个Channel,发布服务并进行监听。代码如下: using System; using System.Configuration; using System.Collections; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Http; using System.Runtime.Serialization.Formatters; namespace IocInCSharp { public class Server { public static void Main() { int port = Convert.ToInt32(ConfigurationSettings.AppSettings["LocalServerPort"]); try { BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider(); BinaryClientFormatterSinkProvider clientProvider = new BinaryClientFormatterSinkProvider(); serverProvider.TypeFilterLevel = TypeFilterLevel.Full; IDictionary props = new Hashtable(); props["port"] = port; props["timeout"] = 2000; HttpChannel channel = new HttpChannel(props, clientProvider, serverProvider); ChannelServices.RegisterChannel(channel); RemotingConfiguration.RegisterWellKnownServiceType( typeof(EnHelloGenerator), "HelloGenerator.soap", WellKnownObjectMode.Singleton); Console.WriteLine("Server started!\r\nPress ENTER key to stop the server..."); Console.ReadLine(); } catch { Console.WriteLine("Server Start Error!"); } } } } (3)全新的客户端调用代码 为了使得客户端MainApp能够调用到远程的对象,我们需要修改它的代码,注册相应的Channel并进行远程访问。修改后的MainApp代码如下: using System; using System.Configuration; using System.Collections; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Http; using System.Runtime.Serialization.Formatters; namespace IocInCSharp { public class MainApp { public static void Main() { //建立连接 BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider(); BinaryClientFormatterSinkProvider clientProvider = new BinaryClientFormatterSinkProvider(); serverProvider.TypeFilterLevel = TypeFilterLevel.Full; IDictionary props = new Hashtable(); props["port"] = System.Convert.ToInt32(ConfigurationSettings.AppSettings["ClientPort"]); HttpChannel channel = new HttpChannel(props, clientProvider, serverProvider); ChannelServices.RegisterChannel(channel ); //创建远程对象 ISayHello sayHello = new SayHello(); string RemoteServerUrl = ConfigurationSettings.AppSettings["RemoteServerUrl"]; sayHello.HelloGenerator = (IHelloGenerator)Activator.GetObject(typeof(IHelloGenerator), RemoteServerUrl); sayHello.SayHelloTo("zhenyulu"); } } } 在这段代码中,远程对象的创建是通过(IHelloGenerator)Activator.GetObject(typeof(IHelloGenerator), RemoteServerUrl)实现的。到此为止,我们就完成了对原有系统的Remoting改造。 经过调整后的系统,其组件间相互依赖关系如下图所示: 注意ICommon.dll文件在Client和Server端都有。 在整个调整过程中,我们修改了Server端的EnHelloGenerator以及CnHelloGenerator的代码,Client端的 MainApp也 作了修改,以加入了远程访问机制。那么能不能对原有代码不作任何修改就实现远程访问机制呢?当然可以!不过我们还要请出Sping.net帮助我们实现这 一切。 六、利用Ioc在不修改任何原有代码的情况下实现Remoting 上文我们提到,为了实现对HelloGenerator.dll的分布式调用,我们不得不修改了原有程序的多处代码。那么有没有可能在不动任何原有 代码的情况下,单纯靠添加组件、修改配置文件实现远程访问呢?当然可以。这次我们还是使用Spring.net完成这个工作。 经过调整后的系统组件构成如下图所示: 该方案没有修改“src\Step3”中的任何代码,仅仅通过修改配置文件和添加了若干个组件就实现了远程访问。修改方案如下: (1)使用Proxy模式代理原有HelloGenerator 如果要让某个对象具有分布式的功能,必须使其继承自MarshalByRefObject。但是由于不能修改任何原有代码,所以这次我们只能绕道而 行, 借助Proxy模式代理原有的HelloGenerator。在RemotingServer项目中,我们定义了一个新类 HelloGeneratorProxy继承自MarshalByRefObject,通过委派的方式对原有的HelloGenerator进行调用,代 码如下: using System; namespace IocInCSharp { public class HelloGeneratorProxy : MarshalByRefObject, IHelloGenerator { private IHelloGenerator _helloGen; public IHelloGenerator HelloGenerator { get { return _helloGen; } set { _helloGen = value; } } public string GetHelloString(string name) { if(_helloGen != null) return _helloGen.GetHelloString(name); return null; } } } 仔细观察,我们会发现HelloGeneratorProxy持有一个对IHelloGenerator的引用,该属性是可以Set的,因此我们可 以借助Ioc的威力,通过调整Sping.net的配置文件动态决定远程服务器究竟发布EnHelloGenerator还是 CnHelloGenerator。 (2)发布HelloGeneratorProxy 通过RemotingServer.exe,我们将HelloGeneratorProxy发布出去,客户端实际上调用的是Proxy对象(不用担心,由于“针对接口编程”,客户端只知道它是IHelloGenerator类型对象)。服务器端代码如下: using System; using System.Configuration; using System.Collections; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Http; using System.Runtime.Serialization.Formatters; using Spring.Context; namespace IocInCSharp { public class Server { public static void Main() { int port = Convert.ToInt32(ConfigurationSettings.AppSettings["LocalServerPort"]); try { BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider(); BinaryClientFormatterSinkProvider clientProvider = new BinaryClientFormatterSinkProvider(); serverProvider.TypeFilterLevel = TypeFilterLevel.Full; IDictionary props = new Hashtable(); props["port"] = port; props["timeout"] = 2000; HttpChannel channel = new HttpChannel(props, clientProvider, serverProvider); ChannelServices.RegisterChannel(channel); IApplicationContext ctx = ConfigurationSettings.GetConfig("spring/context") as IApplicationContext; HelloGeneratorProxy proxy = (HelloGeneratorProxy)ctx.GetObject("myHelloGeneratorProxy"); RemotingServices.Marshal(proxy, "HelloGenerator.soap"); Console.WriteLine("Server started!\r\nPress ENTER key to stop the server..."); Console.ReadLine(); } catch { Console.WriteLine("Server Start Error!"); } } } } 注意其中的几条命令: IApplicationContext ctx = ConfigurationSettings.GetConfig("spring/context") as IApplicationContext; HelloGeneratorProxy proxy = (HelloGeneratorProxy)ctx.GetObject("myHelloGeneratorProxy"); RemotingServices.Marshal(proxy, "HelloGenerator.soap"); 我们使用Ioc向HelloGeneratorProxy注入具体的HelloGenerator对象,并通过 RemotingServices.Marshal(proxy, "HelloGenerator.soap")命令将该实例发布出去。服务器端的配置文件如下: 用户可以尝试将配置文件中更改为,重新启动服务后看看客户端调用结果是什么? (3)客户端实现技术-1 客户端实现起来要麻烦一些。由于不允许修改MainApp中的任何代码,我们必须能够在合适的时机拦截代码运行并创建远程连接,同时确保在Ioc注 入时注入的是远程对象。所有这些工作Sping.net都考虑的很周到。它提供了depends-on属性,允许在执行某一操作前强制执行某段代码。在客 户端的配置文件中,我们可以看到如下的配置选项: ......... 这表示,当我们初始化mySayHello时,要先去调用ForceInit.dll文件中ForceInit类的Init方法。ForceInit是一个新编写的类,其主要目的就是创建并注册一个用于远程通讯的Channel。代码实现如下: using System; using System.Collections; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Http; using System.Runtime.Serialization.Formatters; namespace IocInCSharp { public class ForceInit { public static void Init() { //建立连接 BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider(); BinaryClientFormatterSinkProvider clientProvider = new BinaryClientFormatterSinkProvider(); serverProvider.TypeFilterLevel = TypeFilterLevel.Full; IDictionary props = new Hashtable(); props["port"] = 8199; props["name"] = "myHttp"; HttpChannel channel = new HttpChannel(props, clientProvider, serverProvider); //获得当前已注册的通道; IChannel[] channels = ChannelServices.RegisteredChannels; //关闭指定名为MyHttp的通道; foreach (IChannel eachChannel in channels) if (eachChannel.ChannelName == "myHttp") ChannelServices.UnregisterChannel(eachChannel); ChannelServices.RegisterChannel(channel); } } } (4)客户端实现技术-2 剩下的工作就是为mySayHello的HelloGenerator注入远程对象。通常情况下我们需要使用 Activator.GetObject方法调用远程对象,不过Spring.net已经将其封装起来,我们只需修改一下配置文件,就可以确保调用到远程 对象。配置文件对应部分如下: ...... IocInCSharp.IHelloGenerator, ICommon http://127.0.0.1:8100/HelloGenerator.soap 借助Spring.Remoting.SaoFactoryObject,我们轻松实现了远程对象访问,不必书写一行代码。(目前SAO在 Spring.net的实现尚不完整,按照Spring.net帮助手册上的做法,通过配置文件只能实现客户端访问远程对象,还做不到服务器端发布远程对 象) (5)使用AOP拦截调用 Sping.net目前已经实现AOP功能,我们可以很容易的对方法进行拦截和调用。需要做的工作就是设计相应的Interceptor,然后修改 配置文件。目前Sping.net使用的AOP功能是AopAlliance的实现,因此代码编写时命名空间引用让人感觉多少有些别扭,不是以Sping 开头。我编写的MethodInterceptor代码如下: using System; using AopAlliance.Intercept; namespace IocInCSharp { class MethodInterceptor : IMethodInterceptor { public object Invoke(IMethodInvocation invocation) { Console.WriteLine("Before Method Call..."); object returnValue = invocation.Proceed(); Console.WriteLine("After Method Call..."); return returnValue; } } } 在方法调用前打印"Before Method Call...",在方法调用后打印"After Method Call..."。剩下的工作就是修改配置文件,将其应用到相应的方法上。配置文件片断如下: ...... MethodAdvice 通过以上操作,我们在没有修改任何原有代码的情况下,让原有系统实现了远程分布式访问。 请大家访问示例代码的“bin\Step5"目录,下面有3个子目录:Server、Client、WithoutRemoting。首先运行 Server目录下的RemotingServer.exe,然后运行Client目录下的MainApp.exe进行远程调用。系统通过 Remoting完成远程调用。关闭所有程序后,进入到WithoutRemoting目录,里面有个Readme.txt文件,按照操作步骤将文件: ..\Server\HelloGenerator.dll ..\Client\MainApp.exe ..\Client\ICommon.dll ..\Client\SayHello.dll ..\Client\Spring.Core.dll ..\Client\log4net.dll   拷贝到该目录,再次运行MainApp.exe,你会发现它是一个地地道道的本地应用程序!本地与远程唯一的区别就是配置文件的不同以及增加了几个其它的DLL。这正式我们这个示例的价值体现。 到此为止,我们完成了对Ioc应用的一系列模拟。Ioc写得多一些,AOP写得少了点。欢迎大家批评指正。

62,041

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术交流专区
javascript云原生 企业社区
社区管理员
  • ASP.NET
  • .Net开发者社区
  • R小R
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

.NET 社区是一个围绕开源 .NET 的开放、热情、创新、包容的技术社区。社区致力于为广大 .NET 爱好者提供一个良好的知识共享、协同互助的 .NET 技术交流环境。我们尊重不同意见,支持健康理性的辩论和互动,反对歧视和攻击。

希望和大家一起共同营造一个活跃、友好的社区氛围。

试试用AI创作助手写篇文章吧