vs2019-MFC-ODBC使用者向导的手动实现
最近在一门课程中,要求学习使用MFC-ODBC,在操作时,发现在Visual Studio 2019版本中找不到MFC ODBC使用者向导,查了半天资料最后发现vs2019已弃用了这个wizard
并且我们能看到微软的NOTE:
But,how to create consumer manually?
作为这方面的新新手,经过不断尝试,找到了以下几种解决方案
NO.1-拷贝
第一种方法也是最简单的方法(简单但并不省事)
就是找到一台安装有旧版本vs的计算机(自己下载或者寻求同学的帮助),在这台机子上完成相关类的新建后,把自动生成的XXX.h
和XXX.cpp
文件拷贝到自己的机子,导入,就可以继续操作了
NO.2-直接使用CRecordset
类操作
对MFC ODBC编程主要有5个类,其中的CDatabase和CRecordset都是从基类CObject派生而来的。
CDatabase类用于建立应用程序与数据源的连接。
CRecordset类用于从数据源中选一组记录(记录集)。
也就是说,当我们进行MFC ODBC编程的时候,首先要利用CDatabase
类建立同ODBC数据源的连接,接下来就用CRecordset
类与当前建立的数据源绑定,此时关于数据库的编程操作都放到了记录集上,最后要关闭记录集以及同数据源的连接
根据这些,我们可以得出其实我们是可以直接使用CRecordset
类来进行简单操作的,不必强求wizard自动生成的类。
接下来我们开始编程:
-
前提:
- vs项目:基于MFC的对话框程序
- ODBC数据源:ODBCDemo 通过使用windows管理工具中ODBC Data Sources (32-bit)创建
- 在
pch.h
头文件中添加#include<afxdb.h>
-
建立连接
//在XXXDlg.h文件中的XXXDlg类中加入 public: CDatabase m_db; //数据库 //在XXXDlg.cpp文件中 XXXDlg类的构造函数中加入 if (!m_db.Open(_T("ODBCDemo"), FALSE, FALSE,L"ODBC;")) { AfxMessageBox(L"不能打开数据库"); }
-
初始化记录集并获取第一条记录
//在XXXDlg.h文件中的XXXDlg类中加入 public: CRecordset* m_rs; //记录集指针 //在XXXDlg.cpp文件中 XXXDlg类的构造函数中加入 if() {/*刚刚加入的if语句*/} else { m_rs = new CRecordset(&m_db); //记录集指针 m_rs->Open(CRecordset::snapshot, L"SELECT `sno`,`sname`,`ssex`,`sage`,`depart` FROM `Student`"); //注意:这里的sql语句不能直接写表名(虽然帮助文档说可以,或许还需要其他代码) if (!m_rs->IsOpen()) { AfxMessageBox(L"数据集创建失败"); } }
可以看到,我们要操作的表是ODBCDemo中的Student表:(表中数据仅供测试,并无实际意义)
-
创建界面 如下图,编辑框分别为IDC_EXIT1-5,查询按钮为IDC_BUTTON1,并为其添加事件处理程序
-
编写按钮响应代码,实现 每按一次查询按钮,从表中读一条数据,直到读完
void CDateBaseCodeDlg::OnBnClickedButton1() //为查询按钮自动生成的事件处理函数 { if (!m_rs->IsOpen()) { AfxMessageBox(L"记录集已关闭"); return; } CDBVariant varValue; //用来存储字段的值 CDBVariant对象 short nFields = m_rs->GetODBCFieldCount(); //获取总共多少条记录 if (!m_rs->IsEOF()) //判断是否还有记录 { for (short i = 0; i < nFields; ++i) //循环获取当前记录中每个字段的值并显示 { m_rs->GetFieldValue(i, varValue); SetDlgItemTextW(IDC_EDIT1 + i, *varValue.m_pstringW); } m_rs->MoveNext(); } else { AfxMessageBox(L"已无记录"); m_rs->Close(); m_db.Close(); } }
-
程序效果:
-
PS:关于代码中用到的一些函数的具体使用可以参考vsHelpViewer或者文末链接
NO.3-构造CRecordset的派生类
其实vs之前的MFC-ODBC consumer wizard就是在帮我们做这个工作,那么我们自然也可以手动完成
在本例中,如****意:注释掉的是 使用wizard会自动生成的代码,手写的时候可以选择不写)
// Student.h : CStudent 的声明
#pragma once
#include<afxdb.h>
class CStudent : public CRecordset
{
public:
CStudent(CDatabase* pDatabase = NULL);
//DECLARE_DYNAMIC(CStudent)
// 字段/参数数据
CStringW m_sno;
CStringW m_sname;
CStringW m_ssex;
CStringW m_sage;
CStringW m_depart;
// 重写
public:
virtual CString GetDefaultConnect(); // 默认连接字符串
virtual CString GetDefaultSQL(); // 记录集的默认 SQL
virtual void DoFieldExchange(CFieldExchange* pFX); // RFX 支持
// 实现
/*
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
*/
};
// Student.h : CStudent 类的实现
#include "pch.h"
#include "Student.h"
//IMPLEMENT_DYNAMIC(CStudent, CRecordset)
CStudent::CStudent(CDatabase* pdb):CRecordset(pdb)
{
m_sno = L"";
m_sname = L"";
m_ssex = L"";
m_sage = L"";
m_depart = L"";
m_nFields = 5;
//m_nDefaultType = dynaset; 记录集默认类型
}
//#error Security Issue: The connection string may contain a password
CString CStudent::GetDefaultConnect()
{
return _T("DSN=ODBCDemo;")
//DBQ=C:\\Users\\dell\\Desktop\\score_student.mdb;DriverId=25;FIL=MSAccess;MaxBufferSize=2048;PageTimeout=5;UID=admin; 连接字符串自定义
}
CString CStudent::GetDefaultSQL()
{
return _T("[Student]");
}
void CStudent::DoFieldExchange(CFieldExchange* pFX)
{
pFX->SetFieldType(CFieldExchange::outputColumn);
RFX_Text(pFX, _T("[sno]"), m_sno);
RFX_Text(pFX, _T("[sname]"), m_sname);
RFX_Text(pFX, _T("[ssex]"), m_ssex);
RFX_Text(pFX, _T("[sage]"), m_sage);
RFX_Text(pFX, _T("[depart]"), m_depart);
}
/*
// CStudent 诊断
#ifdef _DEBUG
void CStudent::AssertValid() const
{
CRecordset::AssertValid();
}
void CStudent::Dump(CDumpContext& dc) const
{
CRecordset::Dump(dc);
}
#endif //_DEBUG
*/
通过观察可以发现,继承自CRecordset类的这个CStudent类关键在于以下几项:
- 成员变量:和所选表字段一一对应的几个变量
- 构造函数:初始化变量 并初始化 m_nFields(字段个数)(这个必须有)
-
GetDefaultConnect()
:返回默认的连接字符串 -
GetDefaultSQL()
:返回默认的sql查询语句(这里是可以直接写表名的) -
DoFieldExchange(CFieldExchange* pFX)
:最关键的一个函数,把各个字段的值对应转换为CStudent类中各个成员变量的值
该类的使用就不必赘述了,记得Open()和Close()就好
总结
经过bz的一番折腾,get√到了这几种方法
- 得到自动生成的文件,并添加进项目中 ————这种方式……没什么多说的
- 直接使用
CRecordset
类进行操作 ————code相对来说简单,但可能会受到功能上的限制以及一些奇怪的bug - 声明并实现
CRecordset
类的派生类 ————符合面对对象的设计方式,而且微软自己就是这样干的,不过code相对复杂
最后,bz也正在学习中,如果文章有错误欢迎大家指正,或者大家有更好的解决方案可以一起交流
希望能够帮到大家
参见:
上一篇: SearchView使用详解
下一篇: vscode配置