EntLib.com 专业电子商务平台

基于Microsoft .Net Framework / ASP.Net / C# / AJAX 平台构建的标准电子商务系统
随笔 - 99, 评论 - 69, 引用 - 0

星期二 2009年12月01日

 关于11. 26至12. 1-- EntLib.com服务器中断公告

关于11. 26至12. 1-- EntLib.com服务器中断公告
 
 
近日央视对移动机房相关涉黄问题进行报道,中国移动因信息安全检查工作需要,对机房进行整治,于11.26日早8点开始,中国移动全面暂停怒江路机房所有服务器的接入服务,并要检查每个IP地址对应的网站信息。
 
11.26日移动已经明确封网,此次扫黄打非活动会持续到年底。
 
可怜、无辜的EntLib.com 服务器也受到牵连,短期内也看不到解封的希望,欲哭无泪 …….
 
12. 1日 EntLib.com将服务器搬迁至电信机房,下午5:00网站已全面恢复!
 
因此给您带来了不便,在此EntLib.com向广大用户表示深深的歉意,并感觉大家对EntLib.com一如既往的支持!
 
相关链接:
 

 

posted @ 2009年12月1日 19:41 | EntLib.com 阅读 (210) | 评论 (2) | 编辑 |

星期六 2009年09月26日

 比较Windows Workflow Foundation和BizTalk Server

比较Windows Workflow Foundation和BizTalk Server
 
简介
WF和BizTalk Server 有很多相似性,下面分析、比较WF和BizTalk,和大家一起分享。
 
Windows Workflow Foundation
WF 是一种编程模型、一组工具控件、和运行时,在Windows平台上实现工作流。WF运行时是.NET Framework的一部分,首先出现在.NET Framework 3.0,随后在.NET Framework 3.5 中进行改进。关于WF的范例学习程序,请参考如下链接
 
 
Microsoft BizTalk Server
Microsoft BizTalk Server提供了高效的方式,通过可管理的业务流程来实现系统和业务集成,让不同的系统和业务之间进行灵活地、自动地交互。关于BizTalk 的范例学习程序,请参考如下链接。BizTalk Server 提供了对业务流程管理(Business Process Management, BPM)和自动化的开发和运行环境。
 
 
 
WF和BizTalk Server的本质区别
WF和BizTalk Server并不是相互竞争的技术,而是互补的技术,用来满足不同的需要。
 

 
WF 和BizTalk Server的功能比较
下表显示了WF和BizTalk Server的功能比较。
 
本文由http://forum.EntLib.com 开源ASP.NET论坛小组编译,原文链接如下:
 

 

posted @ 2009年9月26日 20:47 | EntLib.com 阅读 (589) | 评论 (0) | 编辑 | 目录 [ BizTalk & ESB & SOA ]

星期日 2009年09月20日

 ASP.NET MVC 最佳实践向导(Best Practices )

