`
yangyou230
  • 浏览: 1650428 次
文章分类
社区版块
存档分类

高性能Web Service数据库编程

 
阅读更多

简介:... 1

启用ATL ServerData Source Cache支持... 1

实现数据库的交互... 2

创建ATL OLEDB使用者类... 2

提供插入记录的能力... 2

创建数据源连接对象... 3

使用UDL文件代替连接字符串... 4

Web Service调试... 4

性能评测... 5

简介:

ATL Server是性能很高的Web ApplicationWeb Service的开发类库。到目前为止,我认为如果应用程序要和数据库交互,性能最高的办法是使用OLE DB,同时要启用ATL ServerData Source Cache

启用ATL ServerData Source Cache支持

先来创建一个名为HighPerformance的工程,该工程将访问SQL Server数据库。在服务器选项中,选择“数据源缓存”。向导为我们创建了两个工程,一个是HighPerformance,一个是HighPerformanceIsapiHighPerformance是我们的应用程序逻辑所在之处,我们所有的Web Service的可公开函数都在这里。HighPerformanceIsapi是我们的ISAP扩展服务工程,IIS将在第一次Web Service被调用期间加载它,并且一直缓存,直到很长一段时间没有人使用我们的Web Service才释放。因此我们可以利用这个特性将需要缓存的全局变量保存到HighPerformanceIsapi工程中,然后每个应用程序线程(HighPerformance工程)可以通过调用QueryService来获得缓存的全局变量,并且调用全局变量的成员函数。

HighPerformanceIsapi工程内部维护了一个线程池,每个线程对象内部都拥有自己的数据源缓存对象,请看代码:

// 逐线程数据源缓存

typedef CDataSourceCache<> ds_cache_type;

CComObjectGlobal<ds_cache_type> m_dsCache;

CComObjectGlobal用于保存在m_dsCache对象的生存期内,HighPerformanceIsapi服务器不会被卸载掉。CDataSourceCache类实现了IDataSourceCache接口。

HighPerformance工程中的应用程序逻辑通过QueryService请求全局对象时,只要传递

__uuidof(IDataSourceCache)即可。QueryService的实现代码如下:

HRESULT STDMETHODCALLTYPE QueryService(REFGUID guidService,

REFIID riid, void** ppvObject)

{

if (InlineIsEqualGUID(guidService, __uuidof(IDataSourceCache)))

{

CIsapiWorker *pWorker = GetThreadWorker();

if (pWorker)

{

CDataSourceCache<> *pCache = NULL;

if (pWorker->GetWorkerData(_DATASOURCE_CACHE, (void **)&pCache))

{

*ppvObject = static_cast<IDataSourceCache *>(pCache);

return S_OK;

}

}

}

return baseISAPI::QueryService(guidService, riid, ppvObject);

}

实现数据库的交互

创建ATL OLEDB使用者类

我们使用向导连接SQL SERVER (服务器名为GAO、用户名和密码为sa、数据库名为测试数据库、数据表为学生表),并支持属性化,生成支持写操作的OLE DB命令类。具体向导使用这里不叙述,请参考MSDN

我们的OLE DB类名为CDataRowset。属性db_source是代表了连接信息,后面我将通过udl文件替代之。db_command属性中的SQL语句代表了查询表的操作,我们也可以使用Update或者insert语句代替之。

注意,我们的CDataRowset类创建在HighPerformance工程中,HighPerformanceIsapi工程中应该保存我们的全局数据源对象。

提供插入记录的能力

我们需要修改db_command属性中绑定的SQL语句,以便我们插入一条语句。db_command(L"INSERT INTO [学生表] ([姓名],[年龄],[性别],[地址]) VALUES (?,?,?,?)")

db_column属性修改为db_param属性,顺序请对应INSERT语句的四个?号。

为了使我们的Web Service有更好的性能,我在工程中设置了使用UNICODE编码。

[

db_command(L"INSERT INTO [学生表] ([姓名],[年龄],[性别],[地址]) VALUES (?,?,?,?)")

]

class CDataRowset

{

public:

[ db_param(1,DBPARAMIO_INPUT)] TCHAR m_column0[20];

[ db_param(2,DBPARAMIO_INPUT)] TCHAR m_column1[20];

[ db_param(3,DBPARAMIO_INPUT)] TCHAR m_column2[20];

[ db_param(4,DBPARAMIO_INPUT)] TCHAR m_column3[20];

void GetRowsetProperties(CDBPropSet* pPropSet)

{

pPropSet->AddProperty(DBPROP_CANFETCHBACKWARDS, true, DBPROPOPTIONS_OPTIONAL);

pPropSet->AddProperty(DBPROP_CANSCROLLBACKWARDS, true, DBPROPOPTIONS_OPTIONAL);

pPropSet->AddProperty(DBPROP_IRowsetChange, true, DBPROPOPTIONS_OPTIONAL);

pPropSet->AddProperty(DBPROP_UPDATABILITY, DBPROPVAL_UP_CHANGE | DBPROPVAL_UP_INSERT | DBPROPVAL_UP_DELETE);

}

};

编写CAddRecord类,该类从CDataRowset类派生,这里只能使用继承而不是直接修改CDataRowset类,是因为db_command属性导致了CDataRowset类引入了一些隐藏类,这些隐藏类会导致一些意外行为。

CAddRecord类将提供设置成员变量和清除成员变量的方法。代码如下:

void CAddRecord::Clear(void)

{

//set member to zero

m_column0[0]=0;

m_column1[0]=0;

m_column2[0]=0;

m_column3[0]=0;

}

void CAddRecord::SetStudentInfo(BSTR Name,BSTR Age,BSTR Gender,BSTR Address)

{

wcscpy(m_column0,Name);

wcscpy(m_column1,Age);

wcscpy(m_column2,Gender);

wcscpy(m_column3,Address);

}

创建数据源连接对象

删除CDataRowset类的db_source属性,该属性帮助我们创建CDataSource对象,我们这里不需要。

定义连接字符串宏

#define CONN_STRINGW L"Provider=SQLOLEDB.1;Password=sa;Persist Security Info=True;User ID=sa;Initial Catalog=/x6d4b/x8bd5/x6570/x636e/x5e93;Data Source=GAO;Use Procedure for Prepare=1;Auto Translate=True;Packet Size=4096;Workstation ID=FREEBIRD;Use Encryption for Data=False;Tag with column collation when possible=False"

修改CHighPerformance类的代码:

HTTP_CODE InitializeHandler(AtlServerRequest *pRequestInfo, IServiceProvider *pProvider)

{

if (HTTP_SUCCESS != CSoapHandler<CHighPerformanceService>::InitializeHandler(pRequestInfo, pProvider))

return HTTP_FAIL;

if (S_OK != GetDataSource( pProvider,

CONN_STRINGW,

CONN_STRINGW,

&m_dc ))

return HTTP_FAIL;

return HTTP_SUCCESS;

}

m_dc是该类的私有成员变量,类型为CDataConnection

[ soap_method ]

HRESULT AddNewStudent(BSTR Name,BSTR Age,BSTR Gender,BSTR Address)

{

m_rec.SetStudentInfo(Name,Age,Gender,Address);

HRESULT hr=m_rec.OpenRowset(m_dc);

return hr;

}

m_rec定义为CAddRecord m_rec;

使用UDL文件代替连接字符串

UDL文件中获取连接字符串,需要创建MSDAINITIALIZE对象,并调用IDataInitialize接口的LoadStringFromStorage方法才行。这种动作我并不想放到InitializeHandler函数中反复做。唯一的办法是放到ISAPI扩展DLL中。

我们应该在类CHighPerformanceExtension中维护一个成员变量,当我们在应用程序DLL中通过QueryService查询某个接口(这里名叫IUdl)时,我们可以获得该变量的实现的IUdl接口,通过IUdl的方法GetConnectString获取连接字符串。这样我们就可以将该连接字符串作为参数传递给GetDataSource函数,从而取代宏。

实现IUdl接口的对象将在创建时读取c:/gao.udl文件,并将连接字符串保存,IUdl::GetConnectString方法将返回连接字符串。

这里涉及到几个知识点,如何在ATL SERVER中开发一个COM-LIKE 接口,并且将接口暴露为服务;如通过OLE DB组件读取UDL文件。源代码如下,请参考:

#pragma once

//interface IUdl

__interface __declspec(uuid("F4FA35D6-D26D-4b5a-9544-A69F66E927B8"))

IUdl:public IUnknown

{

HRESULT GetConnectString(BSTR* pConString);

};

#pragma once

//#include <stdafx.h>

#include "../CommonServiceDefinition.h"

#include <fstream>

using namespace std;

class CUdl :

public IUdl

{

public:

CUdl()

{

CComPtr<IDataInitialize> spDataInitialize;

HRESULT hr = spDataInitialize.CoCreateInstance( __uuidof(MSDAINITIALIZE));

if (SUCCEEDED(hr))

hr=spDataInitialize->LoadStringFromStorage(CComBSTR("c://gao.udl"), &m_ConnString);

}

ULONG STDMETHODCALLTYPE AddRef() {return 1;}

ULONG STDMETHODCALLTYPE Release() {return 1;}

HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject)

{

if( !ppvObject )

return E_POINTER;

if( IsEqualIID( riid, IID_IUnknown))

*ppvObject = static_cast<IUnknown*>(this);

else if( IsEqualIID( riid, __uuidof(IUdl)))

*ppvObject = static_cast<IUdl*>(this);

else

return E_NOINTERFACE;

AddRef();

return S_OK;

}

HRESULT GetConnectString(BSTR* pConString)

{

if( pConString==NULL)

{

return E_INVALIDARG;

}

CComBSTR bstr(m_ConnString);

*pConString=bstr.Detach();

return S_OK;

}

private:

CComHeapPtr<OLECHAR> m_ConnString;

};

CHighPerformanceExtension中声明变量CUdl m_udl;并且改写QueryService方法

HRESULT STDMETHODCALLTYPE QueryService(REFGUID guidService,

REFIID riid, void** ppvObject)

{

if (InlineIsEqualGUID(guidService, __uuidof(IDataSourceCache)))

{

CIsapiWorker *pWorker = GetThreadWorker();

if (pWorker)

{

CDataSourceCache<> *pCache = NULL;

if (pWorker->GetWorkerData(_DATASOURCE_CACHE, (void **)&pCache))

{

*ppvObject = static_cast<IDataSourceCache *>(pCache);

return S_OK;

}

}

}

if(InlineIsEqualGUID(guidService,__uuidof(IUdl)) &&

InlineIsEqualGUID(riid,__uuidof(IUdl)) )

{

return m_udl.QueryInterface(riid,ppvObject);

}

return baseISAPI::QueryService(guidService, riid, ppvObject);

}

我们的应用程序dll中的代码如下:

// HighPerformance.h : 定义 ATL Server 请求处理程序类

//

#pragma once

#include "addrecord.h"

#include "../CommonServiceDefinition.h"

namespace HighPerformanceService

{

// webservice 的所有 structenum typedefs 应进入命名空间

// IHighPerformanceService - Web 服务接口声明

//

[

uuid("EECB2E3E-9CA8-4E07-8DE9-81A21E99E707"),

object

]

__interface IHighPerformanceService

{

[id(1)] HRESULT AddNewStudent([in]BSTR Name,[in]BSTR Age,[in]BSTR Gender,[in]BSTR Address);

};

// HighPerformanceService - Web 服务实现

//

[

request_handler(name="Default", sdl="GenHighPerformanceWSDL"),

soap_handler(

name="HighPerformanceService",

namespace="urn:HighPerformanceService",

protocol="soap"

)

]

class CHighPerformanceService :

public IHighPerformanceService

{

public:

HTTP_CODE InitializeHandler(AtlServerRequest *pRequestInfo, IServiceProvider *pProvider)

{

if (HTTP_SUCCESS != CSoapHandler<CHighPerformanceService>::InitializeHandler(pRequestInfo, pProvider))

return HTTP_FAIL;

CComPtr<IUdl> spUdl;

HRESULT hr=pProvider->QueryService(__uuidof(IUdl),&spUdl);

if(hr!=S_OK)

return HTTP_FAIL;

CComBSTR connectstring;

hr=spUdl->GetConnectString(&connectstring);

if(hr!=S_OK)

{

return HTTP_FAIL;

}

if (S_OK != GetDataSource( pProvider,

connectstring,

connectstring,

&m_dc ))

{

return HTTP_FAIL;

}

return HTTP_SUCCESS;

}

[ soap_method ]

HRESULT AddNewStudent(BSTR Name,BSTR Age,BSTR Gender,BSTR Address)

{

m_rec.SetStudentInfo(Name,Age,Gender,Address);

HRESULT hr=m_rec.OpenRowset(m_dc);

return hr;

}

private:

CDataConnection m_dc;

CAddRecord m_rec;

// TODO: 在此添加其他 Web 服务方法

}; // CHighPerformanceService

} // 命名空间 HighPerformanceService

Web Service调试

MSDN提供一个例子---SOAPDebugApp 示例:在客户端内存空间中调试 XML Web services

地址:ms-help://MS.MSDNQTR.2003FEB.2052/vcsample/html/vcsamsoapdebugappsample.htm

这种方式不同真正的WEB调用,而是通过直接调用应用程序DLL,这种方式很好,但是有局限性,由于我们的InitializeHandler要在每次调用WEB方法之前执行,并且每次都要从ISAPI扩展DLL线程池中打交道,而这种模拟调用不会导致IIS加载ISAPI扩展DLL。所以,InitializeHandler会失败,我们就没有办法对WEB 方法进行断点跟踪。解决之道有两种,一是用文件输出的方式,而是先不用扩展DLL缓存,当确信应用程序DLL已经没有问题后,再进行修改。但这两种方法都比较麻烦,不知道还有没有跟好的办法?

另外,调试的时候IIS容易出现不正常,请用iisreset命令重启。

性能评测

我编写客户端调用AddNewSutdent方法,一切成功。三个客户端同时各自写入2万笔纪录,总数6万,好极了。性能感觉非常好。

分享到:
评论

相关推荐

    asp.net完全入门

     第三篇 “数据库编程”——本篇详细介绍了数据库编程的基础、ADO.NET数据库编程的基础、ADO.NET数据库基本操作、Dataset的用法和数据绑定技术,是制作动态页面、BBS、电子商务网站的等网站应用程序的基础。...

    JAVA高并发高性能高可用高扩展架构视频教程

    企业必备技能之面向服务编程Web-Service详解 分布式服务下的交易一致性原理及解决 分布式服务框架(dubbo+zookpeer) WEB高级前后台分离思维-懒加载无限级树形菜单 动态页面的静态化处理 大并发展示优化,动态页面的...

    ASP.NET完全入门

     第三篇 “数据库编程技术”——本篇详细介绍了数据库编程的基础、ADO.NET数据库编程的基础、ADO.NET数据库基本连接和操作、Dataset的用法和数据绑定等技术,是制作动态页面、BBS、电子商务网站的等网站应用程序的...

    中美 IT 培训 C# Asp.net 笔记2

    Windows 应用程序、设计模式和Oracle数据库编程(40课时) 系统地讲授Windows应用程序的开发,学习观察者模式和Oracle数据库编程。 熟练开发基于数据库的Windows应用程序,掌握Oracle数据库编程。 C# 高级编程II ...

    中美 IT 培训 C# Asp.net 全套笔记1

    Windows 应用程序、设计模式和Oracle数据库编程(40课时) 系统地讲授Windows应用程序的开发,学习观察者模式和Oracle数据库编程。 熟练开发基于数据库的Windows应用程序,掌握Oracle数据库编程。 C# 高级编程II ...

    中美 IT 培训 C# Asp.net 笔记3

    Windows 应用程序、设计模式和Oracle数据库编程(40课时) 系统地讲授Windows应用程序的开发,学习观察者模式和Oracle数据库编程。 熟练开发基于数据库的Windows应用程序,掌握Oracle数据库编程。 C# 高级编程II ...

    ASP.Net 开发手册

    第三篇 ADO.NET数据库编程 第一章 ADO.NET简介 第二章 访问数据库 第三章 ADO.NET数据连接方法 第四章 ADO.NET数据库基本操作 第五章 Dataset的用法 第六章 数据绑定 第四篇 应用程序 第一章 什么是...

    asp.net教程

    &lt;br&gt; 第三篇 “数据库编程技术”——本篇详细介绍了数据库编程的基础、ADO.NET数据库编程的基础、ADO.NET数据库基本连接和操作、Dataset的用法和数据绑定等技术,是制作动态页面、BBS、电子商务网站的等网站...

    《ASP.Net 手册》。金桥网络信息服务网

    第三篇 ADO.NET数据库编程 第一章 ADO.NET简介 第二章 访问数据库 第三章 ADO.NET数据连接方法 第四章 ADO.NET数据库基本操作 第五章 Dataset的用法 第六章 数据绑定 第四篇 应用程序 第一章 什么是应用程序 第二...

    《ASP.NET 完全入门》

    第三篇 “数据库编程技术”——本篇详细介绍了数据库编程的基础、ADO.NET数据库编程的基础、ADO.NET数据库基本连接和操作、Dataset的用法和数据绑定等技术,是制作动态页面、BBS、电子商务网站的等网站应用程序的...

    值类型与引用类型理论内容.part01.rar

    Windows 应用程序、设计模式和Oracle数据库编程(40课时) 系统地讲授Windows应用程序的开发,学习观察者模式和Oracle数据库编程。 熟练开发基于数据库的Windows应用程序,掌握Oracle数据库编程。 C# ...

    值类型与引用类型理论内容.part05.rar

    Windows 应用程序、设计模式和Oracle数据库编程(40课时) 系统地讲授Windows应用程序的开发,学习观察者模式和Oracle数据库编程。 熟练开发基于数据库的Windows应用程序,掌握Oracle数据库编程。 C# ...

    Asp.net入门详细介绍

    .net入门必读,全篇分为Web Form,ADO.net数据库编程,应用程序,Web Service,性能优化,和高级应用共六篇。

    Visual.Basic.2010.&.NET4.高级编程(第6版)-文字版.pdf

    1.5.8 性能工具 60 1.6 小结 62 第2章 对象和visual basic 63 2.1 面向对象的术语 64 2.1.1 对象、类和实例 64 2.1.2 对象的组成 65 2.1.3 system.object 68 2.2 使用visual basic类型 68 2.2.1 值...

    Asp.net中文手册(CHM)

    第三篇 ADO.NET数据库编程 第一章 ADO.NET简介 第二章 访问数据库 第三章 ADO.NET数据连接方法 第四章 ADO.NET数据库基本操作 第五章 Dataset的用法 第六章 数据绑定 第四篇 应用程序 ...

Global site tag (gtag.js) - Google Analytics