![]() |
![]() |
Как в VFP 7.0 и выше обратиться к Хранимой Процедуре VFP-базы данных через OLE DB |
> Попробовал через OLE DB обратиться к ХП VFP-базы данных,
> однако получаю ошибки... Можно это как-то побороть?
Признаться у меня тоже получилось не сразу :-) ... И я наткнулся по крайней мере на три проблемы:
Обратите также внимание на то, что поскольку функция вместе со значениями параметров передаётся как команда в виде единой строки, то для параметров типа строки (если такие имеются) возникает проблема выбора ограничителей строки в зависимости от значений в таких строковых параметров... :-(
Ниже два примера кода, показывающие как можно осуществить вызов процедуры RemainingCredit() из демонстрационного примера TasTrade.
1. Пример кода на JScript:
//////////// Save as file: SP_test.wsf
<package>
<job>
<script id="main" language="jscript">
var gcCntStr = "Provider=VFPOLEDB.1;Data Source=c:\\program files\\microsoft visual foxpro 7\\samples\\tastrade\\data\\tastrade.dbc;";
var goCnt = null; // As ADODB.Connection
var goCmd = null; // As ADODB.Command
var goRS = null; // As ADODB.Recordset
var gvRetVal = 0; // As Variant
var gErrCode = 1;
var adStateOpen = 1;
var adStateClosed = 0;
var adCmdText = 1;
try
{
goCnt = WScript.CreateObject("ADODB.Connection");
goCnt.ConnectionString = gcCntStr;
goCnt.Open();
if (goCnt.State == adStateOpen)
{
goCmd = WScript.CreateObject("ADODB.Command");
goCmd.CommandText = "RemainingCredit('ALFKI')";
goCmd.CommandType = adCmdText;
goCmd.ActiveConnection = goCnt;
goRS = goCmd.Execute;
gvRetVal = goRS.Fields(0).Value;
goCnt.Close();
gErrCode = 0;
}
}
catch(err)
{
WScript.Echo("Error occurred"
+ "\nCode: " + hex(err.number)
+ "\nDescription: " + err.description);
gErrCode = 1;
}
finally
{
if (goCnt != null && goCnt.State == adStateOpen)
{
goCnt.Close();
}
goRS = null;
goCmd = null;
goCnt = null;
if (gErrCode == 0)
{
WScript.Echo("RemainingCredit('ALFKI') = " + gvRetVal);
}
WScript.Quit(gErrCode);
}
//////////////////////////
function hex(nmb)
{
if (nmb > 0)
return nmb.toString(16);
else
return (nmb + 0x100000000).toString(16);
}
</script>
</job>
</package>
//////////// The end of file: SP_test.wsf
2. Пример кода на CSharp
//////////// File: SP_test.cs
using System;
using System.Data;
using System.Data.OleDb;
namespace ConsoleApp
{
///
/// Summary description for MyCls.
///
class MyCls
{
///
/// The main entry point for the application.
///
[STAThread]
static void Main(string[] args)
{
const string strCntStr = @"Provider=VFPOLEDB.1;Data Source=c:\program files\microsoft visual foxpro 7\samples\tastrade\data\tastrade.dbc;";
const string strCmd = @"RemainingCredit('ALFKI')";
OleDbConnection oleDbConnection = null;
OleDbCommand oleDbCommand = null;
object objRetVal = null;
try
{
oleDbConnection = new OleDbConnection();
oleDbConnection.ConnectionString = strCntStr;
oleDbConnection.Open();
if (oleDbConnection.State == ConnectionState.Open)
{
oleDbCommand = oleDbConnection.CreateCommand();
oleDbCommand.CommandType = CommandType.Text;
oleDbCommand.CommandText = strCmd;
objRetVal = oleDbCommand.ExecuteScalar();
oleDbCommand.Cancel();
if (objRetVal != null)
{
System.Console.WriteLine(strCmd + " = " + objRetVal);
}
}
}
catch(OleDbException e)
{
string Msg = e.Message;
System.Console.WriteLine(Msg);
}
finally
{
if (oleDbCommand != null)
{
oleDbCommand.Dispose();
oleDbCommand = null;
}
if (oleDbConnection != null)
{
if (oleDbConnection.State != ConnectionState.Closed)
{
oleDbConnection.Close();
}
oleDbCommand = null;
}
}
}
}
}
//////////// The end of file: SP_test.cs
см. также: Q299820 Knowledge Base Articles HOWTO: Execute a Stored Procedure in a VFP Database with the VFP OLE DB Provider
Начиная с версии 8.0 возможно использование коллекции параметров, так ниже приведён C#-код, показывающий как можно выполнить параметризованный запрос 'Employee listing' из VFP-базы данных Tastrade.dbc. Чтобы получить определяющее выражение SQL-SELECT параметризованного View, нужно добавить в VFP-базу данных Tastrade.dbc хранимую процедуру __spDbGetProp(), являющуюся обёрткой для обращения к VFP-функции BDGETPROP().
FUNCTION __spDbGetProp LPARAMETERS tcName, tcType, tcProperty RETURN DBGETPROP(tcName, tcType, tcProperty) ENDFUNCдалее код, демонстрирующий выполнение этого параметризованного View, может быть таким:
using System;
using System.Data;
using System.Data.OleDb;
namespace VfpPrmView
{
///
/// Summary description for VfpPrmView
///
class testVfpOleDb
{
///
/// The main entry point for the application.
///
[STAThread]
static void Main(string[] args)
{
VfpOleDb vfpOleDb = new VfpOleDb();
if (vfpOleDb.GetVfpViewData())
{
System.Console.WriteLine("Ok!");
}
else
{
System.Console.WriteLine("Error");
}
}
}
class VfpOleDb
{
static string m_strCntStr = "Provider=VFPOLEDB.1;Data Source=C:\\PROGRAM FILES\\MICROSOFT VISUAL FOXPRO 8\\SAMPLES\\TASTRADE\\DATA\\TASTRADE.DBC;Mode=Share Deny None;Extended Properties=\"\";User ID=\"\";Mask Password=False;Cache Authentication=False;Encrypt Password=False;Collating Sequence=MACHINE;DSN=\"\"";
static string m_strCmdProp = "__spDbGetProp(?,?,?)"; // SP to call VFP-function DBGETPROP()
static string [] m_asPrmName = new string [] {"tcName", "tcType", "tcProperty"}; // args names in __spDbGetProp()
static string [] m_asPrmValues = new string [] {"Employee listing", "view", "SQL"}; // args values to call __spDbGetProp()
static string m_strPrmView = "?cTitle"; // parameter in VFP-view: 'Employee listing'
static string m_strPrmValue = "Sales"; // parameter value
System.Data.OleDb.OleDbConnection m_cnt;
public VfpOleDb(){}
public bool GetVfpViewData()
{
bool bRetVal = false;
if (!CreateConnect())
{
return bRetVal;
}
try
{
/////////////////////////////////////////////////
// (1) Call SP to get SQL-SELECT from VFP-database
using (OleDbCommand cmdProp = m_cnt.CreateCommand())
{
cmdProp.CommandText = m_strCmdProp;
cmdProp.CommandType = CommandType.Text;
for (int nInd = 0; nInd < m_asPrmName.Length; nInd++)
{
cmdProp.Parameters.Add(m_asPrmName[nInd], OleDbType.Char, m_asPrmValues[nInd].Length);
cmdProp.Parameters[m_asPrmName[nInd]].Value = m_asPrmValues[nInd];
}
if (OpenConnect())
{
object objView = cmdProp.ExecuteScalar();
if (objView != null)
{
// Ok! SET ANSI OFF
using (OleDbCommand cmdSet = m_cnt.CreateCommand())
{
cmdSet.CommandText = "SET ANSI OFF";
if(OpenConnect())
{
cmdSet.ExecuteNonQuery();
}
}
/////////////////////////////////////////////////
// (2) Replace parameter '?MyParamName' to '?'
string strSrcSelectView = objView.ToString();
int nPrmPos = strSrcSelectView.IndexOf(m_strPrmView);
if (nPrmPos != (-1))
{
string strCmdSelectView = strSrcSelectView.Substring(0, nPrmPos)
+ "?"
+ strSrcSelectView.Substring(nPrmPos + m_strPrmView.Length);
/////////////////////////////////////////////////
// (3) Execute SQL-SELECT
using (OleDbCommand cmdView = m_cnt.CreateCommand())
{
cmdView.CommandText = strCmdSelectView;
cmdView.CommandType = CommandType.Text;
// Set value to parameter
cmdView.Parameters.Add("0", OleDbType.Char, m_strPrmValue.Length);
cmdView.Parameters["0"].Value = m_strPrmValue;
if (OpenConnect())
{
// Execute SQL-SELECT
OleDbDataReader oRdr = cmdView.ExecuteReader(CommandBehavior.CloseConnection);
if (oRdr != null)
{
/////////////////////////////////////////////////
// (4) Show results
int nRowCount = 0;
int nFld = 0;
string sFldName = null;
while (oRdr.Read())
{
System.Console.Write("\n--- row: {0}", nRowCount.ToString());
for (nFld = 0; nFld < oRdr.FieldCount; nFld++)
{
sFldName = oRdr.GetName(nFld);
System.Console.Write("\n{0}", sFldName);
System.Console.Write(" = {0}",oRdr.GetValue(oRdr.GetOrdinal(sFldName)));
}
nRowCount++;
}
oRdr.Close();
bRetVal = (nRowCount > 0);
if (bRetVal)
{
System.Console.Write("\n===\n");
}
}
}
}
}
}
}
}
}
catch (Exception e)
{
ShowException(e);
}
finally
{
CloseData();
}
return bRetVal;
}
bool CreateConnect()
{
CloseData();
try
{
m_cnt = new OleDbConnection(m_strCntStr);
}
catch (Exception e)
{
ShowException(e);
}
return (m_cnt != null);
}
bool OpenConnect()
{
bool bRetVal = false;
if (m_cnt != null)
{
if (m_cnt.State != System.Data.ConnectionState.Open)
{
m_cnt.Open();
}
bRetVal = (m_cnt.State == System.Data.ConnectionState.Open);
}
return bRetVal;
}
void CloseData()
{
if (m_cnt != null)
{
try
{
if (m_cnt.State != System.Data.ConnectionState.Closed)
{
m_cnt.Close();
}
}
catch (NullReferenceException e)
{
string strSkipMsg = e.Message;
}
finally
{
m_cnt = null;
}
}
}
~VfpOleDb()
{
CloseData();
}
void ShowException(Exception e)
{
System.Console.WriteLine("Message: {0}", e.Message);
System.Console.WriteLine("Source: {0}", e.Source);
}
}
}
Как видите, здесь применено решение типа: "если гора не идёт к Магомету,
то Магомет идёт к годе"... :-)
здесь также коллекция параметров (OleDbCommand.Parameters) использована дважды: первый раз
- для определения значений параметров у хранимой процедуры __spDbGetProp(tcName,
tcType, tcProperty), второй - для
задания значения (m_strPrmValue = "Sales") параметра (m_strPrmView
= "?cTitle") в выражении SQL-SELECT у View 'Employee listing'.
Обратите внимание, что если у вас нестандартная установка примеров,
поставляемых с VFP 8 (и/или не 8), то
перед попыткой выполнения приведённого выше кода, вам необходимо подкорректировать константу m_strCntStr соответствующим образом.
Как пример C#-приложения, использующего данные из VFP-базы данных, можно посмотреть TestUrls-приложение в csurltst.zip (151KB) (MS VS.NET 2003 MS Framework 1.1 SP1), позволяющее выполнять тестирование внешних html-ссылок, там же приведён код ViewResult-приложения для просмотра результатов, получаемых через VFP-SP gettestresult().
Было также установлена проблема Code Page при обращении к хранимым процедурам через MS VFP OLE DB Provider (не из-под VFP-приложений). В том смысле, что на втором и последующих обращениях к хранимой процедуре значение CPCURRENT() перестаёт быть 1251, а изменяется на 1252. Никакие ухищрения с файлом Config.fpw, содержащим строку CODEPAGE=1251 и подложенным во все мыслемые места не помогают. Ситуация может быть исправлена прямым изменением файла vfpoledb.dll (см. в каталоге C:\Program Files\Common Files\System\Ole DB\). Ниже представлена информация где и что следует изменить:
| Файл | Адрес | Что на что менять |
| vfpoledb.dll (8.0.0.3006) | 0004af7e | E4->E3 |
| vfpoledb.dll (8.0.0.3117) | 0004b43c | E4->E3 |
У vfpoledb.dll версий 8.0.0.2521 и 9.0.0.2412 проблемы не обнаружено. См. также адреса для исправлений аналогичной проблемы в vfpXt.dll в статье Как "работает" multi-threaded COM компонента под MS MTS/Component Services?
Было обнаружено, что при попытке использовать VFP 8.0 OLEDB Provider из-под MS IIS 6.0 (OS Windows 2003 Server) получаем: System.Data.OleDb.OleDbException: No error information available: REGDB_E_CLASSNOTREG(0x80040154). Однако, если создать обычное Windows-приложение (например на C# с доступом через System.Data.OleDb), то всё работает без вопросов.
Это означает, что вы пытаетесь использовать версию 8.0.0.3006 и мне известны три варианта преодоления этой проблемы:

![]() |