在 Web 应用程序中处理数据库更新的两种方法
Microsoft Corporation
Prajakta Joshi
2002 年 10 月 8 日
摘要:特邀作家 Prajakta Joshi 讨论了如何使用 .NET 框架 SDK 中的 System.Xml API 为 XPath 创建自定义函数。主题涉及向 XPath 1.0 中添加扩展函数、展望 XPath 2.0 以及使用 XSLT 中的扩展函数。(14 页打印页)
在 XML和XSL公共新闻组中,对扩展函数 的请求是一个经常讨论的主题。撰写本文的动机是因为我注意到有大量的用户帖子涉及该主题。 XPath 1.0中的 XPath 表达式能够返回以下四个基本 XPath 数据类型之一:
XSLT 变量向表达式语言中引入了一个附加类型 — result tree fragment(结果树片段)。
XPath 中的核心函数库和其他几个 XSLT 特定的附加函数提供了几个用来操作 XPath 数据类型的基本工具。纵观这些函数,您会发现这不是一个能够满足所有用户需要的完善集合。
节点集 |
last()、position()、count()、id()、local-name()、namespace-uri()、name() |
字符串 |
string()、concat()、starts-with()、contains()、substring-before()、substring-after()、substring()、string-length()、normalize-space()、translate() |
布尔值 |
boolean()、not()、true()、false()、lang() |
数值 |
number()、sum()、floor()、ceiling()、round() |
XSLT 1.0 中的新增函数 |
document()、key()、format-number()、current()、unparsed-entity-uri()、generate-id()、system-property() |
需要操作非 XPath 数据类型(例如,日期)或者用 XPath 数据类型执行功能强大的/自定义数据操作的 XML 开发人员通常需要额外的函数。本文旨在概述如何使用 Microsoft .NET 框架 SDK 中的 System.XmlAPI 来为 XPath 实现自定义函数。
两个字符串的比较
在编写我的第一个 XPath 查询时,我需要对两个字符串执行不区分大小写的比较。我编写的 books.xml 如下所示:
<bookstore xmlns:my="urn:http//mycompany.com/">
<book style="young adult">
<title>Harry Potter and the Goblet of Fire</title>
<author>
<first-name>Mary</first-name>
<last-name>Gradpre</last-name>
</author>
<my:price>8.99</my:price>
</book>
<book style="young fiction">
<title>Lord of the Rings</title>
<author>
<first-name>J.</first-name>
<last-name>Tolkien</last-name>
</author>
<my:price>22.50</my:price>
</book>
</bookstore>
我寻找到一个类似于 XPath 1.0 字符串函数 中的 String.Compare()的函数。可用在我的解决方案中的最接近的函数是 translate()。我通过以下代码片段使用 System.Xml.XPath命名空间中的 XPath 类解决了该问题:
using System;
using System.Xml;
using System.Xml.XPath;
public class sample
{
public static void Main(string []args)
{
// Load source XML into XPathDocument.
XPathDocument xd = new XPathDocument(args[0], XmlSpace.Preserve);
// Create XPathNavigator from XPathDocument.
XPathNavigator nav = xd.CreateNavigator();
XPathExpression expr;
expr =
nav.Compile("/bookstore/book/title[translate(.,'abcdefghijklmnopqrstuvwxyz',
'ABCDEFGHIJKLMNOPQRSTUVWXYZ') = 'HARRY POTTER AND THE GOBLET OF FIRE']");
XPathNodeIterator iterator = nav.Select(expr);
// Iterate through selected nodes.
while (iterator.MoveNext())
{
Console.WriteLine("Book title: {0}", iterator.Current.Value);
}
}
}
此代码生成如下输出结果:
Book title: Harry Potter and the Goblet of Fire
尽管此解决方案有点长,但我还是解决了我的问题。在几天之后编写 XPath 查询时,我要在由正则表达式匹配定义的位置将字符串拆分成子字符串的数组。
<?xml version="1.0" encoding="utf-8" ?>
<Books>
<Book>
<Title>Stephen Hawking's Universe: The Cosmos Explained</Title>
<Authors>David Filkin, Stephen Hawking</Authors>
</Book>
<Book>
<Title>Writing Secure code</Title>
<Authors>Michael Howard, David LeBlanc</Authors>
</Book>
</Books>
我要从 <Authors> 元素的逗号分隔列表中找出第 n 个作者的姓名 — 一个有点复杂的字符串操作问题。我认为这是一个学习如何实现 XPath 自定义扩展函数的好机会。
实现 XPath 中的扩展函数
我发现 XPath 1.0 建议没有为扩展函数定义机制。但好消息是,我可以为 XPath 处理器提供一个自定义执行上下文,以便解析 XPath 表达式中用户定义的函数和变量。
下图解释了我的解决方案内 System.Xml.Xsl 命名空间中 XsltContext 类、IXsltContextFunction 接口和 IXsltContextVariable接口的角色。
图 1. XsltContext 的角色
解决方案中的关键步骤
• |
1.XPathExpression.SetContext(CustomContext) 提供一个 XPath 处理器 (XPathNavigator),它具有用来解析用户定义的函数和变量的自定义上下文。CustomContext(从抽象类 XsltContext 派生)实现两个关键方法:ResolveFunction() 和 ResolveVariable()。 |
• |
2.当 XPathNavigator 在 XPathExpression 中遇到用户定义的函数时,它会针对自定义上下文调用 ResolveFunction() 方法。ResolveFunction() 返回从 IXsltContextFunction 派生的适当自定义函数。 |
• |
3.XPathNavigator 在运行时使用所提供的参数针对这个自定义函数调用 Invoke() 方法。 |
• |
4.当 XPathNavigator 在 XPathExpression 中检测到用户定义的变量时,它会针对自定义上下文调用 ResolveVariable() 方法。ResolveVariable() 返回从 IXsltContextVariable 派生的适当的自定义变量。 |
• |
5.XPathNavigator 在运行时针对这个自定义变量调用 Evaluate() 方法。 |
我决定编写一个自定义的 XPath 函数 — Split(),使该函数的行为类似于 .NET SDK 中的 RegEx.Split()方法。下面介绍如何将所有这些代码片段合并在一起。
XsltContext 类的角色
首先,我实现了我的自定义 XsltContext,以便给 XPath 处理器提供有关解析用户定义的函数所需的信息。ResolveFunction和ResolveVariable是 XsltContext 类的两个关键方法,用户必须重写它们才能实现自定义解析。这些方法在运行时由 XPathNavigator调用,以便解析对 XPath 查询表达式中用户定义的函数和变量的引用。
请注意,我在 CustomContext 类中封装了一个 ResolveVariable对象。此对象是 XPath 表达式中的变量的容器。
public class CustomContext : XsltContext
{
// XsltArgumentList to store my user defined variables
private XsltArgumentList m_ArgList;
// Constructors
public CustomContext()
{}
public CustomContext(NameTable nt) : base(nt)
{
}
public CustomContext(NameTable nt, XsltArgumentList argList) : base(nt)
{
m_ArgList = argList;
}
// Returns the XsltArgumentList that contains custom variable definitions.
public XsltArgumentList ArgList
{
get
{
return m_ArgList;
}
}
// Function to resolve references to my custom functions.
public override IXsltContextFunction ResolveFunction(string prefix,
string name, XPathResultType[] ArgTypes)
{
XPathRegExExtensionFunction func = null;
// Create an instance of appropriate extension function class.
switch (name)
{
case "Split":
// Usage
// myFunctions:Split(string source, string Regex_pattern, int n) returns string
func = new XPathRegExExtensionFunction("Split", 3, 3, new
XPathResultType[] {XPathResultType.String, XPathResultType.String,
XPathResultType.Number}, XPathResultType.String);
break;
case "Replace":
// Usage
// myFunctions:Replace(string source, string Regex_pattern,
string replacement_string) returns string
func = new XPathRegExExtensionFunction("Replace", 3, 3, new
XPathResultType[] {XPathResultType.String, XPathResultType.String,
XPathResultType.String}, XPathResultType.String);
break;
}
return func;
}
// Function to resolve references to my custom variables.
public override IXsltContextVariable ResolveVariable(string prefix, string name)
{
// Create an instance of an XPathExtensionVariable.
XPathExtensionVariable Var;
var = new XPathExtensionVariable(name);
return Var;
}
public override int CompareDocument(string baseUri, string nextbaseUri)
{
return 0;
}
public override bool PreserveWhitespace(XPathNavigator node)
{
return true;
}
public override bool Whitespace
{
get
{
return true;
}
}
}
IXsltContextFunction 接口的角色
下一步是实现供 CustomContext 类使用的
IXsltContextFunction
接口。此对象的
Invoke()
方法在运行时由 XPathNavigator利用所提供的参数进行调用。
public class XPathRegExExtensionFunction : IXsltContextFunction
{
private XPathResultType[] m_ArgTypes;
private XPathResultType m_ReturnType;
private string m_FunctionName;
private int m_MinArgs;
private int m_MaxArgs;
// Methods to access the private fields.
public int Minargs
{
get
{
return m_MinArgs;
}
}
public int Maxargs
{
get
{
return m_MaxArgs;
}
}
public XPathResultType[] ArgTypes
{
get
{
return m_ArgTypes;
}
}
public XPathResultType ReturnType
{
get
{
return m_ReturnType;
}
}
// Constructor
public XPathRegExExtensionFunction(string name, int minArgs, int
maxArgs, XPathResultType[] argTypes, XPathResultType returnType)
{
m_FunctionName = name;
m_MinArgs = minArgs;
m_MaxArgs = maxArgs;
m_ArgTypes = argTypes;
m_ReturnType = returnType;
}
// This method is invoked at run time to execute the user defined function.
public object Invoke(XsltContext xsltContext, object[] args,
XPathNavigator docContext)
{
Regex r;
string str = null;
// The two custom XPath extension functions
switch (m_FunctionName)
{
case "Split":
r = new Regex(args[1].ToString());
string [] s1 = r.Split(args[0].ToString());
int n = Convert.ToInt32(args[2]);
if (s1.Length < n)
str = "";
else
str = s1[n - 1];
break;
case "Replace":
r = new Regex(args[1].ToString());
string s2 = r.Replace(args[0].ToString(), args[2].ToString());
str = s2;
break;
}
return (object) str;
}
}
IXsltContextVariable 接口的角色
XPath 表达式中可以包含用户定义的变量引用,例如:
XPathExpression expr1 = nav.Compile("myFunctions:Split(string(.), ',',
$var
)");
我需要实现 IXsltContextVariable接口并重写 Evaluate()方法(此方法在运行时
public class XPathExtensionVariable : IXsltContextVariable
{
// The name of the user-defined variable to resolve
private string m_VarName;
public XPathExtensionVariable(string VarName)
{
m_VarName = VarName;
}
// This method is invoked at run time to find the value of the user defined variable.
public object Evaluate(XsltContext xsltContext)
{
XsltArgumentList vars = ((CustomContext) xsltContext).ArgList;
return vars.GetParam(m_VarName, null);
}
public bool IsLocal
{
get
{
return false;
}
}
public bool IsParam
{
get
{
return false;
}
}
public XPathResultType VariableType
{
get
{
return XPathResultType.Any;
}
}
}
把代码合并在一起
最后,我使用了 XPathExpression.SetContext()方法,将它传入我的自定义上下文对象。图 1 汇总了该解决方案中的所有步骤。请注意,XsltContext 类是从 XmlNamespaceManager中继承而来的,而且通过使用 AddNamespace()向集合中添加了我的自定义命名空间。
using System;
using System.Xml;
using System.Xml.Xsl;
using System.Xml.XPath;
using System.Text.RegularExpressions;
public class sample
{
public static void Main(string []argc)
{
// Load source XML into XPathDocument.
XPathDocument doc = new XPathDocument("books.xml", XmlSpace.Preserve);
// Create XPathNavigator from XPathDocument.
XPathNavigator nav = doc.CreateNavigator();
// Add user-defined variable to the XsltArgumentList.
XsltArgumentList varList = new XsltArgumentList();
varList.AddParam("var", "", 2);
// Compile the XPathExpression.
// Note that the compilation step only checks the query expression
// for correct XPath syntax.
// User defined functions and variables are not resolved.
XPathExpression expr1 = nav.Compile("myFunctions:Split(string(.), ',', $var)");
// Create an instance of a custom XsltContext object.
CustomContext cntxt = new CustomContext(new NameTable(), varList);
// Add a namespace definition for myFunctions prefix.
cntxt.AddNamespace("myFunctions", "http://myXPathExtensionFunctions");
// Associate the custom context with the XPathExpression object.
expr1.SetContext(cntxt);
XPathNodeIterator it = nav.Select("/Books/Book/Authors");
while (it.MoveNext())
{
Console.WriteLine("Authors: {0}", it.Current.Value);
Console.WriteLine("Second author: {0}", it.Current.Evaluate(expr1));
}
}
}
运行此代码会生成如下输出结果:
Authors: David Filkin, Stephen Hawking
Second author: Stephen Hawking
Authors: Michael Howard, David LeBlanc
Second author: David LeBlanc
扩展函数的其他用法
在我发现了使用扩展函数的机制之后,在用 XPath编程时,我在其他许多数据操作的情况下都使用了扩展函数。一些其他情况包括:
这种情况数不胜数。根据您的具体情况,可以在您的自定义函数中使用任何 .NET 类。
未来的方向:XPath 2.0
由于 W3C XML 架构数据类型日益与 XPath 2.0集成,因此,Xquery 1.0 和 XPath 2.0 函数和运算符将为 XML 开发人员提供一个比当前存在于 XPath 1.0 中的函数更为丰富的函数库。这不会完全消除 XPath 2.0 对用户定义的函数的需要。对于 XML 的功能强大、可扩展的查询语言来说,扩展的机制将是不可或缺的。
XSLT 中的扩展函数
XSLT 1.0 的XslTransform实现将命名空间 urn:schemas-microsoft-com:xslt 用作扩展命名空间。它具有对 <msxsl:node-set>扩展函数和 <msxsl:script>扩展元素的内置支持。
在 XSLT 中,可通过两种方法来实现用户定义的函数:
2004年11月05日 #
2004年11月04日 #
本来今天打算介绍Updater Application Block的,后来想想一些朋友经常反馈说用将UAB集成到自己开发的应用程序中太麻烦了,询问有没有更简单的方法布署智能客户端。在Whidbey的ClickOnce出来之前,答案当然还是有的,那就是使用AppUpdater组件。在美国举行的TechED 2004上介绍的IssueVision,TaskVision这些经典的Smart Client Demo用的都是这个组件来实现智能客户端的布署。
使用AppUpdater组件要用到下面这些文件:大家可以在http://windowsforms.net/downloads/GDN/dotnetupdater.zip 中下载
文件名 功能描述
AppStart.exe 类似中介代理的程序,由它来启动真正的应用程序。不直接启动主应用程序是为了防止主应用程序升级的过程中被锁死而造成升级失败。
AppStart.config AppStart.exe的配置文件,它指定要启动的应用程序所在目录,以及要启动应用程序的名称
appupdater.dll 最重要的部件,自动升级的主要组件
system.Resources.dll 资源文件
mscorlib.Resources.dll 核心资源文件
UpdateVersion.xml 服务器端版本升级配置文件
OK,开始介绍如何使用它吧。
(1) .在Custom Control中导入AppUpdater
(2).配置自动下载选项
AutoFileLoad :True
ChangeDetectionMode ServerManifestCheck
UpdateUrl http://YourServerDomainName/SmartServer/UpdateVersion.xml
ShowDefaultUI: True
AutoFileLoad 可以让 服务器上的文件动态加载,大家可以把它想像成 Un-Touch 部署中的Assembly.LoadFrom类似的功能
ChangeDetectonMode是设置检测模式:根据配置文件进行检测,也就是UpdateUrl属性指定的文件。如果设成DirectFileCheck则是比较每一个文件的时间戳来决定要不要下载这个文件。
UpdaterUrl指服务器上的更新配置文件,其中UpdateVersion.xml内容如下:
<VersionConfig>
<AvailableVersion>2.0.0.0</AvailableVersion>
<ApplicationUrl>http://YourServerDomainName/SmartServer/Ver/</ApplicationUrl>
</VersionConfig>
UpdateVersion.xml解释:
<AvailableVersion>2.0.0.0</AvailableVersion>
告诉客户端目前可用的版本,客户端appupdater组件会比较本地主应用程序版本号和该项配置的版本号,如果比本地的版本号更高,则进行下载更新。
<ApplicationUrl>http://YourServerDomainName/SmartServer/Ver/</ApplicationUrl>
告诉客户端到哪个网址进行下载更新
建议大家把这个属性设置成Dynamic,即从配置文件中读出来,不过,要让服务器的.config文件允许被下载,我们还要在IIS中 ASP.net应用程序”配置“中对它进行设置,默认是不允许访问的。
ShowDefaultUI表示下载结束时用一个简单的界面提示你要不要启动新版本。你还可以用自己的窗体显示,方法是设成False,并在OnUpdateComplete事件中写代码把你的窗体显示出来。
当然,还有一些其他的属性,比如,是否要使用公钥(可以是一个,也可以提供一个Assembly,里面包括多个公钥)验证要下载的文件。由于时间关系,我就不多说了。
(3) 使用AppStart.exe启动实际的应用程序。
(4) 在IIS把下载文件夹设成允许目录浏览,因为AppUpdater要枚举里面的所有文件,并把它们都下载到本地。如果是Windwos 2003,麻烦一点,要允许WebDAV,并把.config文件设置成允许下载。
(5) 做个msi安装文件,打包应用程序,这样,当应用程序运行时,它就能自动判断有无最新版本,并下载到本地运行。
可以直接访问:http://www.microsoft.com/china/msdn/events/featureevents/2004/MSDevTrainingCourse.mspx
大家如果对Webcast的内容有什么问题,可以直接和我联系,我会尽快解答。
希望大家多多支持中文MSDN站点。
下次我可能会把VS.net团队开发的内容放上去。
1、Windows窗体应用程序开发
2、Delegates and Events
3、XML与SOAP
4、.NET数据库高级应用:数据缓存;连接池管理;分布式事务
5、WEB Service高级应用
6、NET中的I/O管理:Data Streams and Fils
7、ADO.NET访问Oracle数据库
8、.NET 组件开发
9、.NET序列化
10、与非托管代码交互操作
11、.NET Remoting
1、Windows窗体应用程序开发
VS.NET IDE是一个高度可视化,集成和高效的开发环境。她能提供设计、开发、调试和部署Windows应用程序和Web应用程序所需的各种工具。比起VS6.0,她又提供了许多功能强大的组件并采用了很多新的特性,使得开发人员能够轻松简单高效的创建Windows窗体应用程序。
本讲座主要介绍VS.NET2003的新控件和新特性,包括ContextMenu, ToolTip, Splitter, NotifyIcon在内的一系列新控件的使用方法和诸如Anchoring,Docking等一些新的特性。并讨论了如何在VS.NET中集成原有的ActiveX控件,如何进行可视化的继承和怎样开发多文档界面的应用程序。
下载
返回页首
2、Delegates and Events
Microsoft .NET框架使用一种称为Delegate代理的技术来提供回调函数机制,从而极大地增加了应用程序的灵活性。同时,代理还集成了按序调用多个方法的能力,并且同时支持调用静态方法和实例方法。
本讲座详细解释了代理实现的机制及如何声明、实例化和使用代理,并在此基础上引入了单播代理和多播代理、代理链的概念。最后,介绍了事件的实现机制。
下载
返回页首
3、XML与SOAP
XML是一种具有良好开放性、简单性和可扩展性的语言,它能够用来描述非结构化的数据,目前现在广泛的应用于企业应用集成、电子商务和数据交换领域。XML与HTTP是SOAP简单对象访问协议的基础。SOAP主要用于基于Internet环境下的远程过程调用,它使用HTTP协议进行传输,因此较好的解决了跨越企业防火墙的问题,
本讲座主要介绍了XML的概念,语法,XML Schema,XML文档的定义和验证,XML的解析器,XSLT扩展样式语言、XPath等一系列XML相关的内容,并在此基础上简要的介绍了SOAP协议及其实现过程。
下载
返回页首
4、.NET数据库高级应用:数据缓存;连接池管理;分布式事务
数据库访问的性能总是开发人员非常关心的问题。本讲座主要介绍了使用ADO.NET访问数据库的高级应用技术,包括ADO.NET中的连接池管理,各种策略的数据缓存,两种在.NET中实现事务的机制,并讨论了如何实现分布式事务以及优缺点。
下载
返回页首
5、WEB Service高级应用
Web Service是一种新型的Web应用程序。它是自适应、自我描述、模块化的应用程序,这些应用程序可以跨越Web进行发部、定位和调用。Web Service所执行的功能可以是从简单的请求到复杂的商业过程中的任何事。简单的Web Service可以提供股票报价或处理信用卡交易。一旦部署了 Web Service,其它的应用程序可以发现和调用该Web Service。Web Serivce的开发涉及到相当多的技术,包括XML、SOAP、WSDL、UDDI等。
本讲座深入浅出的介绍了Web Service概述和体系结构,如何开发和使用Web Service,以及如何使用Web Service作为数据访问层组件的数据提供者返回复杂的数据类型。最后介绍了Web Service高级开发技术,Web Service Enhancement相关的内容,包括如何对SOAP消息进行加密、签名等。
下载
返回页首
6、NET中的I/O管理:Data Streams and Fils
.NET很好的封装了与数据流和文件操作有关的类,如Stream、FileStream、File、FileInfo等类,并提供了诸如FileSystemWatcher文件系统监控器组件来对文件进行实时的监控与管理。
本讲座主要介绍了.NET中与数据流和文件操作相关的内容,首先介绍了流的概念,接着介绍了如何使用Reader和Writer对象来操作流。最后结合实例对.NET中与文件操作相关的一些基本的类和组件及其使用方法进行了阐述。
下载
返回页首
7、ADO.NET访问Oracle数据库
在ADO.NET中访问Oracle数据库基本的原理与访问SQL Server数据库相同,这个得益于一个专门为Oracle数据库开发的Microsoft .NET Framework Data Provider for Oracle组件,该组件为我们使用.NET访问Oracle数据库提供了极大的方便。 使得那些使用.NET和Oracle的开发人员再也不必使用那个并不十分“专业"的OLEDB来访问Oracle数据库了。
本讲座主要介绍了如何使用Microsoft .NET Framework Data Provider for Oracle组件提供的OracleConnection、OracleCommand、OracleDataReader对象来操作Oracle数据库,并重点介绍了如何访问Oracle数据库中的特殊数据类型以及如何执行Oracle中的储存过程,这个与执行SQL Server的存储过程之间存在相当大的不同。
下载
返回页首
8、.NET 组件开发
组件的开发提供了代码的可重用性,使得应用程序的开发可以像搭积木一样简单和方便。在.NET下可以方便的开发出组件,并可以将其用在控制台应用程序、Windows应用程序、Web应用程序甚至是SmartPhone、Pocket PC上。
本讲座在介绍了.NET Framework开发技术的基础上结合实例介绍了创建和使用.NET组件的步骤和方法。
下载
返回页首
9、.NET序列化
序列化是一个把类的实例转换成一个文件或XML格式的数据流的过程,反序列化则相反。.NET对于序列化的操作进行很好的封装,在.NET下能够非常方便把一个对象序列化成文件或者XML文件。
本讲座主要介绍了序列化的概念、属性,如何把一个或一组具有层次结构的对象序列化成文件,解释了序列化的过程,并举例说明如何执行序列化和反序列化,在讲座的最后,讨论了与序列化相关的安全性的问题。
下载
返回页首
10、与非托管代码交互操作
传统的COM组件和Windows32 API在.NET平台下过时了吗?回答是否定的。许多现存的COM组件和Win32 API在.NET代码中仍然是可用的宝贵资源,.NET对访问这些资源提供了一系列支持,使得这些组件不必重写就能应用于.NET应用程序中。
本讲座全面的介绍了在.NET中调用COM组件和非托管的DLL函数的原理及机制,特别阐述了如何传递结构、类等参数和DLLImport属性的重要选项,并演示了如何向.NET公开COM组件和使用Win32 API。
下载
返回页首
11、.NET Remoting
.NET Remoting提供了一种允许对象通过应用程序域与另一对象进行交互的框架。通过.NET Remoting,两个应用间的通信将变得非常简单,使用上也相当灵活。
本讲座由浅入深的介绍了.NET Remoting的开发技术,首先介绍了Remoting技术的一些基本概念,如远程对象、序列化、客户端和服务器端的激活模式等,其次,详细解释了Remoting的实现机制以及如何使用Remoting技术来设计和实现分布式应用程序,最后通过一个具体的Demo来展示在.NET中实现远程通信是多么的简单。
下载
2004年11月03日 #
异常管理应用程序块Exception Management Application Block
异常管理应用程序块提供了管理应用程序异常的灵活而又简单的方法。先来看一个最简单使用EMAB的步骤:
(1)添加组件引用:using Microsoft.ApplicationBlocks.ExceptionManagement;
(2)在捕获异常的时候调用异常管理
try
{
throw new Exception ("测试一个异常");
}
catch(Exception err)
{
ExceptionManager.Publish (err);
MessageBox.Show (err.Message );
}
异常err的信息此时被自动记录到系统事件日志中。
就这么简单!
ExceptionManager.Publish执行的时候,首先会到app.config(或者web.config/machine.config)文件里查
找mode属性设置成on的<exceptionManagement>节点,如果没有设置信息,它会调用默认的异常发布类(发布到事
件日志中)。所以,我们使用上面最简单的发布异常的例子里,我们没有必要在配置文件里做任何配置。
当然,我们也可以显式地在app.config文件中声明使用默认异常发布类:
配置文件的格式如下:
<configuration>
<configSections>
<section name="exceptionManagement"
type="Microsoft.ApplicationBlocks.ExceptionManagement
.ExceptionManagerSectionHandler,
Microsoft.ApplicationBlocks.ExceptionManagement" />
</configSections>
<exceptionManagement mode="on/off">
<publisher mode="on/off" assembly="AssemblyName" type="TypeName"
exclude="(+)Type;(+)Type" include="(+)Type;(+)Type"
exceptionFormat="value" /*xml or others*/
customattr = "value" />
</exceptionManagement>
</configuration>
我们来解释一下:
<configSections>
<section name="exceptionManagement"
type="Microsoft.ApplicationBlocks.ExceptionManagement.ExceptionManagerSectionHandler,Microsoft.Ap
plicationBlocks.ExceptionManagement" />
</configSections>
上面这个配置表示使用ExceptionManagerSectionHander类来读取exceptionManagement节点注册的Publisher类
。
<exceptionManagement mode="on">
<publisher assembly="Microsoft.ApplicationBlocks.ExceptionManagement"
type="Microsoft.ApplicationBlocks.ExceptionManagement.DefaultPublisher" logname="日志文件名"
applicationname="应用程序名" />
</exceptionManagement>
这个配置信息表示将默认异常发布类注册成应用程序所使用的异常发布类:发布到事件日志的"日志文件名"文
件里(如果不设logname,则使用"应用程序”日志文件)。
我们可以自定义(一个或多个)异常发布类,并把它注册到app.config中,自定义Publisher类主要是要实现IExceptionPublisher接口的Publish方法。这也是很简单的,大家可以查看Exception Management Application block自带的Sample.
下一步就是把一个或多个异常发布类注册到配置文件中:
<exceptionManagement mode="on">
<publisher assembly="ExceptionManagementQuickStartSamples" type="ExceptionManagementQuickStartSamples.ExceptionPublisher" exclude="*" include="ExceptionManagementQuickStartSamples.LogonException, ExceptionManagementQuickStartSamples; ExceptionManagementQuickStartSamples.CustomAppException, ExceptionManagementQuickStartSamples" operatorMail="[email protected]"/>
<publisher assembly="ExceptionManagementQuickStartSamples" type="ExceptionManagementQuickStartSamples.ExceptionXMLPublisher" exclude="*" include="+Microsoft.ApplicationBlocks.ExceptionManagement.BaseApplicationException, Microsoft.ApplicationBlocks.ExceptionManagement" exceptionFormat="xml" fileName="c:\QuickStartSamplesExceptionLog.xml"/>
</exceptionManagement>
OK,就介绍到这吧。
2004年11月02日 #
如果要使用SqlHelper执行多个SqlCommand并将结果填到同一个DataSet的多个表中,除了在上一篇文章介绍过的使用 ExecuteDataset这个函数以外,还可以使用另一个功能强大的函数:FillDataSet,并且这个方法比ExecuteDataSet更为灵活,比如,您可以指定填充的表的名字,可以在多表之间建立TableRelation,等等。
下面给出一段示例代码:
DataSet ds=new DataSet ();
SqlHelper.FillDataset (con,CommandType.Text ,"select * from Customers",ds,new string []{"Customers"});
dataGrid1.DataSource =ds.Tables ["Customers"];
SqlHelper.FillDataset (con,CommandType.Text ,"select * from Orders",ds,new string []{"Orders"});
dataGrid2.DataSource =ds.Tables ["Orders"];
下面是FillDataSet的签名描述:
Executes a command by using a connection string
[C#] FillDataset(string connectionString, CommandType commandType,
string commandText, DataSet dataset, string[] tableNames)
Executes a command by using a connection string and an array of SqlParameters
[C#] FillDataset(string connectionString, CommandType commandType,
string commandText, DataSet dataSet, string[] tableNames,
params SqlParameter[] commandParameters)
Executes a stored procedure by using a connection string
[C#] FillDataset(string connectionString, string spName, DataSet dataSet,
string[] tableNames, params object[] parameterValues)
Executes a command by using a SqlConnection object
[C#] FillDataset(SqlConnection connection, CommandType commandType,
string commandText, DataSet dataSet, string[] tableNames)
Executes a command by using a SqlConnection and an array of SqlParameters
[C#] FillDataset(SqlConnection connection, CommandType commandType,
string commandText, DataSet dataSet, string[] tableNames,
params SqlParameter[] commandParameters)
Executes a stored procedure by using a SqlConnection object
[C#] FillDataset(SqlConnection connection, string spName,
DataSet dataSet, string[] tableNames, params object[] parameterValues)
Executes a command by using a SqlTransaction
[C#] FillDataset(SqlTransaction transaction, CommandType commandType,
string commandText, DataSet dataSet, string[] tableNames)
Executes a command by using a SqlTransaction and SqlParameters
[C#] FillDataset(SqlTransaction transaction, CommandType commandType,
string commandText, DataSet dataSet, string[] tableNames,
params SqlParameter[] commandParameters)
Executes a stored procedure by using a SqlTransaction
[C#] FillDataset(SqlTransaction transaction,
string spName, DataSet dataSet, string[] tableNames,
params object[] parameterValues)
Data Access Application Block 将访问 Microsoft SQL Server™ 数据库的性能和资源管理方面的最佳经验封装在一起。您可以很方便地在自己的 .NET 应用程序中将其作为构造块使用,从页减少了需要创建、测试和维护的自定义代码的数量。
个人认为,DAAB最大的好处之一在于DAAB帮助开发者自动管理了数据库的连接. Connection对象是数据库访问组件中最为消耗资源的,如果没有使用using{}关键字或Dispose方法强制释放连接资源,connection对象要等到Garbage Collector启动后才能回收,而如果使用数据访问应用构造块的话,开发者完全不用处理资源的回收问题。
Data Access Application Block 可以帮助您:
调用存储过程或 SQL 文本命令。
指定参数详细信息。
返回 SqlDataReader、DataSet 或 XmlReader 对象。
例如,在引用了 Data Access Application Block 的应用程序中,您可以简单地在一行代码中调用存储过程并生成 DataSet,如下所示:
[Visual Basic]
Dim ds As DataSet = SqlHelper.ExecuteDataset( _
connectionString, _
CommandType.StoredProcedure, _
"getProductsByCategory", _
new SqlParameter("@CategoryID", categoryID))
[C#]
DataSet ds = SqlHelper.ExecuteDataset(
connectionString,
CommandType.StoredProcedure,
"getProductsByCategory",
new SqlParameter("@CategoryID", categoryID));
Microsoft.ApplicationBlocks.Data.dll 程序集包括一个 SqlHelper 类(其中包含用于执行数据库命令的核心功能)和一个 SqlhelperParameterCache 类(提供参数发现和缓存功能)。
SqlHelper 类提供了一组静态方法,可以用来向 SQL Server 数据库发出许多各种不同类型的命令。
SqlHelperParameterCache 类提供命令参数缓存功能,可以用来提高性能。该类由许多 Execute 方法(尤其是那些只运行存储过程的重写方法)在内部使用。数据访问客户端也可以直接使用它来缓存特定命令的特定参数集。
SqlHelper 类提供了六种 Shared (Visual Basic) 或 static (C#) 方法,它们是:ExecuteNonQuery、ExecuteDataset、ExecuteReader、ExecuteScalar 和 ExecuteXmlReader。实现的每种方法都提供一组一致的重载。这提供了一种很好的使用 SqlHelper 类来执行命令的模式,同时为开发人员选择访问数据的方式提供了必要的灵活性。每种方法的重载都支持不同的方法参数,因此开发人员可以确定传递连接、事务和参数信息的方式。类中实现的所有方法都支持以下重载:
[Visual Basic]
Execute* (ByVal connection As SqlConnection, _
ByVal commandType As CommandType, _
ByVal CommandText As String)
Execute* (ByVal connection As SqlConnection, _
ByVal commandType As CommandType, _
ByVal commandText As String, _
ByVal ParamArray commandParameters() As SqlParameter) //VB.net 里的ParamArray相当于 C#里的params关键字,即参数数组
Execute* (ByVal connection As SqlConnection, _
ByVal spName As String, _
ByVal ParamArray parameterValues() As Object)
/*SqlHelper 类方法的事务型重载不再需要 SqlConnection 参数。在此版本中,连接信息从 SqlTransaction 对象中派生,因此不必在方法签名中包含 SqlConnection 对象参数。*/
Execute* (ByVal transaction As SqlTransaction, _
ByVal commandType As CommandType, _
ByVal commandText As String)
Execute* (ByVal transaction As SqlTransaction, _
ByVal commandType As CommandType, _
ByVal commandText As String, _
ByVal ParamArray commandParameters() As SqlParameter)
Execute* (ByVal transaction As SqlTransaction, _
ByVal spName As String, _
ByVal ParamArray parameterValues() As Object)
除这些重载以外,除 ExecuteXmlReader 之外的其他方法还提供了另一种重载:允许将连接信息作为连接字符串而不是连接对象来传递,如下面的方法签名所示:
[Visual Basic]
Execute* (ByVal connectionString As String, _
ByVal commandType As CommandType, _
ByVal commandText As String)
Execute* (ByVal connectionString As String, _
ByVal commandType As CommandType, _
ByVal commandText As String, _
ByVal ParamArray commandParameters() As SqlParameter)
Execute* (ByVal connectionString As String, _
ByVal spName As String, _
ByVal ParamArray parameterValues() As Object)
ExecuteXmlReader 不支持连接字符串,因为:与 SqlDataReader 对象不同,XmlReader 对象在 XmlReader 关闭时没有提供自动关闭连接的方法。如果客户端传递了连接字符串,那么当客户端完成对 XmlReader 的操作后,将无法关闭与 XmlReader 相关联的连接对象。
通过参考 Data Access Application Block 程序集并导入 Microsoft.ApplicationBlocks.Data 命名空间,您可以轻松编写使用任何一种 SqlHelper 类方法的代码,如下面的代码示例所示:
[Visual Basic]
Imports Microsoft.ApplicationBlocks.Data
[C#]
using Microsoft.ApplicationBlocks.Data;
导入命名空间后,您可以调用任何 Execute* 方法,如下面的代码示例所示:
[Visual Basic]
Dim ds As DataSet = SqlHelper.ExecuteDataset( _
"SERVER=(local);DATABASE=Northwind;INTEGRATED SECURITY=True;",
_
CommandType.Text, "SELECT * FROM Products")
[C#]
DataSet ds = SqlHelper.ExecuteDataset(
"SERVER=DataServer;DATABASE=Northwind;INTEGRATED
SECURITY=sspi;", _
CommandType.Text, "SELECT * FROM Products");
使用 SqlHelperParameterCache 类管理参数
SqlHelperParameterCache 类提供了三种可以用来管理参数的公共共享方法。它们是:
CacheParameterSet。用于将 SqlParameters 数组存储到缓存中。
GetCachedParameterSet。用于检索缓存的参数数组的副本。
GetSpParameterSet。一种重载方法,用于检索指定存储过程的相应参数(首先查询一次数据库,然后缓存结果以便将来查询)。
缓存和检索参数
通过使用 CacheParameterSet 方法,可以缓存 SqlParameter 对象数组。此方法通过将连接字符串和命令文本连接起来创建一个键,然后将参数数组存储在 Hashtable 中。
要从缓存中检索参数,请使用 GetCachedParameterSet 方法。此方法将返回一个 SqlParameter 对象数组,这些对象已使用缓存(与传递给该方法的连接字符串和命令文本相对应)中的参数的名称、值、方向和数据类型等进行了初始化。
注意: 用作参数集的键的连接字符串通过简单的字符串比较进行匹配。用于从 GetCachedParameterSet 中检索参数的连接字符串必须与用来通过 CacheParameterSet 来存储这些参数的连接字符串完全相同。语法不同的连接字符串即使语义相同,也不会被认为是匹配的。
个人认为SqlHelperParameterCache用处不是很大,所以并不想对其进行详细介绍。
如何使用 ExecuteDataset 返回包含多个表的数据集?
通过创建一个可以返回多个行集的存储过程(通过执行多个 SELECT 语句或者对其他存储过程进行嵌套调用),并使用 ExecuteDataset 方法执行该过程,您可以检索包含多个表的数据集。
例如,假设您的数据库包含以下存储过程。
CREATE PROCEDURE GetCategories
AS
SELECT * FROM Categories
GO
CREATE PROCEDURE GetProducts
AS
SELECT * FROM Products
您可以创建一个主存储过程来对这些过程进行嵌套调用,如下面的代码示例所示。
CREATE PROCEDURE GetCategoriesAndProducts
AS
BEGIN
EXEC GetCategories
EXEC GetProducts
END
使用 ExecuteDataset 方法执行此主存储过程将返回一个 DateSet,其中包含两个表:一个表包含分类数据,另一个表包含产品数据。
注意: ExecuteDataset 方法不提供为返回的表指定自定义名称的方法。第一个表的编号始终为 0,名称为 Table,第二个表的编号为 1,名称为 Table1,依此类推。
Data Access Application Block for .NET v2
Authorization and Profile Application Application Block
Offline Application Application Application Block
Service Aggregation Block
Persistent Asynchronous Invocation Application Block
Caching Application Block
Configuration Management Application Block
Exception Management Application Block
Updater Application Block
User Interface Processes Application Block
Trace Application Block
下面几天,隔几天有空的时候介绍一个吧
2004年10月25日 #
打开BAM.xls进行业务活动监视的时候,会报如下错误:
中文版:隐藏模块中编译错误:工具
英文版:"Compile error in hidden module: Utility"
由于BAM Project被密码保护,所以开发人员无法进行调试。
出错原因正是大名鼎鼎的DLL地狱问题,Biztalk 2004 BAM模板中使用的是msado27.tlb(MSDA 2.7)库,如果你安装了最新的MSDA 2.8版本的话,由于两者之间的版本不兼容,BAM无法正常工作。
解决方法是,打开一个新的Excel文件,在VBA IDE中Reference如下文件,C:\Program Files\Common Files\System\ado\msado27.tlb,并把已引用的Microsoft ActiveX Data Object2.8 Library取消,保存这个文件,关闭,重新打开BAM.xls, 这下就work了。
大家可以去try一把试试 ^-^
2004年10月21日 #
用Web Service发布向导可以将Biztalk 工作流发布成Web Service让供应商或合作伙伴通过HTTP 和SOAP适配器远程调用,与此同时,对于内部的ERP系统,可能更希望通过File适配器调用,如何让一个工作流即可以通过SOAP传入的消息启动,又可以通过File Folder中的传入文件启动呢?
方法还是很简单的,就是在一个Receive Port中添加多个Receive Locations, 并让工作流中的一个Specify Later的逻辑端口绑定到有多个Receive Locations的物理端口,每一个Receive Location都可以启动这个工作流。