以对象形式从数据库获取数据
现代的很多编程技术都集中在“数据就是对象”这个概念。如果你在应用层之间使用Data Transfer Objects (DTOs)传输数据的话,这个方法就很有用,使用ORM实现一个数据访问层,或者是客户端查询技术,例如LINQ。
数据库访问模块实现了这个功能,允许你执行SQL或者是存储过程,可以返回一个对象序列,但是要求序列实现IEnumerable接口。
关于Accessors
模块提供了两个方法来实现这种查询要求,SprocAccessor和SqlStringAccessor。你可以使用Database类的ExecuteSprocAccessor和ExecuteSqlStringAccessor,或者是创建一个Accessor,然后调用它的Execute方法。
下面是一张查询示意图
Accessor使用另外两个对象,一个用来管理传入accessor的参数,一个用来将数据库返回的数据行映射成客户端需要的对象。
如果你没有指定一个参数映射,accessor会使用默认的参数映射。默认情况下这个功能只能用于执行SQL Server和Oracle的存储过程。不能执行SQL 语句,或者是其他的数据库和provider,如果需要的话,可以自定义参数映射器。
如果你没有指定一个对象映射,模块使用默认的对象映射,根据列的名称映射到对象的属性。你也可以自定义对象映射,将列映射到指定的对象属性上去。
创建和执行Accessor
//
Create an object array and populate it with the required parameter values
object
[]
params
=
new
object
[] {
"
%bike%
"
};
//
Create and execute a sproc accessor that uses the default
//
parameter and output mappings.
var productData
=
defaultDB.ExecuteSprocAccessor
<
Product
>
(
"
GetProductList
"
,
params
);
//
Perform a client‐side query on the returned data. Be aware that
//
the orderby and filtering is happening on the client, not in the database.
var results
=
from productItem
in
productData
where
productItem.Description
!=
null
orderby productItem.Name
select
new
{ productItem.Name, productItem.Description };
//
Display the results
foreach
(var item
in
results)
{
A Guide to Developing with Enterprise Library
5.0
41
Console.WriteLine(
"
Product Name: {0}
"
, item.Name);
Console.WriteLine(
"
Description: {0}
"
, item.Description);
Console.WriteLine();
}
上面假设由一个Product类,有ID,Name,Description三个属性。
创建和使用映射Mappers
在某些情况下,你需要自定义参数映射,将你的参数传递给要执行查询的accessor。这种典型的情况发生在,你需要执行一个SQL语句,和一个不支持参数的数据库进行交互,或者是默认的参数映射的参数个数或者是类型不匹配。参数映射类需要实现IParameterMapper接口,有一个AssignParameters方法引用了Command对象作为参数,你需要做的就是将需要的参数加入Command对象的Parameters集合。
在更多的情况你需要一个自定义对象映射。为了帮助你完成这个需求,模块提供了MapBuilder类,用它可以创建列和对象属性的映射关系。
默认情况,accessor可以返回一个简单的对象序列。但是,有时候需要返回一个复杂的对象序列,例如,返回Orders的同时返回相关的OrderLines。简单的映射不能满足这样的需求,MapBuilder类也不能满足了。你需要实现IResultSetMapper接口,自定义对象映射关系。
获取XML数据
企业库提供了ExecuteXmlReader方法返回一个XmlReader对象。目前为止,还只能支持SQL Server数据库,意味着你只能用SqlDatabase类。
你需要把Database转换成SqlDatabse,或者是用构造函数直接创建一个SqlDatabse。
static
SqlDatabase sqlServerDB
=
null
;
//
Resolve a SqlDatabase object from the container using the default database.
sqlServerDB
=
EnterpriseLibraryContainer.Current.GetInstance
<
Database
>
()
as
SqlDatabase;
//
Specify a SQL query that returns XML data
string
xmlQuery
=
"
SELECT * FROM OrderList WHERE State = @state FOR XML AUTO
"
;
//
Create a suitable command type and add the required parameter
//
NB: ExecuteXmlReader is only available for SQL Server databases
using
(DbCommand xmlCmd
=
sqlServerDB.GetSqlStringCommand(xmlQuery))
{
xmlCmd.Parameters.Add(
new
SqlParameter(
"
state
"
,
"
Colorado
"
));
using
(XmlReader reader
=
sqlServerDB.ExecuteXmlReader(xmlCmd))
{
while
(
!
reader.EOF)
//
Iterate through the elements in the XmlReader
{
if
(reader.IsStartElement())
{
Console.WriteLine(reader.ReadOuterXml());
}
}
}
}
获取单个值
//
Create a suitable command type for a SQL statement
//
NB: For efficiency, aim to return only a single value or a single row.
using
(DbCommand sqlCmd
=
defaultDB.GetSqlStringCommand(
"
SELECT [Name] FROM States
"
))
{
//
Call the ExecuteScalar method of the command
Console.WriteLine(
"
Result using a SQL statement: {0}
"
,
defaultDB.ExecuteScalar(sqlCmd).ToString());
}
//
Create a suitable command type for a stored procedure
//
NB: For efficiency, aim to return only a single value or a single row.
using
(DbCommand sprocCmd
=
defaultDB.GetStoredProcCommand(
"
GetStatesList
"
))
{
//
Call the ExecuteScalar method of the command
Console.WriteLine(
"
Result using a stored procedure: {0}
"
,
defaultDB.ExecuteScalar(sprocCmd).ToString());
}
异步获取数据
1)准备配置文件
需要在连接字符串中添加Asynchronous Processing=true或者是async=true。
<
connectionStrings
>
<
add name
=
"
AsyncExampleDatabase
"
connectionString
=
"
Asynchronous Processing=true; Data Source=.\SQLEXPRESS;
Initial Catalog
=
"
MyDatabase
"
; Integrated Security
=
True;
"
providerName
=
"
System.Data.SqlClient
"
/>
...
</
connectionStrings
>
另外,企业库中的异步访问数据库只支持SQL Server。在Database类由一个属性SupportsAsync,可以用来查询当前数据库是否支持异步访问。
private
static
bool
SupportsAsync(Database db)
{
if
(db.SupportsAsync)
{
Console.WriteLine(
"
Database supports asynchronous operations
"
);
return
true
;
}
Console.WriteLine(
"
Database does not support asynchronous operations
"
);
return
false
;
}
使用异步访问数据库通常需要一个callback,在调用端会打开另外一个线程。这个callback通常不会直接访问winform和wpf的用户界面。你需要在用户界面添加事件,然后通过委托的方式更新用户界面。
委托可以使用Lambda表达式实现。
DbCommand cmd
=
asyncDB.GetStoredProcCommand(
"
ListOrdersSlowly
"
);
asyncDB.AddInParameter(cmd,
"
state
"
, DbType.String,
"
Colorado
"
);
asyncDB.AddInParameter(cmd,
"
status
"
, DbType.String,
"
DRAFT
"
);
//
Execute the query asynchronously specifying the command and the
//
expression to execute when the data access process completes.
asyncDB.BeginExecuteReader(cmd,
asyncResult
=>
{
//
Lambda expression executed when the data access completes.
try
{
using
(IDataReader reader
=
asyncDB.EndExecuteReader(asyncResult))
{
A Guide to Developing with Enterprise Library
5.0
47
Console.WriteLine();
DisplayRowValues(reader);
}
}
catch
(Exception ex)
{
Console.WriteLine(
"
Error after data access completed: {0}
"
, ex.Message);
}
},
null
);
异步获取对象形式返回的数据
object
[] paramArray
=
new
object
[] {
"
%bike%
"
,
20
};
//
Create the accessor. This example uses the simplest overload.
var accessor
=
asyncDB.CreateSprocAccessor
<
Product
>
(
"
GetProductsSlowly
"
);
//
Execute the accessor asynchronously specifying the callback expression,
//
the existing accessor as the AsyncState, and the parameter values array.
accessor.BeginExecute(
asyncResult
=>
{
//
Lambda expression executed when the data access completes.
try
{
//
Accessor is available via the asyncResult parameter
var acc
=
(IDataAccessor
<
Product
>
) asyncResult.AsyncState;
//
Obtain the results from the accessor.
var productData
=
acc.EndExecute(asyncResult);
//
Perform a client‐side query on the returned data.
//
Be aware that the orderby and filtering is happening
//
on the client, not inside the database.
var results
=
from productItem
in
productData
where
productItem.Description
!=
null
orderby productItem.Name
select
new
{ productItem.Name, productItem.Description };
//
Display the results
Console.WriteLine();
foreach
(var item
in
results)
{
Console.WriteLine(
"
Product Name: {0}
"
, item.Name);
Console.WriteLine(
"
Description: {0}
"
, item.Description);
Console.WriteLine();
}
}
catch
(Exception ex)
{
Console.WriteLine(
"
Error after data access completed: {0}
"
, ex.Message);
}
}, accessor, paramArray);
更新数据库
string
oldDescription
=
"
Carries 4 bikes securely; steel construction, fits 2\
"
receiver hitch.
"
;
string
newDescription
=
"
Bikes tend to fall off after a few miles.
"
;
//
Create command to execute the stored procedure and add the parameters.
DbCommand cmd
=
defaultDB.GetStoredProcCommand(
"
UpdateProductsTable
"
);
defaultDB.AddInParameter(cmd,
"
productID
"
, DbType.Int32,
84
);
defaultDB.AddInParameter(cmd,
"
description
"
, DbType.String, newDescription);
//
Execute the query and check if one row was updated.
if
(defaultDB.ExecuteNonQuery(cmd)
==
1
)
{
//
Update succeeded.
}
else
{
Console.WriteLine(
"
ERROR: Could not update just one row.
"
);
}
//
Change the value of the second parameter
defaultDB.SetParameterValue(cmd,
"
description
"
, oldDescription);
//
Execute query and check if one row was updated
if
(defaultDB.ExecuteNonQuery(cmd)
==
1
)
{
//
Update succeeded.
}
else
{
Console.WriteLine(
"
ERROR: Could not update just one row.
"
);
}