欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

vs2019-MFC-ODBC使用者向导的手动实现

程序员文章站 2022-06-01 10:23:19
...

最近在一门课程中,要求学习使用MFC-ODBC,在操作时,发现在Visual Studio 2019版本中找不到MFC ODBC使用者向导,查了半天资料最后发现vs2019已弃用了这个wizard
vs2019-MFC-ODBC使用者向导的手动实现
并且我们能看到微软的NOTE:
vs2019-MFC-ODBC使用者向导的手动实现
But,how to create consumer manually?
作为这方面的新新手,经过不断尝试,找到了以下几种解决方案


NO.1-拷贝

第一种方法也是最简单的方法(简单但并不省事)

就是找到一台安装有旧版本vs的计算机(自己下载或者寻求同学的帮助),在这台机子上完成相关类的新建后,把自动生成的XXX.hXXX.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,并为其添加事件处理程序
    vs2019-MFC-ODBC使用者向导的手动实现vs2019-MFC-ODBC使用者向导的手动实现

  • 编写按钮响应代码,实现 每按一次查询按钮,从表中读一条数据,直到读完

    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();
    	}
    }
    
  • 程序效果:
    vs2019-MFC-ODBC使用者向导的手动实现

  • 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√到了这几种方法

  1. 得到自动生成的文件,并添加进项目中 ————这种方式……没什么多说的
  2. 直接使用CRecordset类进行操作 ————code相对来说简单,但可能会受到功能上的限制以及一些奇怪的bug
  3. 声明并实现CRecordset类的派生类 ————符合面对对象的设计方式,而且微软自己就是这样干的,不过code相对复杂

最后,bz也正在学习中,如果文章有错误欢迎大家指正,或者大家有更好的解决方案可以一起交流
希望能够帮到大家


参见:

相关标签: 解决问题