非常有效的数据库/配置文件访问模型。成功使用在几万流量的网站上。任何建议欢迎大家交流。
在使用sqlcommand对象过程中,我们需要分配connection对象。 通常,对于大型的entity业务模型来说分配新的sqlconnection的操作非常频繁。要利用pool的优化功能,就要想到保持connection对象。由此想到可以把connection和transaction都保存到connectionproxy对象中。而此对象继承idisposable仅仅存在于一个request过程中。作为一个适用广泛的模型,我们建立executioncontext对象来封装对connectionproxy操作。
以下是connectionproxy代码:
internal class connectionproxy : idisposable
...{
private string _connectionstring = null;
private sqlconnection _connection;
private sqltransaction _transaction;
private int _trancount = 0;
/**//// <summary>
/// constructs a new connectionproxy instance, setting the connection string
/// </summary>
/// <param name="connectionstring">a valid database connection string</param>
internal connectionproxy(string connectionstring)
...{
_connectionstring = connectionstring;
}
/**//// <summary>
/// frees any connection related resources
/// </summary>
public void dispose()
...{
// ensure that the connection does not have a pending transaction
if (_trancount != 0)
...{
// rollback the transaction stack until the rollback already occurs
while (_trancount > 0) this.rollbacktransaction();
throw new dataaccessexception("dispose was called on a connection with a pending transaction. the transaction will be aborted.");
}
// close the connection if it is open
if ((_connection != null) && (_connection.state == connectionstate.open))
...{
_connection.close();
}
_connection = null;
}
/**//// <summary>
/// gets the current connection object
/// </summary>
internal sqlconnection connection
...{
get
...{
// check that the connection string property has been set
if (_connectionstring == null)
...{
//throw new dataaccessexception("connection string has not been set.");
}
// create new connection and open if one does not yet exist
if (_connection == null)
...{
_connection = new sqlconnection(_connectionstring);
_connection.open();
//while (_connection.state == connectionstate.open) ;
}
return _connection;
}
}
/**//// <summary>
/// gets the current transaction context object
/// </summary>
internal sqltransaction transaction
...{
get
...{
return _transaction;
}
}
/**//// <summary>
/// begins a new transaction
/// </summary>
internal void begintransaction()
...{
// only actually begin a new transaction if a transaction context does not yet exist
if (_trancount == 0)
...{
// create new transaction context at the specified isolation level
_transaction = connection.begintransaction(isolationlevel.serializable);
}
_trancount++;
}
/**//// <summary>
/// commits a pending transaction
/// </summary>
internal void committransaction()
...{
// check that a transaction context actually exists
if (_trancount <= 0) throw new dataaccessexception("no transaction is pending");
_trancount--;
// check if an actual commit should occur
if (_trancount == 0)
...{
// if trancount is zero, but we don't have a transaction then something is wrong
if (_transaction == null)
...{
throw (new dataaccessexception("transaction stack indicated a commit but no transaction exists!"));
}
// actually commit the transaction
_transaction.commit();
_transaction = null;
}
}
/**//// <summary>
/// rolls back a pending transaction
/// </summary>
internal void rollbacktransaction()
...{
// check that a transaction context actually exists
if (_trancount <= 0) throw new dataaccessexception("no transaction is pending");
_trancount--;
// check if an actual rollback should occur
if (_trancount == 0)
...{
// if trancount is zero, but we don't have a transaction then something is wrong
if (_transaction == null)
...{
throw (new dataaccessexception("transaction stack indicated a rollback but no transaction exists!"));
}
// actually rollback the transaction
_transaction.rollback();
_transaction = null;
}
}
}
之后我们可以建立executioncontext.目的是使用这个proxy.当然也可以保存和使用其他的实例化对象:
public sealed class executioncontext
...{
private static string connproxy = "connproxy";
private static string configproxy = "config";
private static connectionproxy _connproxy;
private static config _config;
/**//// <summary>
/// this class cannot be instantiated
/// </summary>
private executioncontext()
...{
}
/**//// <summary>
///
/// </summary>
private static connectionproxy connectionproxy
...{
get
...{
if (httpcontext.current != null) //web app
return (connectionproxy)httpcontext.current.items[connproxy];
else
return _connproxy;
}
set
...{
if(httpcontext.current != null) //web app
httpcontext.current.items.add(connproxy, value);
else
_connproxy = value;
}
}
private static config config
...{
get
...{
if (httpcontext.current != null) //web app
return (config)httpcontext.current.items[configproxy];
else
return _config;
}
set
...{
if (httpcontext.current != null) //web app
httpcontext.current.items.add(configproxy, value);
else
_config = value;
}
}
/**//// <summary>
/// returns the connection object for the current execution context
/// </summary>
public static sqlconnection connection
...{
get
...{
assertinitialisation();
return connectionproxy.connection;
}
}
/**//// <summary>
/// returns the current transaction object for the current execution context
/// </summary>
public static sqltransaction transaction
...{
get
...{
assertinitialisation();
return connectionproxy.transaction;
}
}
/**//// <summary>
/// </summary>
public static config configuration
...{
get
...{
if (config == null)
throw new exception("config.xml cannot be loaded!");
return config;
}
}
/**//// <summary>
/// begins a new execution context
/// </summary>
public static void begin()
...{
// cleanup from any previous begin calls
end();
// create a configuration object
config = new config();
// create a new database connection proxy
connectionproxy = new connectionproxy(config.connectionstring);
}
/**//// <summary>
/// ends the current execution context and cleans up any resources used
/// </summary>
public static void end()
...{
// clean up any objects that have not been cleaned up since the last begin call on the thread
if (connectionproxy != null) connectionproxy.dispose();
if (httpcontext.current != null) //web app
...{
httpcontext.current.items.remove(connproxy);
httpcontext.current.items.remove(configproxy);
}
}
/**//// <summary>
/// begins a new transaction
/// </summary>
public static void begintransaction()
...{
assertinitialisation();
connectionproxy.begintransaction();
}
/**//// <summary>
/// commits the current transaction
/// </summary>
public static void committransaction()
...{
assertinitialisation();
connectionproxy.committransaction();
}
/**//// <summary>
/// rolls back the current transaction
/// </summary>
public static void rollbacktransaction()
...{
assertinitialisation();
connectionproxy.rollbacktransaction();
}
/**//// <summary>
/// asserts that the execution context has been correctly initialised
/// </summary>
private static void assertinitialisation()
...{
if (connectionproxy == null)
...{
throw new executioncontextexception("execution context has not been initialised.");
}
}
}
使用的时候,要在global.asax的application_beginrequest中加入:executioncontext.begin();和application_endrequest中加入:executioncontext.end();也可以在winform程序的application中加入这2行。
准备工作完成后我们就可以来测试了:
executioncontext.begintransaction();
try
...{
cmd.connection = executioncontext.connection;
cmd.transaction = executioncontext.transaction;
cmd.executenonquery();
executioncontext.committransaction();
}
catch
...{
executioncontext.rollbacktransaction();
throw;
}
总结:
非常有效的数据库/配置文件访问模型。成功使用在几万流量的网站上。任何建议欢迎大家交流。
新闻热点
疑难解答
图片精选