Tripous - 数据访问






4.75/5 (9投票s)
关于使用Tripous数据访问类的教程
引言
Tripous是一个开源应用程序框架,用C#编写,用于快速实现WinForms数据录入应用程序。
官方网站位于 http://tripous-net.com。
SourceForge上的项目页面位于 http://sourceforge.net/projects/tripous。
Tripous数据访问层提供统一的数据访问,即与数据库服务器无关的SQL语句,以及最重要的,与服务器无关的SQL参数。
人们可能会期望像.NET这样的系统在访问不同数据库数据源时提供一定程度的统一性。不幸的是,事实并非如此。.NET在这方面唯一提供的是“数据提供程序”的概念。最初,数据提供程序只是一组接口,如IDbConnection
、IDbCommand
等。为了“实现”这样的数据提供程序,您必须从头开始编写自己的类来实现所有这些接口。没有提供通用基类。这不是一项容易的任务。那是一个英雄的时代。
正如您可能知道的,执行SQL语句的最佳方法之一是使用参数化SQL语句。Tripous广泛使用参数化SQL语句。好吧,ADO.NET尽最大努力使编写服务器无关的参数化语句变得非常困难。在IDbDataParameter
接口和(.NET 2)DbParameter
类中有一个DbType
属性,类型为System.Data.DbType
,但是任何数据提供程序都有自己的一组数据类型。在很多情况下,很难说哪个对应哪个。更糟的是,任何数据提供程序都在语句文本中使用自己的符号作为参数的前缀。一个数据提供程序使用@
后跟参数名,而另一个则使用:
后跟参数名。然后还有另一个数据提供程序根本不使用命名参数。它只需要在参数位置使用?
。看看吧。
select * from Customer where Id = @Id
select * from Customer where Id = :Id
select * from Customer where Id = ?
那么,如何在该系统中编写服务器无关的参数化语句?
.NET 2.0之后,数据提供程序类基于通用基类DbConnection
、DbCommand
等。迟早会实现的。此外,.NET 2.0还为我们带来了DbProviderFactories
和DbProviderFactory
类。DbProviderFactories
只是一个工厂,用于基于所谓的不变名称创建DbProviderFactory
实例。一个string
,在大多数情况下,它是特定DbProviderFactory
类的完整名称,包括命名空间。
现在,“公开DbProviderFactory
的数据提供程序在machine.config文件中注册配置信息和提供程序字符串”。这并非我所设想的。它听起来太“static
”了。当然,您无法即时注册自己的DbProviderFactory
。
无论如何,在获取DbProviderFactory
实例后,您将它用于创建DbConnection
、DbCom
实例等。因此,您可以编写服务器无关的SQL,前提是您不使用参数。mand
以上是Tripous必须处理的第一组问题。
Tripous数据提供程序
Tripous.Data
命名空间包含所有与数据访问层相关的类。
Tripous.Data.DataProviders
类实际上是Tripous.Data.DataProvider
实例的注册表。
public static class DataProviders
{
public static int Count { get; }
public static char GlobalPrefix { get; set; }
public static void Add(DataProvider Provider);
public static DataProvider ByIndex(int Index);
public static DataProvider ByName(string Alias);
public static bool CanCreateDatabases(string Alias);
public static bool Contains(string Alias);
public static void Error(string Text);
public static void Error(string Text, params object[] Args);
public static DataProvider Find(string Alias);
public static DataProvider[] GetCreatableProviders();
}
GlobalPrefix
属性是您设置在编写参数化SQL语句时用作参数前缀的字符的地方。这是Tripous.Data.DataProvider
类。
public abstract class DataProvider
{
public DataProvider();
public string Alias { get; }
public virtual bool CanCreateDatabases { get; }
public string Description { get; }
public MidwareType MidwareType { get; }
public char NativePrefix { get; }
public OidMode OidMode { get; }
public PrefixMode PrefixMode { get; }
public bool RequiresNativeParamNames { get; }
public ServerType ServerType { get; }
public bool SupportsGenerators { get; }
public bool SupportsTransactions { get; }
public virtual void AssignParameter(IDataParameter Parameter, object Value);
public virtual void AssignParameter
(DataColumn Column, IDataParameter Parameter, object Value);
public virtual void AssignParameters(DbCommand Command, params object[] Params);
public virtual void AssignParameters
(IDataParameterCollection Parameters, params object[] Params);
public abstract DbDataAdapter CreateAdapter();
public DbCommand CreateCommand
(DbConnection Connection, string SqlText, params object[] Params);
public virtual DbCommandBuilder CreateCommandBuilder();
public abstract DbConnection CreateConnection(string ConnectionString);
public virtual DbParameter CreateParameter();
public virtual DbConnectionStringBuilder CreateConnectionStringBuilder();
public virtual ConnectionStringBuilder CreateConnectionStringBuilder
(string ConnectionString);
public virtual bool CreateDatabase(string ConnectionString);
public virtual bool CreateDatabase
(string ServerName, string DatabaseName, string UserName, string Password);
public DataTable Select(DbCommand Command);
public DataTable Select(string ConnectionString, string SqlText);
public DataTable Select
(string ConnectionString, string SqlText, params object[] Params);
public void Exec(string ConnectionString, string SqlText);
public void Exec(string ConnectionString, string SqlText, params object[] Params);
public virtual string FormatConnectionString(string FileOrDatabaseName);
public virtual string FormatConnectionString
(string HostComputer, string FileOrDatabaseName);
public virtual string NormalizeAlterTableColumnSql(string SqlText);
public void PrefixRemove(DbCommand Command);
public virtual string PrefixRemove(string ParameterName);
public void PrefixToGlobal(DbCommand Command);
public virtual string PrefixToGlobal(string ParameterName);
public void PrefixToNative(DbCommand Command);
public virtual string PrefixToNative(string ParameterName);
public virtual string ReplaceDataTypePlaceholders(string SqlText);
public void SetupCommand(DbCommand Command, string SqlText, params object[] Params);
public virtual DbType TypeToDbType(Type Source);
}
Tripous中已经有许多DataProvider
的派生类(MsSqlProvider
、FirebirdProvider
等),并且DataProviders
类的static
构造函数会注册所有这些类。您可以创建自己的DataProvider
类并以相同的方式注册它们。请将您的DataProvider
类发送给我,我将将其添加到Tripous官方源代码中,以便其他人可以受益于您的工作。
DataProvider
类主要用于创建DbConnection
、DbCommand
实例等。
在Tripous中,DataProvider
类由其Alias
(一个string
)唯一标识。Tripous.Data.ConnectionStringBuilder
辅助类为所有提供程序别名提供了public
常量。一些DataProviders
知道如何创建一个新数据库。所有DataProviders
都知道如何“格式化”连接字符串。
Tripous连接字符串与ADO.NET连接字符串相同,再加上一个Alias
条目。这是MsSql
的Tripous连接字符串。
Alias=MsSql; Data Source=localhost; Integrated Security=SSPI; Initial Catalog=DevApp
ConnectionStringBuilder
为最常见的情况提供了预定义的连接字符串格式的public
常量。这是MsSql
的连接字符串。
Alias=MsSql; Data Source={0}; Integrated Security=SSPI; Initial Catalog={1};
可以使用DataProvider
或ConnectionStringBuilder
类的FormatConnectionString()
方法轻松创建这些Tripous连接字符串。
DataProvider Provider = DataProviders.Find(Alias);
if (Provider != null)
{
string ConnectionString = Provider.FormatConnectionString
(HostComputer, FileOrDatabaseName);
MessageBox.Show(ConnectionString);
}
如果DataProvider
支持该操作,则可以使用DataProvider.CreateDatabase()
创建新数据库。在这种情况下,CanCreateDatabases
返回true
。
DataProvider Provider = DataProviders.Find(Alias);
if (Provider != null)
{
Provider.CreateDatabase(ServerName, DatabaseName, UserName, Password);
MessageBox.Show("DONE");
}
TripousDataProvider
类有许多有用的只读属性和一些辅助方法。
警告:如果您要使用Firebird,则必须先安装它,然后将FirebirdSql.Data.FirebirdClient.dll从Tripous的ThirdParty文件夹复制到您的bin文件夹。
创建数据库模式
在Tripous的说法中,datastore
一词表示一组信息,该信息在唯一名称下描述数据库连接和属性。还有一个Datastore
类以及一个static Datastores
类,该类是所有已注册Datastore
项的集合。
Tripous提供了一套类,用于简化数据库模式的创建和更新。SchemaDatastores
、SchemaDatastore
和SchemaTable
是最重要的。
SchemaDatastore
实例代表特定数据存储的特定版本模式。您使用数据存储名称(例如“MAIN
”)和版本号。SchemaDatastores
是SchemaDatastore
项的集合,并提供了一个Execute()
方法来将这些更改应用于一个或多个数据库。
对于在SchemaDatastores
中以特定名称注册的每个SchemaDatastore
项,必须有一个在Datastores
下以相同名称注册的Datastore
。否则,SchemaDatastores.Execute()
将失败。
成功调用SchemaDatastores.Execute()
会创建已注册的模式以及一个“ini
”表,即SYS_INI
,该表类似于Microsoft Windows的*.ini文件。Datastore
类提供了一个类型为DbIni
的Ini
属性。
该DbIni
类提供了一些方便的方法,例如以下方法
public class DbIni
{
...
public void WriteString(string Entry, string Value);
public void WriteInteger(string Entry, int Value);
public void WriteFloat(string Entry, double Value);
public void WriteDateTime(string Entry, DateTime Value);
public void WriteBool(string Entry, bool Value);
public string ReadString(string Entry, string Default);
public int ReadInteger(string Entry, int Default);
public double ReadFloat(string Entry, double Default);
public DateTime ReadDateTime(string Entry, DateTime Default);
public bool ReadBool(string Entry, bool Default);
...
...
}
在SchemaDatastores.Execute()
执行后,该ini
表中会有一个关于刚刚创建的模式及其版本的条目。
让我们回到数据库创建。
为了在数据库中创建数据库表,您首先创建一个SchemaDatastores
实例。然后,您在特定名称和版本下Add()
一个SchemaDatastore
。除了Add()
之外,SchemaDatastores
还提供了ForceFind()
方法,该方法返回具有指定Name
且版本等于指定Version
的第一个SchemaDatastore
(如果存在)。如果不存在这样的SchemaDatastore
,则会创建一个新的并返回。
这是一个创建新的空Sqlite数据库、注册Datastore
、创建SchemaDatastores
和SchemaDatastore
、添加SchemaTable
并在数据库中创建模式的方法。
void CreateDatabase()
{
string DatabaseName = Path.GetFullPath(@"..\..\bin\Debug\Test.db3");
if (!File.Exists(DatabaseName))
{
string Alias = ConnectionStringBuilder.Sqlite;
DataProvider Provider = DataProviders.Find(Alias);
/* create the database */
Provider.CreateDatabase(string.Empty, DatabaseName,
string.Empty, string.Empty);
/* add a Datastore */
string ConnectionString = Provider.FormatConnectionString(DatabaseName);
Datastores.Add(Sys.MAIN, Alias, ConnectionString);
/* schema */
int Version = 1;
SchemaDatastores SchemaDatastores = new SchemaDatastores();
SchemaDatastore Schema = SchemaDatastores.Add(Sys.MAIN, Version);
string TableName = "Person";
string SqlText = @"
create table Person (
Id @PRIMARY_KEY
,Name varchar(32) @NOT_NULL
,Birthday @DATE @NULL
,Salary double @NULL
,Rank integer @NULL
,Notes @BLOB_TEXT @NULL
);
";
Schema.AddTable(TableName, SqlText);
/* create the table */
SchemaDatastores.Execute();
}
}
正如您所见,创建表的脚本包含一些占位符。这些词以at(@
)字符开头。Tripous致力于实现服务器无关性。这些占位符就是为此目的而存在的。DataProvider.ReplaceDataTypePlaceholders()
方法根据实际使用的服务器替换这些占位符。
请随意深入研究这些类,因为还有很多东西需要发现。希望您能找到任何错误并发送一些代码给我纠正。
数据存储、数据存储和执行程序
Datastores
类是一个static
类,它充当Datastore
实例的注册表。
public static class Datastores
{
public static int Count { get; }
public static Datastore Main { get; }
public static void Add(Datastore Datastore);
public static Datastore Add
(string Name, string ProviderAlias, string ConnectionString);
public static Datastore ByIndex(int Index);
public static Datastore ByName(string Name);
public static bool CanCreateDatabase(Datastore Datastore);
public static bool CanCreateDatabase(string ConnectionString);
public static void Clear();
public static bool Contains(string Name);
public static bool CreateDatabase(Datastore Datastore);
public static bool DatabaseExists(Datastore Datastore);
public static Datastore Find(string Name);
public static void LoadFrom(DatastoreDescriptors Descriptors);
public static string NextDatastoreName(string Prefix);
public static void Remove(Datastore Datastore);
public static void Remove(string Name);
public static Datastore[] ToArray();
}
最重要的red-only方法是Add()
方法。
Datastore
类是一个abstract
类。有两个派生类:AdoNetDatastore
和ClientDatastore
类。
现在先忽略ClientDatastore
类。只需说它使用XML TCP命令通过套接字连接将请求发送到AdoNetDatastore
。简而言之,它充当服务器端AdoNetDatastore
的客户端代理。您可以从Pda(Compact Framework)应用程序使用ClientDatastore
来访问远程数据库,就像访问桌面系统中的数据库一样。
警告:Datastores.Add(string Name, string ProviderAlias, string ConnectionString)
仅创建AdoNetDatastore
实例。
这是Datastore
类
public abstract class Datastore
{
public abstract bool ConnectionChecked { get; }
public virtual DbIni Ini { get; }
public abstract MidwareType MidwareType { get; }
public abstract string Name { get; }
public abstract OidMode OidMode { get; }
public Dictionary<string, DataTable> SchemaTables { get; }
public abstract ServerType ServerType { get; }
public abstract bool SupportsGenerators { get; }
public abstract bool SupportsTransactions { get; }
public object SyncRoot { get; }
public virtual int Version { get; }
public abstract Executor CreateExecutor();
public abstract void EnsureConnection();
public void GeneratorAdd(string GenaratorName);
public virtual bool GeneratorExists(string GenaratorName);
public abstract DataTable GetSchema();
public abstract DataTable GetSchema(string collectionName);
public abstract DataTable GetSchema
(string collectionName, string[] restrictionValues);
public virtual string QSDate(DateTime Value);
public override string ToString();
}
如您所见,它是一个简陋的类。只是一组只读信息属性和几个方法。如前所述,Datastore
类代表一个在唯一名称下的数据库连接,该名称在其他已注册的Datastore
项中是唯一的,以及一些特征。最重要的方法是CreateExecutor()
,它创建并返回一个Executor
实例。Executor
类是这个级别上最重要的类。
public abstract class Executor
{
public Executor(Datastore Datastore);
public Datastore Datastore { get; }
public virtual MidwareType MidwareType { get; }
public virtual OidMode OidMode { get; }
public virtual ServerType ServerType { get; }
public virtual bool SupportsGenerators { get; }
public virtual bool SupportsTransactions { get; }
public abstract DataTransaction BeginTransaction();
public abstract Executor CreateExecutor();
public void CreateGenerator(string GeneratorName);
public abstract bool CreateTable(string SqlText);
public DataTable Select(string SqlText);
public virtual DataTable Select(string SqlText, params object[] Params);
public DataTable Select(DataTransaction Transaction, string SqlText);
public abstract DataTable Select
(DataTransaction Transaction, string SqlText, params object[] Params);
public int SelectTo(DataTable Table, string SqlText);
public int SelectTo(DataTable Table, string SqlText, params object[] Params);
public int SelectTo(DataTransaction Transaction, DataTable Table, string SqlText);
public int SelectTo(DataTransaction Transaction,
DataTable Table, string SqlText, params object[] Params);
public void Exec(string SqlText);
public virtual void Exec(string SqlText, params object[] Params);
public void Execute(DataTransaction Transaction, string SqlText);
public abstract void Execute(DataTransaction Transaction,
string SqlText, params object[] Params);
public void ExecuteNested(DataTransaction ParentTransaction, string SqlText);
public void ExecuteNested(DataTransaction ParentTransaction,
string SqlText, params object[] Params);
public DataRow SelectResults(string SqlText);
public DataRow SelectResults(DataTransaction Transaction, string SqlText);
public DataRow SelectResults(string SqlText, params object[] Params);
public DataRow SelectResults(DataTransaction Transaction,
string SqlText, params object[] Params);
public object SelectResult(string SqlText, object Default);
public object SelectResult(DataTransaction Transaction,
string SqlText, object Default);
public int IntegerResult(string SqlText, int Default);
public int IntegerResult(DataTransaction Transaction, string SqlText, int Default);
public bool TableExists(string TableName);
public bool TableIsEmpty(string TableName);
public bool FieldExists(string TableName, string FieldName);
public bool IndexExists(string IndexName);
public bool GeneratorExists(string GeneratorName);
public abstract IList<string> GetFieldNames(string TableName);
public abstract IList<string> GetIndexNames();
public abstract IList<string> GetTableNames();
public void GetNativeSchema(string SqlText, string TableName,
string SchemaName, DataTable Table);
public int LastId(string TableName);
public int LastId(DataTransaction Transaction, string TableName);
public int NextId(string TableName);
public int NextId(DataTransaction Transaction, string TableName);
public int NextIdByGenerator(string GeneratorName);
public int NextIdByGenerator(DataTransaction Transaction, string GeneratorName);
public void ResetTable(string TableName);
public void ResolveSql(ref string SqlText);
public void SetGeneratorTo(string GeneratorName, int Value);
public void SetTableGeneratorTo(string TableName, int Value);
}
在Tripous中,应用程序程序员从不直接使用DbConnection
、DbCommand
等类。它使用Datastore
和Executor
。
与Datastore
类一样,有一个AdoNetExecutor
和一个ClientExecutor
类。
您从不直接创建Executor
。您始终使用Datastore.CreateExecutor()
调用。
Executor
类可以使用各种SelectXXX()
方法执行SELECT
语句,以及使用Exec()
和Execute()
方法执行INSERT
、UPDATE
、DELETE
、CREATE TABLE
等语句。其中一些重载接受一个用params
关键字标记的最后一个参数。Executor对处理参数一无所知。相反,它将该信息传递给它的DataProvider SetupCommand()
和AssignParameters()
方法。
正如AssignParameters()
方法的文档所述,参数可以是
- 逗号分隔的参数列表
- 或者
Params[0]
元素,即Params
中的第一个元素,可能是一个DataRow
、通用IDictionary
、IList
或Array
,在这种情况下,将不使用其他Params
元素。
如果您传递一个DataRow
,那么Tripous使用Column
名称来匹配SQL参数名称。如果您传递一个IDictionary
,那么Tripous期望它是一个通用的,其中第一个类型参数是string
(SQL参数名称),第二个是object
(值)。在所有其他情况下,都会使用位置逻辑。
这是一个演示窗体中的private
方法,它使用Executor
将一行插入数据库表中。
void InsertRow()
{
EnsureDatastore();
if (tblParams == null)
{
string TableName = "Person";
string SchemaName = string.Empty;
string SqlText = string.Empty;
tblParams = new DataTable(TableName);
executor.GetNativeSchema(SqlText, TableName, SchemaName, tblParams);
}
tblParams.Rows.Clear();
DataRow Row = tblParams.NewRow();
tblParams.Rows.Add(Row);
Row["Name"] = edtName.Text;
Row["Birthday"] = edtBirthday.Value;
Row["Salary"] = edtSalary.Text;
Row["Rank"] = edtRank.Text;
Row["Notes"] = edtNotes.Text;
string SqlInsert = @"
insert into Person
(
Name
,Birthday
,Salary
,Rank
,Notes
)
values
(
:Name
,:Birthday
,:Salary
,:Rank
,:Notes
)
";
executor.Exec(SqlInsert, Row);
RefreshData();
}
上述方法准备一个包含单行的DataTable
。该DataRow
用作执行参数化SQL INSERT
语句的Executor.Exec()
调用的第二个参数。

Executor.GetNativeSchema()
用于从数据库获取有关表或SELECT
语句的模式信息。
为数据库表构建SQL语句
Tripous.Data.Db
类是一个static
辅助类,包含一些方便的方法。BuildSql()
可以构建SQL语句(SELECT
、INSERT
、UPDATE
、DELETE
)。使用方法如下。
void BuildSql()
{
SqlStatements Statements = new SqlStatements();
Db.BuildSql("Person", "Id", false, executor, Statements, true);
StringList List = new StringList();
List.Add("================= Insert ================= ");
List.Add(Statements.Insert);
List.Add("");
List.Add("================= Update ================= ");
List.Add(Statements.Update);
Sys.CourierBox(List.Text);
}
BuildSql()
需要一个SqlStatements
实例,以及其他参数。调用后,该SqlStatements
实例将被填充语句。
StringList
只是一个方便的string
列表类,Tripous在许多地方使用它。
Sys static
类位于Tripous系统之上,它只是一个包含大量有用实用方法和属性的集合。
历史
- 2010年6月6日:初次发布