ASP.NET MVC 最佳实践向导(Best Practices
 
ASP.NET MVC 是微软官方提供的一个新的开发框架,下面有一些博客帖子介绍了使用ASP.NET MVC 框架开发Web Applications的推荐最佳实践。
 
 
ASP.NET MVC 资源
 
 

 

posted @ 2009年9月20日 19:03 | EntLib.com 阅读 (572) | 评论 (0) | 编辑 | 目录 [ MVC Framework ]

星期六 2009年07月04日

 业务逻辑层(ASP.NET 3.5 Enterprise Application Development)读书笔记

业务逻辑层(ASP.NET 3.5 Enterprise Application Development)读书笔记
-- Problem – Design – Solution
 
本文是针对Wrox出版的《ASP.NET 3.5 Enterprise Application Development With Visual Studio 2008 Problem Design Solution》该书第三章Business Logic Layer(业务逻辑层)读书笔记。http://forum.EntLib.com 开源论坛小组提供,欢迎交流,分享。
 
 
原文介绍在三层架构应用中业务逻辑层的设计。在业务逻辑层中,定义了四个基类,如下图所示:
 


 
ENTBaseBO 是业务对象的基类,不允许用户改变。通常,这一类型的业务对象显示在用户界面的下拉列表框。该类中包含了一些所有业务对象共同的属性,如ID、InsertDate、InsertENTUserAccountId…等等。另外,增加了一个有用的只读属性 DisplayText,以及一个对应的抽象方法GetDisplayText(),必须在继承子类中实现该方法。
public string DisplayText
{
get { return GetDisplayText(); }
}
protected abstract string GetDisplayText();
这一属性可用来将对象显示在UI层或者任何数据绑定控件。
 
该类唯一的公有方法Load,用来从数据库中检索数据 – 没有增加、更新、或删除操作。
        #region Abstract Methods
        public abstract bool Load(int id);
        protected abstract void MapEntityToCustomProperties(IENTBaseEntity entity);
        protected abstract string GetDisplayText();
        #endregion Abstratct Methods
 
        #region Public Methods
        public void MapEntityToProperties(IENTBaseEntity entity)
        {           
            if (entity != null)
            {
                InsertDate = entity.InsertDate;
                InsertENTUserAccountId = entity.InsertENTUserAccountId;
                UpdateDate = entity.UpdateDate;
                UpdateENTUserAccountId = entity.UpdateENTUserAccountId;
                Version = entity.Version;
                this.MapEntityToCustomProperties(entity);
            }
        }
 
        #endregion Public Methods
 
任何继承ENTBaseBO 的业务类必须事先Load和MapentityToCustomProperties 方法。在实现Load方法时,从DAL检索一个单一的实体,并调用MapEntityToProperties 方法。MapEntityToProperties 方法负责将5个公共的字典赋值给当前的对象实例,并接着调用MapEntityToCustomProperties 方法。MapEntityToCustomProperties 方法则负责将其他的实体对象的属性赋值给业务对象的属性,这是因为这些属性不是基类中的共用的属性。 --- http://forum.EntLib.com 开源小组注:作者使用的业务对象(Business Object)是自己编写的,不是LINQ to SQL 设计器自动创建的实体对象(Entity Object)。
 
下面是业务对象中,Load和MapEntityToCustomProperties 方法的一段范例代码:
        public override bool Load(int id)
        {
            ENTUserAccount userAccount = new ENTUserAccountData().Select(id);
            if (userAccount != null)
            {
                MapEntityToProperties(userAccount);
                return true;
            }
            else
            {
                return false;
            }       
        }
 
        protected override void MapEntityToCustomProperties(IENTBaseEntity entity)
        {
            ENTUserAccount userAccount = (ENTUserAccount)entity;
 
            ID = userAccount.ENTUserAccountId;
            WindowsAccountName = userAccount.WindowsAccountName;
            FirstName = userAccount.FirstName;
            LastName = userAccount.LastName;
            Email = userAccount.Email;
            IsActive = userAccount.IsActive;
        }
 
ENTBaseBOList 集合类表示ENTBaseBO 基类集合,包含唯一的一个抽象方法Load,该方法负责从数据库中检索所有的记录。如果你需要过滤部分记录,你可以在继承类中重载Load方法。
 
ENTBaseEO 是另一个抽象类,EO表示“可编辑对象(Edit Object)”,表示一个单一实例的特定业务对象。ENTBaseEO 和 ENTBaseBO的区别是:ENTBaseEO 允许对象使用者增加、更新和删除数据库记录。ENTBaseEO继承自ENTBaseBO。
ENTBaseEO 类中添加了一个枚举类型DBActionEnum:
        public enum DBActionEnum
        {
            Save,
            Delete
        }
同时,也包含DBActionEnum的公有属性DBAction:
public DBActionEnum DBAction { get; set; }
 
关于Save方法,其中一个在基类中实现,并调用另一个抽象的Save方法,该方法必须在继承子类中实现,因为至少需要传入具体的参数。
 
在保存记录到数据库之前,必须调用Validate方法进行业务数据和规则的验证。其中,会涉及到ENTValidationError 类和ENTValidationErros 集合类。验证业务数据和规则一定与具体的业务对象相关,因此Validate抽象方法一定要在业务子类中来实现。
 
 
另一个Init抽象方法,主要是实现一些数据的初始化操作。
 
下面是Delete操作相关的一些方法。首先是下面的两个抽象方法:
        protected abstract void DeleteForReal(HRPaidTimeOffDataContext db);
        protected abstract void ValidateDelete(HRPaidTimeOffDataContext db, ref ENTValidationErrors validationErrors);
 
DeleteForReal方法会调用DAL的Delete方法。ValidateDelete 方法类似于前面的Validate方法,主要是在执行删除操作之前,进行必要的数据检查。
 
Delete方法在基类中实现,并调用DeleteForReal方法,与Save方法比较类似,有2个重载的Delete方法。第一个Delete方法启动事务,并调用第二个Delete方法,传入HRPaidTimeOffDataContext 对象。第二个Delete方法在调用DeleteForReal方法之前,先调用ValidateDelete 方法,进行数据验证。
 
IsNewRecord 方法比较简单,只是用来返回对象是否为新纪录,用在Save方法中判断对象是插入或者更新操作。
 
最后一个是UpdateFailed方法,在更新记录发生异常时,该方法用来添加一条信息到validationErrors 集合对象中,并最终显示在UI界面上。
 
最后一个基类是ENTBaseEOList,表示可编辑的业务对象集合,该类继承自ENTBaseBOList类。
[Serializable()]
public abstract class ENTBaseEOList<T> : ENTBaseBOList<T>
where T : ENTBaseEO, new()
{ … }
 
ENTBaseEOList 类主要有2个方法:Save 和 GetAll。其中,Save 方法将增加或删除集合对象到数据库中(http://forum.entlib.com 开源小组注:根据ENTBaseEO.DBActionEnum 属性值,来执行Save或Delete操作),Save方法有2个重载。
 
GetAll方法比较简单,调用基类ENTBaseBOList 的Load方法,检索所有的业务对象实例,返回集合对象自身。
        public List<T> GetAll()
        {
            this.Load();
            return this;
        }
 
到现在为止,我们已经创建了所有的基类。下一步,创建一个可编辑的业务对象ENTUserAccountEO。
    [Serializable()]
    public class ENTUserAccountEO : ENTBaseEO
关于该类的具体实现,可以查看原文。
 
总结 Summary
本文集中介绍了业务逻辑层和创建了4个基类,本书的后面章节都将使用都这些基类。其中,在BLL层,将业务对象分为两类:静态业务对象和可编辑业务对象。
 
BLL和DAL的交互:
1. 从DAL检索数据,以单一实体对象(Entity Object)、实体对象计划、或数值(Scalar values)传递给BLL。
2. 以数值(Scalar values)的形式传递数据到DAL层。(http://forum.entlib.com 开源论坛小组注:是以数值的形式传递,还是以实体对象的形式传递,各有优缺点。不过,在具体项目中,我们一般多采用传递实体对象,这样,方法的signature 不需要发生变化。)
 
BLL和UI层的交互:
1. UI层创建业务对象实例,并调用Load 方法检索数据。
2. UI层创建业务对象实例,并给对象属性赋值,然后调用Save方法,增加或更新记录;或者调用Delete方法删除记录。
3. 冲突的验证规则会以ENTValidationErrors集合类的形式传递到UI层。
 
创建继承自ENTBaseBO基类的关键点:
1. 定义类为 Serializable。
2. 创建业务对象(business object)的自有属性。
3. 实现GetDisplayText 方法,返回对象在下拉列表或ListBox 中显示的文本。
4. 实现Load 方法,从DAL返回单一的实体对象(Entity Object)。
5. 实现MapEntityToCustomProperties 方法,将实体对象的属性赋值给业务对象的对应属性。
 
创建继承自ENTBaseBoList基类的关键点:
1. 定义类为 Serializable。
2. 实现Load方法,从DAL返回所有的实体对象。
3. 重载Load方法,过滤从DAL返回的实体对象。
 
创建继承自 ENTBaseEO 基类的关键点:
1. 按照上述实现ENTBaseBo基类的相同步骤。
2. 实现Save方法,并调用Validate方法进行数据验证,同时确定是新增或更新记录,然后以数值(Scalar value)的形式传递给DAL中相应的方法。
3. 实现Validate 方法,对业务对象的数据进行验证。任何冲突的数据将添加到validationErrors集合中。
4. 实现DeleteForReal方法,该方法调用DAL层的Delete方法,进行数据删除操作。
5. 实现ValidateDelete 方法,在删除记录之前,进行强制数据规则验证,一般为数据完整性验证。
6. 实现Init方法,该方法在用户创建一条新纪录是,设置对象的默认属性。
 
创建继承自ENTBaseEOList 基类的关键点:
1. 完全按照实现ENTBaseBOList基类的步骤即可。
 
下一节将介绍用户界面层(User Interface Layer)的设计,欢迎交流。
 

 

posted @ 2009年7月4日 8:50 | EntLib.com 阅读 (965) | 评论 (0) | 编辑 | 目录 [ ASP.NET 3.5 ]

星期四 2009年06月11日

 数据访问层(ASP.NET 3.5 Enterprise Application Development)读书笔记

数据访问层(ASP.NET 3.5 Enterprise Application Development)读书笔记
-- Problem – Design – Solution
 
本文是针对Wrox出版的《ASP.NET 3.5 Enterprise Application Development With Visual Studio 2008 Problem Design Solution》该书第二章Data Access Layer(数据访问层)读书笔记。http://forum.EntLib.com 开源论坛小组提供,欢迎交流,分享。
 
原文介绍在三层架构应用中使用 LINQ to SQL技术。该设计模式使用DataContext 和数据库交互,ORM Designer设计器生成的实体对象(Entity Object)则负责与BLL(业务逻辑层)交互。
该架构的设计图如下所示:
 
 
 
在原文中通过将存储过程拖拉到ORM 设计器中,并使用存储过程来访问数据,如Insert、Update、Delete、Select等等方法。如下图所示:
 
 
将Method Panel中的方法与ORM 设计器中实体对象进行的关联的步骤如下。
首先,选择ORM 设计器中的实体对象,如上图中的ENTUserAccount。
 
然后,查看属性列表,比如点击Insert 属性后的弹出按钮,弹出如下对话框。
 
 
经过上述设置后,DataContext 将调用存储过程来插入记录,而不是想之前的创建动态的SQL脚本。在DBML自动创建的cs文件中,可以看到如下2个方法:
         private void InsertENTUserAccount(ENTUserAccount obj)
         {
              System.Nullable<int> p1 = obj.ENTUserAccountId;
              this.ENTUserAccountInsert(ref p1, obj.WindowsAccountName, obj.FirstName, obj.LastName, obj.Email, ((System.Nullable<bool>)(obj.IsActive)), ((System.Nullable<int>)(obj.InsertENTUserAccountId)));
              obj.ENTUserAccountId = p1.GetValueOrDefault();
         }
 
         [Function(Name="dbo.ENTUserAccountInsert")]
         public int ENTUserAccountInsert([Parameter(Name="ENTUserAccountId", DbType="Int")] ref System.Nullable<int> eNTUserAccountId, [Parameter(Name="WindowsAccountName", DbType="VarChar(50)")] string windowsAccountName, [Parameter(Name="FirstName", DbType="VarChar(50)")] string firstName, [Parameter(Name="LastName", DbType="VarChar(50)")] string lastName, [Parameter(Name="Email", DbType="VarChar(100)")] string email, [Parameter(Name="IsActive", DbType="Bit")] System.Nullable<bool> isActive, [Parameter(Name="InsertENTUserAccountId", DbType="Int")] System.Nullable<int> insertENTUserAccountId)
         {
              IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), eNTUserAccountId, windowsAccountName, firstName, lastName, email, isActive, insertENTUserAccountId);
              eNTUserAccountId = ((System.Nullable<int>)(result.GetParameterValue(0)));
              return ((int)(result.ReturnValue));
         }
其中第一个方法接收一个ENTUserAccount 对象的参数,并调用第二个方法,将对象的属性作为参数传入。第二个方法接收存储过程的每一个参数,接着调用DataContext对象的ExecuteMethodCall方法,该方法知道如何调用存储过程,并返回结果。
 
另外的Delete、Update属性操作相似。操作完成后,ENTUserAccount 实体对象的属性列表如下所示:
 

 
客户端调用的示例代码如下:
//Create an instance of the data context
HRPaidTimeOffDataContext db = new HRPaidTimeOffDataContext();
 
//Create a new ENTUserAccount object and set the properties
ENTUserAccount userAccount = new ENTUserAccount
{
WindowsAccountName = @”VARALLO1\VaralloMadison”,
FirstName = “Madison”,
LastName = “Varallo”,
Email = “madison.varallo@v2.com”,
IsActive = true,
InsertDate = DateTime.Now,
InsertENTUserAccountId = 1,
UpdateDate = DateTime.Now,
UpdateENTUserAccountId = 1
};
//Signal the context to insert this record
db.ENTUserAccounts.InsertOnSubmit(userAccount);
 
//Save the changes to the database
db.SubmitChanges();
 
为什么要这样设计呢?我不是很认同这种设计。
 
1. 任何对表ENTUserAccount的修改,都需要重新将EntUserAccount表拖拉到ORM设计器中,并重新设置Insert、Update、Delete 等等属性。
2. 需要编写很多琐碎的存储过程,如上述的Insert、Update、Delete、Select等等,相信很多开发人员不喜欢编写这么多简单的Stored Procedure。
3. 这样设计增加很多工作量,并且不便于后期的维护开发,也影响工作效率。
 
本章后面部分定义的一些接口类、抽象基类是针对该项目设计的特性提取的一些共用特征。这种设计方法值得推荐,但并不一定适合于实际的开发项目。
public interface IENTBaseEntity
public abstract class ENTBaseData<T> where T : IENTBaseEntity
在数据访问层DAL仅仅只有2个上述的基类。
public class ENTUserAccountData : ENTBaseData<ENTUserAccount>
上述的Data类封装出来ENTUserAccount实体对象,ENTUserAccountData类继承自ENTBaseData基类,实现基类中的抽象方法。
ORM设计器负责为数据库中每一个表生成一个对应的实体类,对应Data类则负责与数据库和业务逻辑层交互。
 
下一篇是关于业务逻辑层BLL的设计探讨。
 

 

posted @ 2009年6月11日 19:58 | EntLib.com 阅读 (1361) | 评论 (7) | 编辑 | 目录 [ ASP.NET 3.5 ]

星期二 2009年06月02日

 WF 状态机工作流构建订单处理流程-范例程序分析 Part 2

WF 状态机工作流构建订单处理流程-范例程序分析 Part 2
 
状态机工作流(State Machine Workflow)是以状态的变化为驱动而进行业务流转的,且一定需要人为的干预,而不像顺序类型工作流(Sequential Workflow)那样按照事先设计好的业务流程一步一步依次执行下去。本范例程序由http://forum.entlib.com 开源ASP.NET/C# 论坛小组提供。
 
 
实现IOrder 接口
下面实现IOrder 接口,下面的代码Order类实现IOrder 接口。这里,仅仅在控制台显示一些简单的提示信息。该Order类将由Client端程序调用,其中内部的调用,将触发工作流中相应的活动。
 

 
 
创建工作流实例
下面基于workflow runtime 创建并启动工作流实例,并添加Order 对象作为服务。
            WorkflowInstance objWorkFlowInstance;
            WorkflowRuntime objWorkFlowRuntime = new WorkflowRuntime();
            Order objOrder = new Order();
            ExternalDataExchangeService objService = new ExternalDataExchangeService();
            Guid InstanceId = Guid.NewGuid();
 
            objWorkFlowRuntime.AddService(objService);
            objService.AddService(objOrder);
            objWorkFlowInstance = objWorkFlowRuntime.CreateWorkflow(typeof(OrderWorkFlow.WorkflowOrder), null, InstanceId);
            objWorkFlowInstance.Start();
            Console.WriteLine("工作流启动啦 -- Work flow started");
 
下面是Client端调用代码,根据用户输入的数值,调用Order 对象的相应方法,触发工作流中相应的活动。
            ExternalDataEventArgs objDataEventArgs = new ExternalDataEventArgs(InstanceId);
            objDataEventArgs.WaitForIdle = true;
            Console.WriteLine("1 - 订单尚未付款");
            Console.WriteLine("2 - 产品入库");
            Console.WriteLine("3 - 对订单付款");
            Console.WriteLine("4 - 订单发货");
            Console.WriteLine("5 - 送货地址错误");
            Console.WriteLine("6 - 送货地址正确");
            Console.WriteLine("7 - 产品有库存");
            Console.WriteLine("请输入上述数字: ");
            int intValueEntered=0;
            while(intValueEntered < 7)
            {
                intValueEntered = Convert.ToInt16(Console.ReadLine().ToString());
                if (intValueEntered == 1)
                {
                    objOrder.CallNotPaid(objDataEventArgs);
                }
                else if (intValueEntered == 2)
                {
                    objOrder.CallInStock(objDataEventArgs);
                }
                else if (intValueEntered == 3)
                {
                    objOrder.CallPaymentMade(objDataEventArgs);
                }
                else if (intValueEntered == 4)
                {
                    objOrder.CallDispatch(objDataEventArgs);
                }
                else if (intValueEntered == 5)
                {
                    objOrder.CallAddressNotProper(objDataEventArgs);
                }
                else if (intValueEntered == 6)
                {
                    objOrder.CallAddressCorrected(objDataEventArgs);
                }
                else if (intValueEntered == 7)
                {
                    objOrder.CallProductStocked(objDataEventArgs);
                }
            }
该工作流演示界面如下:
 

 
欢迎下载该范例程序源代码。
 

 点击下载 下载Workflow范例程序 (111 KB)

posted @ 2009年6月2日 19:43 | EntLib.com 阅读 (1503) | 评论 (2) | 编辑 | 目录 [ Workflow (WF) ]

星期六 2009年05月16日

 SQL Server XML 代码编写示例

SQL Server XML 代码编写示例
 
 
XML 数据类型有很多内置的方法,这些方法只针对XML数据类型。比较常用的有.query、.value、.modify、.nodes、.exist 等等方法。本文中所有的SQL 脚本范例都基于AdventureWorks 数据库进行过测试。
 
(1) .query 方法
允许用户通过运行基于XQuery 格式化的查询来访问XML。XQuery 允许返回多个数据片段,而不是一个离散的值。.Query要求一个参数,运行在XML数据实例之上有效的XQuery。
 
如下是提取Production.ProductModel 表中ProductModelID 为 66 的产品文档:
Select ProductModelID,
    Instructions.query('declare namespace PI="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";
    /PI:root/PI:Location/PI:step') AS Steps
From Production.ProductModel
where ProductModelID=66
 
Select ProductModelID,
    Instructions.query('declare default element namespace "http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";
    /root/Location/step') AS Steps
From Production.ProductModel
where ProductModelID=66
 
上述2个SQL 脚本在定义XML namespace有一些细微的差异,可以比较一下执行产生的结果。也可以在 XQuery 之外使用 With XMLNamespaces ('http://xxx' as XX),提高XML 查询的可读性。上述代码通过WITH XMLNamespaces() 声明编写功能相同的代码:
;WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions' AS PI)
 
Select ProductModelID,
    Instructions.query('/PI:root/PI:Location/PI:step') AS Steps
From Production.ProductModel
where ProductModelID=66
 
设置默认命名空间:
;WITH XMLNAMESPACES(DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions')
 
Select ProductModelID,
    Instructions.query('/root/Location/step') AS Steps
From Production.ProductModel
where ProductModelID=66
 
 
(2) .value 方法
是查询离散数据的方法,使用一个XPath 的语法来定位指定的结点,并得到标量值。需要确保指定的XPath 真正返回一个离散值。
查询ProductModelID为 66 的第一个Location元素中LabelHours 属性的值。
;WITH XMLNAMESPACES(DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions')
 
Select ProductModelID,
    Instructions.value('(/root/Location/@LaborHours)[1]', 'decimal (5,2)') AS Steps
From Production.ProductModel
where ProductModelID=66
 
如果不是属性,而是子元素,则应该去掉@符号。
 
(3) .modify 方法
是对XQuery 的扩展,以提供XQuery 的数据修改能力。这种对XQuery的扩展称作XML数据处理语言(XML Data Manipulation Language),或者XML DML。XML DML向XQuery 中添加了3条新命令:insert / delete / replace value of。其中replace value of 替代了SQL的UPDATE语句。
例如,将上述查询的LabelHours属性值,由原来的1.5小时更新为1.75小时。
;WITH XMLNAMESPACES(DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions')
 
UPDATE Production.ProductModel
SET Instructions.modify('replace value of (/root/Location/@LaborHours)[1] with 1.75')
WHERE ProductModelID=66
 
在UPDATE语句中,使用 replace value of 关键字来指定更新中的XML部分。
 
(4) .nodes 方法
用来获取XML块,并将之拆分多个数据行,以关系的形式存储。通过.nodes 所做的工作,本质上是将一个XML 数据的实例拆分到自己的表中。这意味着,我们需要将.nodes 的结果作为一个表,而不是一个列来看待。在.nodes 和一个数据库表之间的主要差别是,必须将.nodes 的结果交叉应用(cross apply)回指定的XML数据的来源表中。可以将cross apply 想象成某种连接,只不过使用的是cross apply关键字,而不是JOIN关键字,并且.nodes 替代了ON子句。
;WITH XMLNAMESPACES(DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions')
 
SELECT pm.ProductModelID,
    pmi.Location.value('./@LocationID', 'int') AS LocationID,
    pmi.Location.value('./@LaborHours', 'decimal(5,2)') AS LaborHours
FROM Production.ProductModel pm
CROSS APPLY pm.Instructions.nodes('/root/Location') AS pmi(Location)
 
将LocationID和LaborHours 信息拆分到列上,就好像它们位于关系表中一样。通过使用.nodes 方法,本质上将一个表 ProductModel 转换成了2个表(源表,以及ProductModel表中的Instructions 列所生成的.nodes 结果)。
 
下面的范例脚本将Instructions XML数据中material 子节点的数据库取出,转为以数据表n的字段 c 来显示,其中字段c 的数据类型也是XML,进一步取出c的值,并转换为nvarchar数据类型。
 
DECLARE @xml XML
SELECT @xml=Instructions FROM Production.ProductModel
WHERE ProductModelID=66
 
;WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions' AS ns)
 
SELECT c.value('.','nvarchar(max)') AS NewCol
FROM @xml.nodes('/ns:root/ns:Location/ns:step/ns:material') as n(c)
 
 
 
(5) .exist 方法
.exist 方法有点像SQL中的EXISTS 语句,接受一个XQuery 表达式,并返回一个布尔值。
查询Instructions XML数据中存在LaborHours属性的数据记录。
;WITH XMLNAMESPACES(DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions')
 
SELECT ProductModelID, Instructions.value('(/root/Location/@LaborHours)[1]','decimal(5,2)')
FROM Production.ProductModel
WHERE Instructions.exist('/root/Location/@LaborHours')=1
 
总结一下SQL Server 2005/2008 为XML数据类型提供的函数:

 

 

posted @ 2009年5月16日 10:11 | EntLib.com 阅读 (1134) | 评论 (0) | 编辑 | 目录 [ SQL Server & Service Broker ]

星期三 2009年04月29日

 SQL Server 高级查询 – 学习笔记

SQL Server 高级查询 – 学习笔记
 
本文是《SQL Server 2005 高级程序设计》, Professional SQL Server 2005 Programming – 第六章:高级查询的学习笔记。主要包含如下内容:
1. 嵌套的子查询;
2. 相关子查询;
3. 派生表;
4. EXISTS 运算符的使用;
 
大部分的子查询(当然不是全部)也能够用联接(Join)来实现,关于联接和子查询的性能比较,需要具体分析。本文所有的范例SQL 脚本使用AdventureWorks 数据库
 
1. 构建嵌套的子查询
嵌套的子查询(nested subquery) - 要么返回一个用于外部查询的值,要么返回一个与IN运算符一起使用的值的列表。当想要明确使用 = 运算符时,则使用返回一个值的查询,也就是从一行返回一列。如果要返回一个列表,则必须在外部查询中使用 IN 运算符。
 
(1)创建使用单值的嵌套子查询
查询系统中购买产品的第一天所销售的所有产品项的ProductID,SQL 脚本如下:
 
SELECT DISTINCT soh.OrderDate, sod.ProductID
FROM Sales.SalesOrderHeader soh
JOIN Sales.SalesOrderDetail sod
 ON soh.SalesOrderID = sod.SalesOrderID
WHERE soh.OrderDate =(SELECT MIN(OrderDate) FROM Sales.SalesOrderHeader)
 
内部查询(SELECT MIN(OrderDate) FROM Sales.SalesOrderHeader)获取一个值,该值将在外部查询中使用。由于这里使用了 = ,因此内部查询只能返回一个值,也就是一行中返回一列,否则将在运行时产生错误。
 
 
(2)创建返回多个值列表的嵌套子查询
在实际的应用中,最常见的是这种子查询,返回某种类型的范围列表,并在查询中作为一种判断标准来使用。
下面查询条件:查询应征了公司另一个职位的所有雇员的列表。
SELECT e.EmployeeID, FirstName, LastName
FROM HumanResources.Employee e
JOIN Person.Contact c
 ON e.ContactID = c.ContactID
WHERE e.EmployeeID IN
 (SELECT DISTINCT EmployeeID FROM HumanResources.JobCandidate)
 
上述代码返回2条记录:
 
 
上述的查询脚本也可以使用内部联接而非嵌套的子查询来实现,脚本如下所示:
SELECT e.EmployeeID, FirstName, LastName
FROM HumanResources.Employee e
JOIN Person.Contact c
 ON e.ContactID = c.ContactID
JOIN HumanResources.JobCandidate jc
 on jc.EmployeeID = e.EmployeeID
 
 
2. 构建相关子查询
相关子查询与前面的嵌套子查询的不同之处在于,在相关子查询中,信息是双向而非单向的。在嵌套子查询中,内部查询只处理一次,信息被传出以用于外部查询,外部查询也只执行一次。
在相关子查询中,按以下3个步骤来进行:
(1)外部查询得到一条记录并将其传入到内部查询。
(2)内部查询基于传入的值执行。
(3)内部查询从其结果中把值传回到外部查询,外部查询使用这些值来完成其处理。
 
WHERE 子句中的相关子查询
查询每一位顾客在系统中的第一张订单中的OrderID 和 OrderDate,也就是要知道顾客在第一天下订单的日期和订单的ID。
SELECT soh.SalesOrderID, soh.OrderDate, soh.CustomerID
FROM Sales.SalesOrderHeader soh
WHERE soh.OrderDate=(SELECT MIN(soh2.OrderDate)
                  FROM Sales.SalesOrderHeader soh2
                  WHERE soh2.CustomerID = soh.CustomerID)
ORDER BY CustomerID
 
 
SELECT 列表中的相关子查询
查询顾客的名字以及首次订购的日期。因为要获得顾客的名字,这意味着必须用到Person.Contact表,另外还需要知道第一次订购的日期。
SELECT c.LastName,
(SELECT MIN(OrderDate)
 FROM Sales.SalesOrderHeader o
 WHERE o.ContactID = c.ContactID)
 AS OrderDate
FROM Person.Contact c
 
上述查询结果中,OrderDate列会有NULL值。这是由于Sales.SalesOrderHeader 表中没有与Person.Contact 表(外部查询)中的记录相匹配的记录。
 
上述查询也可以通过Join联接来实现:
SELECT c.LastName, o.OrderDate
FROM Person.Contact c
LEFT JOIN(SELECT ContactID, MIN(OrderDate) AS OrderDate
    FROM Sales.SalesOrderHeader
    GROUP BY ContactID ) AS o
    ON c.ContactID = o.ContactID
 
 
3. 派生表
派生表(derived table),有时也称为内联视图,是由查询结果集的列和行组成,与常规的表一样。
假设要获取订购过某种产品的顾客列表,如Minipump:
SELECT c.FirstName, c.LastName, p.Name
FROM Person.Contact AS c
JOIN Sales.SalesOrderHeader AS soh
    ON c.ContactID = soh.ContactID
JOIN Sales.SalesOrderDetail AS sod
    ON soh.SalesOrderID = sod.SalesOrderID
JOIN Production.Product AS p
    ON sod.ProductID = p.ProductID
WHERE p.Name = 'Minipump'
 
上述查询很容易,但是如果想要获得同时订购过Minipump产品,又订购过 AWC Logo Cap 产品的所有顾客,就有点麻烦了。
需要把Minipump的订购者和AWC Logo Cap产品的订购者的查询结果联接起来,可以通过派生表来实现。要创建派生表,需要注意如下事项:
1. 用圆括号包围产生结果集的查询结果;
2. 给查询的结果赋予别名;
 
下面通过派生表,实现 - 获得同时订购过Minipump产品,又订购过 AWC Logo Cap 产品的所有顾客:
SELECT c.FirstName, c.LastName
FROM Person.Contact AS c
JOIN(SELECT DISTINCT ContactID
    FROM Sales.SalesOrderHeader AS soh
    JOIN Sales.SalesOrderDetail AS sod
       ON soh.SalesOrderID = sod.SalesOrderID
    JOIN Production.Product AS p
       ON sod.ProductID = p.ProductID
    WHERE p.Name = 'Minipump'
    ) pumps
ON c.ContactID = pumps.ContactID
JOIN(SELECT DISTINCT ContactID
    FROM Sales.SalesOrderHeader AS soh
    JOIN Sales.SalesOrderDetail AS sod
       ON soh.SalesOrderID = sod.SalesOrderID
    JOIN Production.Product AS p
       ON sod.ProductID = p.ProductID
    WHERE p.Name = 'AWC Logo Cap'
    ) caps
ON c.ContactID = caps.ContactID
 
但需要注意的是,派生表并不能解决所有的问题。当结果集相当庞大且需要联接大量的记录时,可能要考虑使用临时表,并在其上创建索引(派生表没有索引)。
 
4. EXISTS 运算符
当使用EXISTS 时,并不真正返回数据 – 返回的是关于数据是否存在的TRUE/FALSE,数据满足在EXISTS 语句运行所基于的查询中建立的条件。
 --- 查询应征了该公司另一个职位的雇员列表:
SELECT e.EmployeeID, FirstName, LastName
FROM HumanResources.Employee e
JOIN Person.Contact c
    ON e.ContactID = c.ContactID
WHERE EXISTS
    (SELECT EmployeeID
    FROM HumanResources.JobCandidate jc
    WHERE jc.EmployeeID = e.EmployeeID)
 
使用EXISTS 关键字时,SQL Server 不必在所有的行上进行连接。实际上,SQL Server将浏览记录,直到找到第一个满足条件的记录然后停止于那里。只要有一条记录满足条件,EXISTS 将为真,因此不再需要继续浏览。在进行内部连接时,性能差异将更为显著。
 

 

posted @ 2009年4月29日 21:23 | EntLib.com 阅读 (1135) | 评论 (0) | 编辑 | 目录 [ SQL Server & Service Broker ]

星期一 2009年03月30日

 Enterprise Library for .NET Framework 3.5 (EntLib v4.1) 微软企业库v4.1实验手册发布啦!

Enterprise Library for .NET Framework 3.5 (EntLib v4.1) 微软企业库v4.1实验手册发布啦!
 
Enterprise Library for .Net Framework 3.5 – EntLib v4.1 是patterns & practices 小组为.NET Framework 3.5 开发一套企业库,目前最新版本为v4.1,共包括9个Application Block,包括数据访问(Data Access Application Block)、异常管理(Exception Handling Application Block)、数据验证(Validation Application Block)等等,对企业应用开发非常有帮助,也非常实用。
 
Enterprise Library v4.1 实验手册包括全部9个Application Block,如下图所示。
 
 
EntLib v4.1 下载地址,包括EntLib v4.1安装包和实验手册:
 
希望有兴趣一起翻译的朋友,欢迎和我们联系:entlib@hotmail.com
 
 

 

posted @ 2009年3月30日 21:01 | EntLib.com 阅读 (4100) | 评论 (2) | 编辑 | 目录 [ Enterprise Library ]

星期一 2009年03月23日

 WF 状态机工作流构建订单处理流程-范例程序分析 Part 1

WF 状态机工作流构建订单处理流程-范例程序分析 Part 1
 
状态机工作流(State Machine Workflow)是以状态的变化为驱动而进行业务流转的,且一定需要人为的干预,而不像顺序类型工作流(Sequential Workflow)那样按照事先设计好的业务流程一步一步依次执行下去。本范例程序由http://forum.entlib.com 开源ASP.NET/C# 论坛小组提供。
 
State活动有3种类型的状态:起始状态(Initial State)、业务逻辑过程状态、终止状态(Completed State),在一个状态机工作流中起始状态和终止状态只能有一个。状态机工作流是从起始状态开始执行,在运行过程中通过业务逻辑状态的变化来进行工作流的流转,最终由终止状态标志工作流的结束。因此在状态机工作流设计界面上只能添加 State 状态活动,另外,在该状态中还可以添加一个或多个EventDriven 活动。下面通过一个订单处理流程的范例,详细介绍状态机工作流的使用。
 
下图简要描述了订单处理流程,你会发现许多地方需要用户参与或输入,来改变工作流。例如,财务部门依赖于支付情况来改变工作流,采购部门依赖于库存情况来改变工作流等等。
 
 
 
按照下面具体的操作步骤,一步一步实现上述状态机工作流演示程序。
 
1. 创建接口程序
由于状态机工作流中大量使用HandleExternalEvent 活动来接收应用程序的操作事件,从而改进该工作流上运行的业务实体的状态,因此需要定义一个接口程序作为工作流和应用程序之间沟通的桥梁。本范例程序中将通过接口中的7个事件来模拟状态机工作流的触发,接口IOrder.cs 如下所示。需要注意的是:我们引用System.Workflow.Activities,并且接口添加了[ExternalDataExchange]属性。
 
 
2. 创建Class Library项目,并添加State Machine Workflow(状态机工作流)文件。如下图所示。
 
 
在添加State Machine Workflow文件后,Workflow设计器自动打开。工作流的每一个State都有事件,因此在State Activity 添加EventDriven组件。
 
 
在事件Event触发后,State状态进入下一个状态。因此,双击EventDriven组件,添加HandleExternalEvent组件和SetState组件,其中HandleExternalEvent定义监听的事件,SetState定义将进入的下一个状态。
 


 
按照上面的步骤,重复创建OrderPlaced、OrderApproved、OrderPending、OrderDelivered、OrderCancelled状态,并添加相应的EventDriven组件,最终实现的状态机工作流如下所示:
  
 

 

posted @ 2009年3月23日 1:32 | EntLib.com 阅读 (3196) | 评论 (6) | 编辑 | 目录 [ Workflow (WF) ]