使用DbConnection要记得手工关闭
上次不小心调用DbConnection后忘记手工关闭了,原以为它会和Stream一样,
在生命周期结束之后就会自己关闭掉,但事实证明,不是这样的,看MSDN是这样说的:
Close 方法回滚任何挂起的事务。然后,它将连接释放到连接池,或者在连接池被禁用的情况下关闭连接。
应用程序可以多次调用 Close。不会生成任何异常。
如果 DbConnection 超出范围,则不会将其关闭。因此,必须通过调用功能上等效的 Close 或 Dispose 显式关闭该连接。
如果将连接池值 Pooling 设置为 true 或 yes,则也会释放物理连接。
使用DbProviderFactories并扩充兼容mysql
ADO.NET2.0 为各种ADO.NET类引入了一个提供程序工厂的模型以及通用基类。
ADO.NET通用接口的限制
接口不易扩展 ADO.NET1.1无法创建某些类的实例
ADO.NET1.1无法判断可用的.NET数据提供程序。
提供工厂模型如何解决上述限制
通过抽象积累来扩展ADO,NET模型
使用DbProviderFactory类来创建对象
提供程序工厂模型的限制
许多查询结构都是数据库特有的
为参数化查询设置CommandText时,可能需要提供程序特有的代码
指定参数数据类型可能需要提供程序特有的代码
为了使开发的代码通用,不局限于特定的数据库,本次开发中决定使用DbProviderFactory+标准SQL,
以开发一个适用于mysql和sqlserver的封装,但DbProviderFactories
并没有提供对mysql的DbProviderFactory的支持,
所以需要扩充DbProviderFactories类兼容mysql, 而且在ADO.net
2.0中mysql和sqlserver的ParameterMarkerFormat都有bug,所以扩展类顺带解决这个bug
public static class DbProviderFactoryEx
{
public static DbProviderFactory GetFactory ( string providerName )
{
if ( providerName == null )
throw new ArgumentNullException ( "providerName" );
switch ( providerName )
{
case "MySql.Data.MySqlClient" :
return new MySqlClientFactory ();
default :
return DbProviderFactories . GetFactory ( providerName );
}
}
public static string GetParameterMarkerFormat ( DbConnection connect )
{
if ( connect == null )
throw new ArgumentNullException ( "connect" );
Type type = connect . GetType ();
if ( type == typeof ( MySql . Data . MySqlClient . MySqlConnection ))
return MySqlParameterMarkerFormat ; //mysql bug
else
if ( type == typeof ( System . Data . SqlClient . SqlConnection ))
return SqlServerParameterMarkerFormat ; //ms bug
connect . Open ();
string result =
connect . GetSchema ( "DataSourceInformation" ). Rows [ 0 ][ "ParameterMarkerFormat" ]. ToString ();
connect . Close ();
return result ;
}
public static readonly string SqlServerParameterMarkerFormat = "@{0}" ;
public static readonly string MySqlParameterMarkerFormat = "?{0}" ;
}
开启sql2005远程连接功能
连接到SQL Server 2005时,在默认的设置下SQL
Server不允许进行远程连接,需要自己手工开启,详细步骤如下:
开始 -->
所有程序 -->
Microsoft SQL Server 2005 -->
Configuration Tools -->
SQL Server Surface Area Configuration -->
Surface Area Configuration for Services and Connections -->
MSSQLSERVER节点下的Database Engine 节点 -->
Remote Connections -->
Local and remote connections -->
选择Using Tcp/Ip only 或 Using both Tcp/Ip and named pipes
开始 -->
所有程序 -->
Microsoft SQL Server 2005 -->
Configuration Tools -->
SQL Server configuration Manager -->
重启数据库服务.
使用MySql中文乱码问题
有权限修改my.ini
[mysqld]
default-character-set = utf8
没有权限修改my.ini
假设数据库采用了默认编码latin1,经研究发现:
Select出的数据需要将其从拉丁文转化为GBK
传入的SQL语句需要将其从GBK转化为拉丁文
实现:
public static class DBHelper
{
public static T Get < T >( DbDataRecord record , string field )
{
int num = record . GetOrdinal ( field );
if ( record . IsDBNull ( num ))
return default ( T );
return ( T ) record [ num ];
}
public static int ? ToInt32 ( object value )
{
if ( value == null )
return null ;
return (( IConvertible ) value ). ToInt32 ( null );
}
public static void AddParameter ( string name , object value , DbCommand cmd )
{
DbParameter para = cmd . CreateParameter ();
para . ParameterName = string . Format ( ParameterMarkerFormat , name );
if ( value == null )
para . Value = DBNull . Value ;
else
para . Value = value ;
cmd . Parameters . Add ( para );
}
public static String LatinToGBK ( String str )
{
try
{
byte [] bytesStr = Encoding . GetEncoding ( "latin1" ). GetBytes ( str );
return Encoding . GetEncoding ( "GB2312" ). GetString ( bytesStr );
}
catch
{
return str ;
}
}
public static String GBKToLatin ( String str )
{
try
{
byte [] bytesStr = Encoding . GetEncoding ( "GB2312" ). GetBytes ( str );
return Encoding . GetEncoding ( "latin1" ). GetString ( bytesStr );
}
catch
{
return str ;
}
}
public static string ParameterMarkerFormat =
DbProviderFactoryEx . SqlServerParameterMarkerFormat ;
}
应用:
// 处理DbDataReader
using ( DbDataReader reader = cmd . ExecuteReader ())
{
foreach ( DbDataRecord record in reader )
{
role . ID = DBHelper . Get < uint >( record , "ID" );
role . Name = DBHelper . LatinToGBK ( DBHelper . Get < string >( record , "RoleName" ));
role . Account = DBHelper . LatinToGBK ( DBHelper . Get < string >( record , "Account" ));
return true ;
}
}
// 处理DataTable
foreach ( DataRow row in table . Rows )
{
row [ "OperationType" ] = DBHelper . LatinToGBK ( row [ "OperationType" ]. ToString ());
row [ "OperationReason" ] = DBHelper . LatinToGBK ( row [ "OperationReason" ]. ToString ());
}
// 传入的SQL语句
DbCommand cmd = conn . CreateCommand ();
cmd . CommandText = "select * from role where rolename=?rolename" ;
DBHelper . AddParameter ( "rolename" , DBHelper . GBK2Latin ( name ), cmd );
避免SQL注入和特殊字符的一种方法
避免SQL注入和特殊字符的办法有很多,不同数据库也有不同数据库的解决方案,ADO.NET中使用DbCommand.Parameters解决这个问题,为了了解他的原理,我查了一下.NET中SQLCommand的源代码和MySQL.NET中MySQLCommand的源代码。
.NET源代码超级难跟踪,貌似是用了SQL
Server独有的特性,把参数的名称、类型、把值的二进制都传给了SQL Server
API,具体没有跟到。
MySQL实现就很简单了,他会把字符串类型的参数value使用一个过滤数组来过滤,在过滤字符前面加上“\”,我认为这是一种通用的做法,虽然效率上不如SQLCommand好,但是和容易移植更容易看懂!
使用PHP+MySQL做网站的同学也可以利用着类似的思想生成安全的SQL连接串,我看了PHPWind数据库处理函数,貌似并没有对所有的过滤字符进行处理,这就存在着一些安全隐患。
源代码
private static string stringOfBackslashChars = "\u005c\u00a5\u0160\u20a9\u2216\ufe68\uff3c" ;
private static string stringOfQuoteChars = "\\u0027\\u00b4\\u02b9\\u02ba\\u02bb\\u02bc\\u02c8\\u02ca\\u02cb\\u02d9\\u0300\\u0301\\u2018\\u2019\\u201a\\u2032\\u2035\\u275b\\u275c\\uff07" ;
public static string EscapeString ( string value )
{
StringBuilder sb = new StringBuilder ();
foreach ( char c in value )
{
if ( stringOfQuoteChars . IndexOf ( c ) >= 0 || stringOfBackslashChars . IndexOf ( c ) >= 0 )
sb . Append ( "\\" );
sb . Append ( c );
}
return sb . ToString ();
}
本文链接, 未经许可,禁止转载