65.9K
CodeProject 正在变化。 阅读更多。
Home

非托管应用程序的基于 Web 的数据访问对象

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.81/5 (5投票s)

2013年4月25日

CPOL

3分钟阅读

viewsIcon

18730

downloadIcon

224

非托管代码应用程序通过 Web 服务访问数据库中的数据,无需数据库提供商。

引言

我描述了托管代码应用程序(基于 Web 的数据访问对象)如何在没有数据库提供商的情况下通过 Web 服务访问数据库中的数据。今天,我将描述非托管代码应用程序如何通过 Web 服务访问数据库中的数据。非托管 C++ 可以直接引用 Web 服务。但这没什么用,因为非托管 C++ 不支持 .NET Framework 的 [System.Data.DataTable] 类。在没有 [System.Data.DataTable] 类的情况下处理记录集形式的数据非常困难。因此,我提出了一种方法,即非托管代码调用 COM DLL(动态链接库),该 DLL 引用 ASP.NET 的 Web 服务来实现 Web-DAO。当客户端请求 SQL 查询时,Web 服务器通过数据库提供商查询。然后将记录集形式的结果数据发送给客户端。客户端的托管代码 DLL 从 Web 服务器接收数据,并将其分配给 [System.Data.DataTable] 类型的类变量。非托管代码应用程序从 COM DLL 的已分配变量中获取每个数据值。下图描述了非托管代码应用程序如何通过 COM DLL 访问数据库中的数据。

          

背景

首先,我们需要了解为什么要通过 Web 服务访问数据库中的数据以及如何访问。我们还应该了解如何创建托管代码的 COM DLL 以及在非托管代码中调用 COM DLL 的方法。以下文章对此进行了描述。

使用代码

步骤 1. 创建一个基于托管 C# 的 COM DLL    

1. 为外部调用函数创建一个接口 [IWebDatabaseAccessObject]。

[Guid("db0eef1a-22e2-4cc2-9a9c-58d5de4614ae")]
public interface IWebDatabaseAccessObject
{
    [DispId(1)]void SetConnectionString(string strConnection);
    [DispId(2)]void SetConnection(string strIPAddress, int nPortNo, bool bSSL);
    [DispId(3)]int ExecuteNonQuery(string strSQL);
    [DispId(4)]bool Open(string strSQL);
    [DispId(5)]bool IsEOF();
    [DispId(6)]bool Close();
    [DispId(7)]string GetFieldStringByColumnNo(int nColumnNo);
    [DispId(8)]string GetFieldStringByColumnName(string strColumnName);
    [DispId(9)]byte[] GetBlobFieldValueByColumnNo(int nColumnNo);
    [DispId(10)]byte[] GetBlobFieldValueByColumnName(string strColumnName);
    [DispId(11)]bool MoveNext();
    [DispId(12)]int GetColumnSize();
    [DispId(13)]int GetRecordCount();
    [DispId(14)]string GetFieldNameByColumnNo(int nColumnNo);
    [DispId(15)]int ExecuteNonQueryBlob(string strSQL, string strParameter, byte[] arbBlobValue);
}  

2. 实现接口 [IWebDatabaseAccessObject] 的类 [WebDatabaseAccessObject]。

    [Guid("6ff59b6a-fb7e-4df6-9399-541ce0cd8632")]
    [ClassInterface(ClassInterfaceType.None)]
    [ProgId("WebDAO")]  
    public class WebDatabaseAccessObject : IWebDatabaseAccessObject

3. 创建一个函数,为 ASP.NET 的 Web 服务分配 Web 服务器信息。

public string ConnectionString
{
    get { return String.Format(@"{0}/{1}", 
            strConnection.TrimEnd('/'), strWebServiceFileName); }
}

// Set IP Address and Port No. of Web Server
public void SetConnectionString(string strConnection)
{
    this.strConnection = strConnection;
}

public void SetConnection(string strIPAddress, int nPortNo, bool bSSL)
{
    this.strConnection = String.Format("http{3}://{0}:{1}", 
          strIPAddress, nPortNo, (bSSL) ? "s" : null);
}

4. 我们编写代码以向 Web 服务器请求 SQL 查询,并将记录集形式的结果数据分配给 [System.Data.DataTable] 类型的变量。Open 函数连接到 Web 服务的 Web 服务器,接收结果数据并异常地将其分配给变量。

// Get Data in a Database by the Parameter-SQL
public bool Open(string strSQL)
{
    DSWebDAO.WebServiceDAO wsdWebServiceDAO = new DSWebDAO.WebServiceDAO();
    wsdWebServiceDAO.Url = ConnectionString;
    try
    {
        dtRecodeSet = wsdWebServiceDAO.GetRecodeSet(strSQL);
        nRecodeIndex = 0;
    }
    catch
    {
    }
    return (dtRecodeSet != null);
}  

5. 我们应该在 COM DLL 中创建类似于 ADO 或 DAO 结构的成员函数。MoveNext 函数移动到记录集形式数据的下一条记录行,GetFieldValue 函数可以获取选定记录行中的每个列数据。

// Move Next Row
public bool MoveNext()
{
    bool bReturnFlag = false;
    if (dtRecodeSet != null)
        if (bReturnFlag = (nRecodeIndex < dtRecodeSet.Rows.Count))
            nRecodeIndex++;
    return bReturnFlag;
}

