[英]Convert persisted ADO 2.8 COM recordset to ADO.Net DataSet
我有一个VB6应用程序,我转换为.net。 我分阶段这样做,所以客户端同时拥有VB6和.net应用程序。 部分应用程序将ADO 2.8 COM记录集缓存到SQL Server中的表,并根据需要检索它们。 .net应用程序使用相同的持久记录集。 我有c#代码检索持久化记录集并将其转换为数据集。 我的问题是 - 我是否以最有效的方式做到了?
这是我从数据库中检索记录集的代码 -
Stream adoStream = null;
SqlParameter cmdParameter;
SqlCommand cmd = null;
SqlDataReader dr = null;
string cmdText;
int bytesReturned;
int chunkSize = 65536;
int offSet = 0;
UnicodeEncoding readBytes;
try
{
cmdParameter = new SqlParameter(parameterName, idParamter);
cmdText = sqlString;
cmd = new SqlCommand();
cmd.CommandType = CommandType.Text;
cmd.CommandTimeout = 0;
cmd.CommandText = cmdText;
cmd.Connection = this.pbiSQLConnection;
cmd.Parameters.Add(cmdParameter);
dr = cmd.ExecuteReader(CommandBehavior.SequentialAccess);
dr.Read();
if (dr.HasRows)
{
readBytes = new UnicodeEncoding();
byte[] byteChunk = new byte[chunkSize];
adoStream = new Stream();
adoStream.Type = StreamTypeEnum.adTypeText;
adoStream.Open(Type.Missing, ConnectModeEnum.adModeUnknown,
StreamOpenOptionsEnum.adOpenStreamUnspecified, "", "");
do
{
bytesReturned = (int)dr.GetBytes(0, offSet, byteChunk, 0,
chunkSize);
size += bytesReturned;
if (bytesReturned > 0)
{
if (bytesReturned < chunkSize)
{
Array.Resize(ref byteChunk, bytesReturned);
}
adoStream.WriteText(readBytes.GetString(byteChunk),
StreamWriteEnum.stWriteChar);
adoStream.Flush();
}
offSet += bytesReturned;
} while (bytesReturned == chunkSize);
}
}
catch (Exception exLoadResultsFromDB)
{
throw (exLoadResultsFromDB);
}
finally
{
if (dr != null)
{
if (!dr.IsClosed)
{
dr.Close();
}
dr.Dispose();
}
if (cmd != null)
{
cmd.Dispose();
}
}
这是将ado流转换为数据集的代码 -
adoStream = LoadTextFromDBToADODBStream(resultID, "@result_id",
"some sql statement", ref size);
if (adoStream.Size == 0)
{
success = false;
}
else
{
adoStream.Position = 0;
DataTable table = new DataTable();
Recordset rs = new Recordset();
rs.Open(adoStream, Type.Missing, CursorTypeEnum.adOpenStatic,
LockTypeEnum.adLockBatchOptimistic, -1);
if (adoStream != null)
{
adoStream.Close();
adoStream = null;
}
source.SourceRows = rs.RecordCount;
table.TableName = "Source";
source.Dataset = new DataSet();
source.Dataset.Tables.Add(table);
OleDbDataAdapter adapter = new OleDbDataAdapter();
adapter.MissingSchemaAction = MissingSchemaAction.AddWithKey;
adapter.Fill(source.Dataset.Tables[0], rs);
if (adapter != null)
{
adapter.Dispose();
adapter = null;
}
if (adoStream != null)
{
adoStream.Close();
adoStream = null;
}
if (rs != null)
{
if (rs.State == 1)
{
rs.Close();
}
rs = null;
}
}
谢谢大家
编辑:我添加了一个赏金,看看是否有人可以使代码更有效。
一般来说,你没有充分利用using语句并自己处理它。 不幸的是,你这样做是错误的,因为如果你有一个IDisposable的实现,它会在调用Dispose时抛出异常,那么其他对Dispose的调用就不会发生。 如果使用using语句,则无论它们是如何嵌套的,都将调用IDisposable.Dispose的所有实现。
我们先来看看LoadTextFromDBToADODBStream。 这里的大量问题是,你分享的时候,你不应该是一个连接。 您应该为您的操作创建连接,使用它,然后关闭它。 这里情况不同。
因此,我们假设您使用单独的方法创建连接,如下所示:
SqlConnection CreateConnection()
{
// Create the connection here and return it.
return ...;
}
您还需要以下结构来正确管理COM引用:
struct ComReference<T> : IDisposable where T : class, new()
{
private T reference;
public T Reference { get { return reference; } }
public static ComReference<T> Create()
{
// Create the instance.
ComReference<T> retVal = new ComReference<T>();
// Set the reference.
retVal.reference = new T();
// Return.
return retVal;
}
public ComReference<T> Release()
{
// Create a copy for return.
// Note, this is copied on the stack.
ComReference<T> retVal = this;
// Set this reference to null;
this.reference = null;
// Return the reference.
return retVal;
}
public void Dispose()
{
// If there is a reference, then release.
Marshal.ReleaseComObject(reference);
}
}
您希望使用此方法管理COM引用,以便在完成它们时释放它们,而不是通过垃圾回收。 COM依赖于确定性的最终化,你不会因为你在.NET中而忽略它。 上面的结构利用IDisposable(事实上它是一个结构和它带来的细微差别)以确定的方式帮助这样做。
类型参数T
将是为COM互操作创建的类类型,对于流,它将是ADODB.StreamClass。
您的LoadTextFromDBToADODBStream然后如下所示:
ComReference<StreamClass> LoadTextFromDBToADODBStream(int idParameter,
string parameterName, string sqlString, ref int size)
{
int bytesReturned;
int chunkSize = 65536;
int offSet = 0;
// Create the command.
using (SqlCommand cmd = new SqlCommand())
{
// Set the parameters.
cmd.CommandType = CommandType.Text;
cmd.CommandTimeout = 0;
cmd.CommandText = sqlString;
// See (1).
using (SqlConnection connection = CreateConnection())
{
// Set the connection on the command.
cmd.Connection = connection;
// Create the parameter and add to the parameters.
SqlParameter cmdParameter = new SqlParameter(
parameterName, idParameter);
cmd.Parameters.Add(cmdParameter);
// Create the reader.
using (SqlDataReader dr = cmd.ExecuteReader(
CommandBehavior.SequentialAccess))
{
dr.Read();
// See (2)
if (!dr.HasRows)
{
// Return an empty instance.
return new ComReference<StreamClass>();
}
// Create the stream here. See (3)
using (ComReference<StreamClass> adoStreamClass =
ComReference<StreamClass>.Create())
{
// Get the stream.
StreamClass adoStream = adoStreamClass.Reference;
// Open the stream.
adoStream.Type = StreamTypeEnum.adTypeText;
adoStream.Open(Type.Missing,
ConnectModeEnum.adModeUnknown,
StreamOpenOptionsEnum.adOpenStreamUnspecified,
"", "");
// Create the byte array.
byte[] byteChunk = new byte[chunkSize];
// See (4)
Encoding readBytes = Encoding.Unicode;
// Cycle.
do
{
bytesReturned = (int)dr.GetBytes(0, offSet,
byteChunk, 0, chunkSize);
size += bytesReturned;
if (bytesReturned > 0)
{
if (bytesReturned < chunkSize)
{
Array.Resize(ref byteChunk,
bytesReturned);
}
adoStream.WriteText(
readBytes.GetString(byteChunk),
StreamWriteEnum.stWriteChar);
adoStream.Flush();
}
offSet += bytesReturned;
} while (bytesReturned == chunkSize);
// Release the reference and return it.
// See (5).
return adoStreamClass.Release();
}
}
}
}
}
笔记:
ComReference<StreamClass>
的新实例更容易。 当你创建它时,它具有返回没有引用的结构的效果(这是你想要的,而不是调用静态Create方法)。 ComReference<StreamClass>
。 这样,StreamClass引用仍然存在,并且当在堆栈变量上调用Dispose时,它不会将该引用传递给ReleaseComObject。 继续调用LoadTextFromDBToADODBStream的代码:
// See (1)
using (ComReference<StreamClass> adoStreamClass =
LoadTextFromDBToADODBStream(resultID, "@result_id",
"some sql statement", ref size))
{
// Set to the class instance. See (2)
StreamClass adoStream = adoStreamClass.Reference;
if (adoStream.Size == 0)
{
success = false;
}
else
{
adoStream.Position = 0;
DataTable table = new DataTable();
// See (3)
using (ComReference<RecordsetClass> rsClass =
ComReference<RecordsetClass>.Create())
{
Recordset rs = rsClass.Reference;
rs.Open(adoStream, Type.Missing, CursorTypeEnum.adOpenStatic,
LockTypeEnum.adLockBatchOptimistic, -1);
if (adoStream != null)
{
adoStream.Close();
adoStream = null;
}
source.SourceRows = rs.RecordCount;
table.TableName = "Source";
source.Dataset = new DataSet();
source.Dataset.Tables.Add(table);
// See (4)
using (OleDbDataAdapter adapter = new OleDbDataAdapter())
{
adapter.MissingSchemaAction =
MissingSchemaAction.AddWithKey;
adapter.Fill(source.Dataset.Tables[0], rs);
}
}
}
}
adoStreamClass.Reference.<method>
ComReference<RecordsetClass>
。 在使用using语句时,您可以清理许多使其难以阅读的代码。 此外,一般情况下,您正在清理一些在异常情况下会出现的资源问题,以及处理未正确处理的COM实现。
如果您说整个COM记录集作为二进制对象(字节数组)被持久化到数据库表中的单个列,那么我没有看到任何解决方案的复杂性。 在操作之前,必须将字节数组转换为序列化的相同具体对象(COM记录集)。
没有回答你的具体问题......但既然你在指定效率,我认为你想要速度。
您是否考虑过将其持久化两次,无论是作为ADO还是ADO.Net,请求者都可以检索最合适的并跳过运行时转换(假设读取次数多于写入次数)。 为了获得额外的提升,可以在内存中存储n个数据集,这些数据集可以立即返回,而不是从数据库重新加载。 同样,这只会根据您的具体数据和请求而有用。
代码看起来非常有效。 虽然我同意需要通用的COM处理例程(只是为了保持一致性,如果没有别的),我不知道你建议不要重用db连接的额外性能。
我唯一担心的是你正在使用ADO Stream。 这个特殊的物体具有像糖果店里的孩子一样吃记忆的小副作用。 我查看了这篇文章( http://www.vbrad.com/article.aspx?id=12 )并确保您的代码没有此问题。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.