数据应用程序块把我们从大量的机械的数据库访问代码中解放出来,同时给我们带来了在多个数据库甚至多个不同类型的数据库的中无缝切换的可能。
数据应用程序块的类继承层次非常简单,这里就不做详细介绍了(向看此文的朋友问个问题:为什么Vs.net2003的类视图窗口中很多类都不显示?)。
我们在我前面的配置应用程序块中提到过,要读取我们的自定义配置数据,首先要写一个配置数据类,数据应用程序块也不例外,这个类就是DatabaseSettings,其结构如下图:
![](https://lifework.cnblogs.com/images/cnblogs_com/lifework/DataConfigurationData.gif)
从这个图里,我们可以看到DatabaseSettings主要包括3个集合类,DatabaseTypeData说明了配置的数据库类型,ConnectionStringData指明了每种数据库类型的连接串,InstanceData指明了当前的数据库实例。
2、使用说明
使用时分两步:先用配置控制台添加数据应用程序块,再在程序中写代码。
⑴用配置控制台添加数据应用程序块
①添加数据应用程序块,之后是分别设置应用程序要访问的数据库类型、每个类型的数据库即连接串和当前的默认数据库。
②添加应用程序要访问的数据库类型(Database Types),如SQL Server、Oracle、Sybase、Access等
③设置每个数据库的连接串。添加上相应的连接参数。
④设定默认的数据库及其连接串。
⑤设置完毕,保存。
⑵写数据访问代码
数据应用程序块支持我们常见的大部分功能:执行SQL文本,执行存储过程、启用事务、离线处理、批量更新等等,下面我们分别举例说明。
①执行SQL语句
Database db = DatabaseFactory.CreateDatabase();
string sql = "SELECT * FROM [AddressList]";
DBCommandWrapper cmdWrapper = db.GetSqlStringCommandWrapper(sql);
return db.ExecuteDataSet(cmdWrapper);
②执行存储过程
Database db = DatabaseFactory.CreateDatabase();
string sqlCommand = "GetUserById";
DBCommandWrapper cmd = db.GetStoredProcCommandWrapper(sqlCommand);
cmd.
Literal1.Text = (string)db.ExecuteScalar(cmd);
③启用事务
Database db = DatabaseFactory.CreateDatabase();
string sqlCommand = "CreditAccount";
DBCommandWrapper creditCommandWrapper = db.GetStoredProcCommandWrapper(sqlCommand);
sqlCommand = "DebitAccount";
DBCommandWrapper debitCommandWrapper = db.GetStoredProcCommandWrapper(sqlCommand);
using (IDbConnection connection = db.GetConnection())//
{
connection.Open();
IDbTransaction transaction = connection.BeginTransaction();
try
{
db.ExecuteNonQuery(creditCommandWrapper, transaction);
db.ExecuteNonQuery(debitCommandWrapper, transaction);
transaction.Commit();
result = true;
}
catch
{
transaction.Rollback();
}
connection.Close();
return result;
}
④批量更新
Database db = DatabaseFactory.CreateDatabase();
DataSet productsDataSet = new DataSet();
string sqlCommand = "Select ProductID, ProductName, CategoryID, UnitPrice, LastUpdate From Products";
DBCommandWrapper dbCommandWrapper = db.GetSqlStringCommandWrapper(sqlCommand);
string productsTable = "Products";
db.LoadDataSet(dbCommandWrapper, productsDataSet, productsTable);
DataTable table = productsDataSet.Tables[productsTable];
DataRow addedRow = table.Rows.Add(new object[] {DBNull.Value, "New product", 11, 25});
table.Rows[0]["ProductName"] = "Modified product";
DBCommandWrapper insertCommandWrapper = db.GetStoredProcCommandWrapper("AddProduct");
insertCommandWrapper.AddInParameter("@ProductName", DbType.String, "ProductName", DataRowVersion.Current);
insertCommandWrapper.AddInParameter("@CategoryID", DbType.Int32, "CategoryID", DataRowVersion.Current);
insertCommandWrapper.AddInParameter("@UnitPrice", DbType.Currency, "UnitPrice", DataRowVersion.Current);
DBCommandWrapper deleteCommandWrapper = db.GetStoredProcCommandWrapper("DeleteProduct");
deleteCommandWrapper.AddInParameter("@ProductID", DbType.Int32, "ProductID", DataRowVersion.Current);
DBCommandWrapper updateCommandWrapper = db.GetStoredProcCommandWrapper("UpdateProduct");
updateCommandWrapper.AddInParameter("@ProductID", DbType.Int32, "ProductID", DataRowVersion.Current);
updateCommandWrapper.AddInParameter("@ProductName", DbType.String, "ProductName", DataRowVersion.Current);
updateCommandWrapper.AddInParameter("@LastUpdate", DbType.DateTime, "LastUpdate", DataRowVersion.Current);
int rowsAffected = db.UpdateDataSet(productsDataSet, "Products", insertCommandWrapper, updateCommandWrapper,
deleteCommandWrapper, UpdateBehavior.Standard);
这里的代码来源于Quick Start,这里需要注意的就是DataRowVersion.Current,批量更新时根据具体情况选择其值。同时ExecuteScalar、UpdateDataSet等方法重载好几个不同的方法原型,您可以根据具体情况选择。
⑶注意的问题
这里需要注意的就是在执行存储过程时,有两个获取IDbCommand对象的方法:
public abstract DBCommandWrapper GetStoredProcCommandWrapper(string storedProcedureName);
public abstract DBCommandWrapper GetStoredProcCommandWrapper(string storedProcedureName, params object[] parameterValues);
如果你要缓存参数,就用第二个方法,不缓存参数就用第一个方法(不知我说的对不对?),因为在准备IDbCommand对象时,有下面的语句,
if (command.IsFurtherPreparationNeeded())
{
parameterCache.FillParameters(command, ParameterToken);
}
这样,在用第二个方法时,会从缓存中取参数,当你多次调用同一存储过程时,可以考虑用用第二个方法,当然,如果有必要可以缓存执行的结果,性能更佳,这将用到缓存应用程序块。
还需要提一句,就是对于保存在配置文件中的数据库信息,我们也是可以加密的,方法与配置应用程序块的加密方式一致。可以看我关于配置应用程序块的使用方法说明。每个应用程序块的配置数据都是可以加密保存的。
有人已经制作了使用数据应用程序块的模板,比较不错:http://cstemplates.sourceforge.net
1、概述
当在不安全信道上传递一些敏感数据时,要对这些数据采取保护措施,即便是被人截获也难以从中得到所携带的信息,当我们保存一些敏感数据到物理设备时。也需要先将这些数据加密。
在对数据加密时,对于不同的场景采用的加密方式是不同的,一般来说常用的数据加密方式有三种。
² 私钥加密:私钥加密算法使用单个私钥来加密和解密数据。由于具有密钥的任意一方都可以使用该密钥解密数据,因此必须保护密钥不被未经授权的代理得到。私钥加密又称为对称加密或单钥加密,因为同一密钥既用于加密又用于解密。私钥加密算法非常快(与公钥算法相比),特别适用于对较大的数据流执行加密转换。一般情况下,对正式数据的传送中,一般先用公钥传送对称密钥,再用这个对称密钥加密要传送的数据。私钥加密的关键是如何保护对称密钥。
² 公钥加密:公钥加密使用一个必须对未经授权的用户保密的私钥和一个可以对任何人公开的公钥。公钥和私钥都在数学上相关联;用公钥加密的数据只能用私钥解密,而用私钥签名的数据只能用公钥验证。公钥可以提供给任何人;公钥用于对要发送到私钥持有者的数据进行加密。两个密钥对于通信会话都是唯一的。公钥加密算法也称为不对称算法,原因是需要用一个密钥加密数据而需要用另一个密钥来解密数据。它只可以加密少量数据。公钥加密一般用于交换私钥加密的私钥、对摘要进行签名等。
单向哈希加密:哈希算法将任意长度的二进制值映射为固定长度的较小二进制值,这个小的二进制值称为哈希值。哈希值是一段数据唯一且极其紧凑的数值表示形式。如果散列一段明文而且哪怕只更改该段落的一个字母,随后的哈希计算都将产生不同的值。要找到散列为同一个值的两个不同的输入,在计算上是不可能的。注意当用哈希算法对消息进行散列后是不能解密的,只能对同样的消息用同样的哈希算法加密,对照加密后的值是否一致,从而判断消息是否被篡改。哈希加密主要用于密码验证、身份验证、数据完整性等方面。比如数字签名的基本原理如下:
可以看出,数字签名不是一种具体的技术实现,它是基于以上各种加密技术组合的一种数据验证解决方案。
我们在配置应用程序块中已说明如何将配置数据加密,其他应用程序块的加密方式也相同,一般数据和配置应用程序块的配置数据我们会加密。比较理想的做法是用配置控制台将配置文件加密时,用DPAPI Provider ,这样我们就不用保存对称密钥了,系统会自动管理,这样数据加密就完全透明了,无需我们任何干预。
2、使用步骤
跟前面的配置方法一致,添加加密应用程序块后,会有两个节点Hash Provider和Symmetric Provider,没有提供Asymmetric Provider,分别提供哈希加密和对称加密的功能。这里已对称加密的设置为例来说明,在Symmetric Provider节点上点右键,New->Symmetric Algorithm Provider,选RijndaelManaged,Rijndael是美国AES选定的加密标准,DES(原来的加密标准)会逐渐被放弃,.Net Framework中内置的Rijndael算法的secure key为32byte,IV为16byte,比其他算法强度都高,而且非常高效,是首先得对称加密算法。选定RijndaelManaged后,我们再选Generate创建一个对称密钥,然后确认就可以了。
我们一般用其Façade类Cryptographer来写代码,
// 哈希算法的四个方法原型如下:
public static byte[] CreateHash(string hashInstance, byte[] plaintext);
public static string CreateHash(string hashInstance, string plaintext);
public static bool CompareHash(string hashInstance, byte[] plaintext, byte[] hashedText);
public static bool CompareHash(string hashInstance, string plaintext, string hashedText);
// 对称加解密的四个方法原型如下:
public static string EncryptSymmetric(string symmetricInstance, string plaintext);
public static byte[] EncryptSymmetric(string symmetricInstance, byte[] plaintext);
public static string DecryptSymmetric(string symmetricInstance, string ciphertextBase64);
public static byte[] DecryptSymmetric(string symmetricInstance, byte[] ciphertext);
①对称加密代码// 加密
private const string symmProvider = "SymmProvider";
string cipherText = Cryptographer.EncryptSymmetric(symmProvider, 要加密的文本);
//解密
string plainText = Cryptographer.DecryptSymmetric(symmProvider, 密码文本);
// 或者用下面的代码
//加密
String[] cipherText = Cryptographer.EncryptSymmetric(symmProvider, Encoding.Unicode.GetBytes(password));
//解密
string[] plainText = Cryptographer.DecryptSymmetric(symmProvider, Encoding.Unicode.GetBytes(password));
注意这里的symmProvider 是指标示具体的加密算法节点名称,如RijndaelManaged,而不是节点“Symmetric Providers”的名称。上面两种加解密代码,到底用那种要看你具体应用情况。
②哈希算法代码
private const string hashProvider = "PasswordHasher";
string hashPassword = Cryptographer.CreateHash(hashProvider, originalPassword);//生成摘要
bool isPass = Cryptographer.CompareHash(hashProvider, originalPassword, hashPassword);//比较是否与原来的相同
// 或者
String[] hashPassword = Cryptographer.CreateHash(hashProvider, Encoding.Unicode.GetBytes(password));//生成摘要
bool isPass = Cryptographer.CompareHash(hashProvider, Encoding.Unicode.GetBytes(password), hashPassword);//比较是否与原来的相同
还有就是用哈希加密时一定要用Salted hash,这样强度更高。
数据程序应用