// Get Selected Column Value of Selected Row
object GetFieldValue(object objColumn)
{
    object objReturn = null;
    try
    {
        if (dtRecodeSet != null)
            if (nRecodeIndex >= 0 && nRecodeIndex < dtRecodeSet.Rows.Count)
            {
                switch (Type.GetTypeCode(objColumn.GetType()))
                {
                    case TypeCode.Int16:
                    case TypeCode.Int32:
                    case TypeCode.Int64:
                        objReturn = dtRecodeSet.Rows[nRecodeIndex][Convert.ToInt32(objColumn)];
                        break;
                    default:
                        objReturn = dtRecodeSet.Rows[nRecodeIndex][(DataColumn)objColumn];
                        break;
                }
            }
    }
    catch
    {
    }
    return objReturn;
}
string GetFieldString(object objColumn)
{
    return Convert.ToString(GetFieldValue(objColumn));
}
public string GetFieldStringByColumnName(string strColumnName)
{
    return GetFieldString(dtRecodeSet.Columns[strColumnName]);
}
public string GetFieldStringByColumnNo(int strColumnNo)
{
    return GetFieldString(strColumnNo);
}  

步骤 2. 创建一个非托管 C++ 的包装类来调用托管 C# COM DLL  

接下来,在生成类型库 (tlb) 文件后,我们将像下面的代码一样导入它。

#import "WebDAO.tlb" named_guids raw_interfaces_only  

我们应该为类型库 (tlb) 文件创建一个包装类,并可以在包装类中插入附加函数。例如,GetNumberByColumnNo 函数可以将 GetStringByColumnNo 函数转换为获取数字类型的值。

CWebDAO::CWebDAO(CString strConnection)
{
    bCOMInitailzed = Initialize();
    BSTR bstrTemp = strConnection.AllocSysString();
    m_pDotNetCOMPtr->SetConnectionString(bstrTemp);
    SysFreeString(bstrTemp);
}
 
BOOL CWebDAO::Initialize()
{
    HRESULT hRes = m_pDotNetCOMPtr.CreateInstance(WebDAO::CLSID_WebDatabaseAccessObject);
    return (hRes == S_OK);
}
 
BOOL CWebDAO::Open(CString strSQL, BOOL bOnlyTableName, BOOL bBlob, BOOL bUseClient)
{
    VARIANT_BOOL vbRetVal;
    if(bCOMInitailzed)
    {
        BSTR bstrTemp = strSQL.AllocSysString();
        m_pDotNetCOMPtr->Open(bstrTemp, &vbRetVal);
        SysFreeString(bstrTemp);
    }
    return (bCOMInitailzed &&(vbRetVal == -1));
} 
 
int CWebDAO::GetNumberByColumnNo(int nColumnNo)
{
    int nReturn = -99999;
    CString strReturn = _T("");
    try
    {
        if(bCOMInitailzed)
        {
            BSTR bstrReturn;
            m_pDotNetCOMPtr->GetFieldStringByColumnNo(nColumnNo, &bstrReturn);
            strReturn = (LPCTSTR)(_bstr_t)bstrReturn;
        }
        nReturn = atoi((LPSTR)(LPCTSTR)strReturn);
    }
    catch (CException* e)
    {
        e->Delete();
    }
 
    return nReturn;
}  

步骤 3. 非托管 C++ 应用程序调用托管 C# COM DLL 的包装类  

非托管 C++ 应用程序必须调用 CoInitialize 函数来初始化和反初始化 COM 组件。

CoInitialize(NULL);

int CWebDAOTestApp::ExitInstance()
{
    // TODO: Add your specialized code here and/or call the base class
    CoUninitialize();
 
    return CWinApp::ExitInstance();
}

在运行 IIS(Internet Information Services)之后,运行非托管 C++ 应用程序 [WebDAOTest]。请在 [Web Address] 文本框中输入 ASP.NET 开发服务器的根 URL。如果您正在运行 Web 服务的实际 Web 服务器,请输入 Web-DAO 的实际 URL。如果应用程序正确连接到 Web 服务器,您将能够获得结果数据。

关注点

通常,ADO 或 DAO 的 Open 函数连接到数据库服务器并保持连接。但是 WebDatabaseAccessObject 类的 Open 函数不同,因为 Web 服务是不连续的、请求-响应模式。因此,WebDatabaseAccessObject 类的 Open 函数连接到 Web 服务的 Web 服务器,此外,它接收结果数据并将其分配给 System.Data.DataTable 类的变量。如果您想在结果数据中包含 BLOB(二进制大对象),您应该使用 CWebDAO 的 GetBlobFieldValue 函数而不是 GetFieldValue 函数。如果您希望包装类 CWebDAO 替换现有的 ADO 类或包装,您应该参考以下代码。如果您修改了 CWebDAO 包装类的函数和变量名,现有的类将很容易被替换。

#ifdef _WEB_
    CWebDAO recordSet(CDwUtility::GetWebDAOConnection());
#else
    CDwAdoRecord recordSet(CDwUtility::GetDBConnection());            
#endif
    CString strSQL;
    strSQL.Format("SELECT COUNT(*) AS EXAM_COUNT FROM T_CODE WHERE EXAM_KEY = %s", m_strExamKey);
    
    BOOL bExamCount = FALSE;
    if (recordSet.Open(strSQL))
    {
        while (recordSet.IsEOF() == FALSE)
        {
            int nCount;
            recordSet.GetFieldValue(_T("EXAM_COUNT"), nCount);
            bExamCount = (nCount > 0);
            recordSet.MoveNext();
        }
        recordSet.Close();
    }
© . All rights reserved.