Code CopyHideScrollFull
namespace FoxPro
{
using System;
using
System.Data;
using
System.Data.OleDb;
using
System.Collections.Generic;
using
System.Data.Common;
/// <summary>
///
Connection to a foxpro database/folder.
///
The FoxPro OleDB provider (VFPOLEDB) must be installed on the executing machine (// oledb driver: http://www.microsoft.com/downloads/details.aspx?FamilyId=E1A87D8F-2D58-491F-A0FA-95A3289C5FD4&displaylang=en)
///
</summary>
public
class FoxProConnection:IDisposable
{
/// <param name="DBFolder">The folder containing the tables, or the dbc file of the foxpro database.
///
This value can be null, but no data can be obtained until the <see cref="DataBaseFolder"/> can be set</param>
///
<seealso cref="DataBaseFolder"/>
public
FoxProConnection(string DBFolder)
{
DataBaseFolder = DBFolder;
GC
.SuppressFinalize(this);
}
private string dbFolder;
///
<summary>
///
The folder or dbc file to connect to.
///
</summary>
public
string DataBaseFolder
{
get { return dbFolder; }
set

{
dbFolder = value;
CloseConnection(true);
conn = null;
}
}
public override string ToString()
{
return "Foxpro Connection '" + dbFolder + "'";
}
// oledb driver: http://www.microsoft.com/downloads/details.aspx?FamilyId=E1A87D8F-2D58-491F-A0FA-95A3289C5FD4&displaylang=en
public
const string Provider = "VFPOLEDB.1";
public
string GetConnectionString()
{
return string.Format(
"Provider={0};Data Source={1};",
Provider, dbFolder);
}
OleDbConnection conn;
///
<summary>
///
The OleDbConnection used for obtaining data.
///
</summary>
public
OleDbConnection Connection
{
get
{
if (conn == null)
{
if (dbFolder == null)
throw new Exception("DataBaseFolder or dbc file not set. Please set the DataBaseFolder first");
conn = new OleDbConnection(GetConnectionString());
}
return
conn;
}
}
#region Open/close
public void CloseConnection()
{
CloseConnection(false);
}
void
CloseConnection(bool Forced)
{
if (
Forced || (--opencount == 0 && ConnectionIsOpen))
{
if(conn!=null)
conn.Close();
opencount = 0;
}
}
int opencount;
public
void OpenConnection()
{
opencount++;
if
(!ConnectionIsOpen)
{
Connection.Open();
OnConnectionOpened();
}
}
/// <summary>
///
This code is executed when the connection is opened
///
</summary>
protected
virtual void OnConnectionOpened()
{
string sql = string.Format("{0}\r\n{1}\r\n{2}\r\n",
GetSetString("EXCLUSIVE",exclusive),
GetSetString("NULL", setnull),
GetSetString("DELETED",true));
ExecuteNonQuery(sql);
}
public bool ConnectionIsOpen
{
get
{
return conn != null && conn.State != ConnectionState.Closed;
}
}
#endregion
#region set statements
void SetParam(string value, bool on,ref bool current)
{
if (on == current) return;
current = on;
if
(ConnectionIsOpen)
ExecuteNonQuery(GetSetString(value, on));
}
string GetSetString(string par, bool on)
{
return "SET " + par + " " + (on ? "ON" : "OFF");
}
private bool setnull=false;
///
<summary>
///
Changes the way alter/create table and inserts handle null values.
///
The default FoxPro value is on, the default value for this connection is off, because
///
that allows null values for columns not included in an insert statement, which is needed
///
for a lot of (automatic) update executions.
///
</summary>
public
bool SetNull
{
get { return setnull; }
set

{
SetParam("NULL", value, ref setnull);
}
}
private bool exclusive;
///
<summary>
///
Specifies whether Visual FoxPro opens table files for exclusive or shared use on a network. The default
///
for FoxPro is On, the default for this connection object is Off.
///
</summary>
public
bool Exclusive
{
get { return exclusive; }
set

{
SetParam("EXCLUSIVE", value, ref exclusive);
}
}

#endregion

/// <summary>
///
Creates an <see cref="OleDbCommand"/> based on the provided sql string
///
</summary>
///
<param name="sql"></param>
///
<returns></returns>
public
OleDbCommand GetCommand(string sql)
{
return new OleDbCommand(sql, Connection);
}
/// <summary>
///
Executes the sql statement
///
</summary>
public
int ExecuteNonQuery(string sql)
{
OpenConnection();
try

{
return GetCommand(sql).ExecuteNonQuery();
}
finally

{
CloseConnection();
}
}
public
object ExecuteScalar(string sql)
{
OpenConnection();
try

{
return GetCommand(sql).ExecuteScalar();
}
finally

{
CloseConnection();
}
}
///
<summary>
///
Returns the collection of tables listed in the directory/dbc file
///
</summary>
///
<returns></returns>
public
string[] GetTables()
{
try
{
OpenConnection();
DataTable
dt = Connection.GetSchema("Tables");
string
[] res = new string[dt.Rows.Count];
int
i = 0;
foreach
(DataRow dr in dt.Rows)
{
res[i++] = dr["Table_name"].ToString();
}
return
res;
}
finally

{
CloseConnection();
}
}
/// <summary>
///
Returns the collection of fieldnames in the specified table
///
</summary>
///
<param name="Table"></param>
///
<returns></returns>
public
string[] GetFields(string Table)
{
try
{
OpenConnection();
DataTable
dt = Connection.GetSchema("Columns", new string[] { null, null, Table });
string
[] res = new string[dt.Rows.Count];
int
i = 0;
foreach
(DataRow dr in dt.Rows)
{
res[i++] = dr["Column_name"].ToString();
}
return
res;
}
finally

{
CloseConnection();
}
}
Filler FillData(string sql,DataTable dt)
{
OpenConnection();
try

{
Filler f = new Filler(sql, this);
f.Fill(dt);
return
f;
}
finally

{
CloseConnection();
}
}
internal class Filler
{
public readonly OleDbDataAdapter da;
FoxProConnection
conn;
string
sql;
public
Filler(string sql,FoxProConnection conn)
{
da = conn.GetDataAdapter(sql);
this
.sql = sql;
this
.conn = conn;
}
public List<Exception> FillErrors;
public void Fill(DataTable dt)
{
da.FillError += new FillErrorEventHandler(da_FillError);
try

{
da.Fill(dt);
}
finally

{
da.FillError -= new FillErrorEventHandler(da_FillError);
}
}
void da_FillError(object sender, FillErrorEventArgs e)
{
if (conn.continueonErr)
{
e.Continue = true;
if
(FillErrors == null)
FillErrors = new List<Exception>();
FillErrors.Add(e.Errors);
}
}
}
private bool continueonErr;
/// <summary>
///
Specifies that all oledb fill errors should be ignored and filling should continue.
///
Errored rows are simply skipped so be careful with use. In general: only use if you
///
know where the fault is and you just want to skip it ;-)
///
</summary>
public
bool ContinueFillOnError
{
get { return continueonErr; }
set
{ continueonErr = value; }
}
public QueryResult GetData(string sql)
{
return GetData(sql, new DataTable());
}
public
QueryResult GetData(string sql,DataTable dt)
{            
Filler f = FillData(sql, dt);
QueryResult
res = new QueryResult(this, dt, f);
OnAfterGetData(res);
return
res;
}
protected
virtual void OnAfterGetData(QueryResult qr)
{
}
public void ReplaceNulls(DataRow dr)
{
DataTable dt = dr.Table;
int
cnt = dt.Columns.Count;
for
(int i = 0; i < cnt; i++)
{
if (dr[i] == DBNull.Value)
{
object o;
if
(dt.Columns[i].DataType==typeof(string))
o = "";
else
o = Activator.CreateInstance(dt.Columns[i].DataType);
dr[i] = o;
}
}
}
public void UpdateData(OleDbDataAdapter da, DataTable dt)
{
FoxProCommandBuilder builder = new FoxProCommandBuilder(da,dt);
try
{
da.Update(dt);
}
catch

{
throw;
}
}

public OleDbDataAdapter GetDataAdapter(string sql)
{
return new OleDbDataAdapter(sql, Connection);
}
//public const int MaxQueryLength = 5000;
#region temp tables
#region
IDisposable Members
public void Dispose()
{
Dispose(false);   
}
void
Dispose(bool fromFinalizer)
{
if (temptables == null) return;
if
(!fromFinalizer)
GC.SuppressFinalize(this);
foreach (TempTable t in temptables)
{
t.Dispose();
}
temptables.Clear();
temptables = null;
}
~FoxProConnection()
{
Dispose(true);
}
List<TempTable> temptables;
public TempTable CreateTempTable(DataTable dt)
{
return CreateTempTable(dt, null);
}
public
TempTable CreateTempTable(DataTable dt,string FileName)
{
if (FileName == null)
FileName = DataBaseFolder + @"\" + FileName;
string Name = dt.TableName;
if
(Name.Length==0)dt.TableName= "tmpTable";
TempTable
t = new TempTable(this,dt);
if
(temptables == null)
{
temptables = new List<TempTable>();
GC
.ReRegisterForFinalize(this);
}
temptables.Add(t);
return
t;
}

#endregion
#endregion
}
/// <summary>
///
The result returned by a <see cref="FoxProConnect.GetData"/> command by a foxpro connection
///
</summary>
public
class QueryResult:System.ComponentModel.IListSource
{
public readonly DataTable Table;
public
readonly OleDbDataAdapter Adapter;
public
readonly FoxProConnection Connection;
///
<summary>
///
In case the <see cref="Connection"/> had the <see cref="FoxProConnection.ContinueFillOnError"/> property
///
set to true, this array contains the errors encountered while filling (if any)
///
</summary>
public
readonly Exception[] FillErrors;
internal
QueryResult(FoxProConnection conn, DataTable dt, FoxProConnection.Filler f)
{
this.Table = dt;
this
.Adapter = f.da;
this
.Connection = conn;
if
(f.FillErrors != null)
FillErrors = f.FillErrors.ToArray();
}
public void Update()
{
Connection.UpdateData(Adapter, Table);
}
public int Count
{
get { return Table.Rows.Count; }
}
public DataRow FirstRow
{
get
{
return Table.Rows[0];
}
}

public
void SetKeyField(string FieldName)
{
DataColumn c  = Table.Columns[FieldName];
//c.AutoIncrement = true;

if
(c == null) return;
if
(c.DataType.IsValueType)
{
c.AutoIncrement = true;
c.AutoIncrementStep = 1;
c.AutoIncrementSeed = 1;
}
Table.PrimaryKey = new DataColumn[] { c };
}
public void SetKeyField(params string[] FieldNames)
{
if (FieldNames.Length == 0)
return;
if (FieldNames.Length == 1)
SetKeyField(FieldNames[0]);
else
{
DataColumn[] cols = new DataColumn[FieldNames.Length];
for
(int i = 0; i < cols.Length; i++)
{
cols[i] = Table.Columns[FieldNames[i]];
}
Table.PrimaryKey = cols;                
}
}
#region IListSource Members
bool System.ComponentModel.IListSource.ContainsListCollection
{
get { return true; }
}
System.Collections.IList System.ComponentModel.IListSource.GetList()
{
return ((System.ComponentModel.IListSource)Table).GetList();
}
#endregion
}

public class FoxProCommandBuilder : DbCommandBuilder
{
DataTable dt;
public
FoxProCommandBuilder(OleDbDataAdapter da, DataTable dt)
{
if(da!=null)
this.DataAdapter = da;
this.dt = dt;
}
DataTable dtSchema;
string
[] setkeyfields = { "iskey", "isautoincrement", "isunique" };
protected
override DataTable GetSchemaTable(System.Data.Common.DbCommand sourceCommand)
{
if (dtSchema == null)
{
dtSchema = base.GetSchemaTable(sourceCommand);
DataRow
[] rows = dtSchema.Select("iskey=true");
if
(rows.Length == 0 && this.dt.PrimaryKey.Length > 0)
{
SetReadonly(false);
foreach
(DataColumn dc in this.dt.PrimaryKey)
{
foreach (DataRow dr in dtSchema.Rows)
{
if (dr["columnname"].ToString() == dc.ColumnName)
{
foreach (string fld in setkeyfields)
{
dr[fld] = true;
}
break
;
}
}
}
SetReadonly(true);
dtSchema.AcceptChanges();
}
}
return
dtSchema;
}
void SetReadonly(bool value)
{
foreach (string fld in setkeyfields)
{
dtSchema.Columns[fld].ReadOnly = value;
}
}
protected
override void ApplyParameterInfo(
DbParameter commonparameter,
DataRow
row,
StatementType
statementType,
bool
whereClause)
{
OleDbParameter parameter = (OleDbParameter)commonparameter;
object
obj2 = row[SchemaTableColumn.ProviderType];
parameter.OleDbType = (OleDbType)obj2;
object
obj1 = row[SchemaTableColumn.NumericPrecision];
byte
nul = 0;
if
(DBNull.Value != obj1)
{
byte num2 = (byte)((short)obj1);
parameter.Precision = (0xff != num2) ? num2 : nul;
}
obj1 = row[SchemaTableColumn.NumericScale];
if
(DBNull.Value != obj1)
{
byte num1 = (byte)((short)obj1);
parameter.Scale = (0xff != num1) ? num1 : nul;
}
}
protected override string GetParameterName(string parameterName)
{
return parameterName;
}
protected override string GetParameterName(int parameterOrdinal)
{
return "p" + parameterOrdinal;
}
protected override string GetParameterPlaceholder(int parameterOrdinal)
{
return "?";
}

protected override void SetRowUpdatingHandler(System.Data.Common.DbDataAdapter adapter)
{
if (adapter == base.DataAdapter)
{
((OleDbDataAdapter)adapter).RowUpdating -= new OleDbRowUpdatingEventHandler(this.OleDbRowUpdatingHandler);
}
else

{
((OleDbDataAdapter)adapter).RowUpdating += new OleDbRowUpdatingEventHandler(this.OleDbRowUpdatingHandler);
}
}
private void OleDbRowUpdatingHandler(object sender, OleDbRowUpdatingEventArgs ruevent)
{
base.RowUpdatingHandler(ruevent);
if
(ruevent.StatementType == StatementType.Insert)
{
OleDbCommand cmd = (OleDbCommand)ruevent.Command.Connection.CreateCommand();
cmd.CommandText = "SET NULL OFF";
cmd.ExecuteNonQuery();
}
//ruevent.Command.CommandText  = "SET NULL OFF\r\n " + ruevent.Command.CommandText;
}


}
public abstract class TableCreator
{
public readonly IDbConnection Connection;
public
readonly string TableName;
readonly
DataTable dt;
public
bool StructureOnly;
public
TableCreator(IDbConnection conn, DataTable dt, bool StructureOnly)
{
this.Connection = conn;
this
.TableName = dt.TableName;
this
.dt = dt;
this
.StructureOnly = StructureOnly;
}
public void Create()
{
string sql = GetCreateSQL();
bool
manualopen = Connection.State != ConnectionState.Open;
try

{
if (manualopen) Connection.Open();
IDbCommand cmd = Connection.CreateCommand();
cmd.CommandText = sql;
cmd.ExecuteNonQuery();
if (!StructureOnly)
{
foreach (DataRow dr in dt.Rows)
{
dr.SetAdded();
}
int
res = Fill(dt, Connection);
}
}
finally

{
try
{
if (manualopen) Connection.Close();
}
catch
{ }
}
}
public static int Fill(DataTable dt, IDbConnection conn)
{

if
(conn is OleDbConnection)
return Fill(dt, (OleDbConnection)conn);
throw new Exception("not supported yet :(");
}
public static int Fill(DataTable dt, OleDbConnection conn)
{
string sql = "select * from [" + dt.TableName + "]";
OleDbDataAdapter
da = new OleDbDataAdapter(sql, conn);
OleDbCommandBuilder
builder = new OleDbCommandBuilder(da);
return
da.Update(dt);
}
public string GetCreateSQL()
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
sb.Append("create table [").Append(TableName).Append("] (");
int
i = 0;
string[] names = new string[dt.Columns.Count];
foreach
(DataColumn dc in dt.Columns)
{
string name = dc.ColumnName;
FormatFieldName(ref name);
if
(name != dc.ColumnName)
{
if (dc.Caption == null || dc.Caption.Length == 0
|| dc.Caption == dc.ColumnName)
dc.Caption = dc.ColumnName;
dc.ColumnName = name;
}
names[i++] = name;
}
i = 0;
foreach
(DataColumn dc in dt.Columns)
{
if(i>0)sb.Append(" , ");                

sb.Append("[").Append(names[i])
.Append("] ")
.Append(GetTypeIndicator(dc.DataType,DetermineLength(dc)));
i++;
}
return
sb.Append(")").ToString();
}
private int DetermineLength(DataColumn dc)
{
if (dc.MaxLength > 0 || dc.DataType != typeof(string)) return dc.MaxLength;
int
c = dt.Columns.IndexOf(dc);
int
max = 20;
for
(int i = 0; i < dt.Rows.Count; i++)
{
if (!dt.Rows[i].IsNull(c))
{
int len = (dt.Rows[i][c] as string).Length;
if
(len > max)
max = len;
}
}
return
max;
}
public abstract string GetTypeIndicator(Type DataType, int Length);
public
int MaximumFieldNameLength = 255;
public
string NotAllowed = @" ()[]<>-\/?;";
public
char ReplacementChar = '_';
public
virtual void FormatFieldName(ref string Name)
{
if (MaximumFieldNameLength > 0 && Name.Length > MaximumFieldNameLength)
Name = Name.Remove(MaximumFieldNameLength);
foreach (char c in NotAllowed)
{
Name = Name.Replace(c, ReplacementChar);
}
}
}
public class FoxProTableCreator:TableCreator
{
public FoxProTableCreator(IDbConnection conn, DataTable dt, bool StructureOnly)
:base(conn,dt,StructureOnly)
{
MaximumFieldNameLength = 10;
}
/*
* FieldType nFieldWidth  nPrecision Description
C n Character field of width n
D Date
T DateTime
N n d Numeric field of width n with d decimal places
F n d Floating numeric field of width n with d decimal places
I Integer
B d Double  
Y Currency
L Logical
M Memo
G General
*/

public
override string GetTypeIndicator(Type dt,int Length)
{
if (dt == typeof(string))
{
if (Length <= 0) Length = 254;
return
"C(" + Length + ")";
//return "M"
}
if
(dt == typeof(DateTime))
return "T";
if (dt == typeof(int) || dt == typeof(long) || dt==typeof(byte))
return "I";
if (dt == typeof(double) || dt == typeof(float))
return "B";
if (dt == typeof(decimal))
return "Y";
throw new Exception("DataType not supported: " + dt.ToString());
}
}
/// <summary>
///
A temporary table. Do not create directly, but create with the
///
CreateTempTable function
///
</summary>
public
class TempTable:FoxProTableCreator, IDisposable
{
public readonly FoxProConnection conn;
internal
TempTable(FoxProConnection conn, DataTable dt)
:base(conn.Connection,dt,false)
{
this.conn = conn;
Create();
}
#region IDisposable Members
public void Dispose()
{
conn.OpenConnection();
try

{
OleDbCommand cmd = new OleDbCommand("Drop Table [" + TableName + "]", conn.Connection);
cmd.ExecuteNonQuery();
}
finally

{
conn.CloseConnection();
}
}
#endregion
}
}
. . .