5656
Как в 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

Как использовать параметры в VFP 8.0 и выше
В начало

Начиная с версии 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
В начало

Было также установлена проблема 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?

Проблема доступа к OLEDB VFP 8.0 для IIS 6.0
В начало

Было обнаружено, что при попытке использовать 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 и мне известны три варианта преодоления этой проблемы:

  1. Вместо VFP 8.0 OLEDB Provider использовать VFP OLEDB Provider для версии 7.0
  2. В файле machine.config в секции processmodel подменить UserName с MACHINE на SYSTEM
  3. Удалите VFP 8.0 OLEDB Provider версии 8.0.0.3006, и установите именно версию 8.0.0.3117 (SP1VFP8) или выше, взяв её с http://msdn.microsoft.com/vfoxpro/downloads/updates/default.aspx Обратите внимание, что для корректной регистрации OLE-компоненты вам нужны права именно локального администратора сервера (НЕ сетевого или контроллера домена администратора, а именно локального админа).
 
 
Hosted by uCoz