Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/303.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在使用SqlBulkCopy的类上实现Dispose_C#_Sql Server_Sqlbulkcopy - Fatal编程技术网

C# 在使用SqlBulkCopy的类上实现Dispose

C# 在使用SqlBulkCopy的类上实现Dispose,c#,sql-server,sqlbulkcopy,C#,Sql Server,Sqlbulkcopy,我有一个记录API调用的c#网站,它将日志作为一行插入SQL Server数据库。当站点变得非常繁忙时,这就是关键点,日志表会锁定,我们会看到等待连接到数据库的超时。我试图通过使用SqlBulkCopy来解决这个问题。在一个新的BulkLogger类中,原始的log方法被替换为一个,当包含日志的数据表达到一个限制(目前为100)时,调用Flush()方法,并将所有日志写入数据库 原始的log方法是从所有代码中调用的,它是没有DI的旧代码,因此我必须在几个地方创建新的BulkLogger。我担心一

我有一个记录API调用的c#网站,它将日志作为一行插入SQL Server数据库。当站点变得非常繁忙时,这就是关键点,日志表会锁定,我们会看到等待连接到数据库的超时。我试图通过使用
SqlBulkCopy
来解决这个问题。在一个新的
BulkLogger
类中,原始的log方法被替换为一个,当包含日志的数据表达到一个限制(目前为100)时,调用
Flush()
方法,并将所有日志写入数据库

原始的log方法是从所有代码中调用的,它是没有DI的旧代码,因此我必须在几个地方创建
新的BulkLogger
。我担心一些日志不会被写入,因为异常处理得不好,
BulkLogger
可能会丢失,或者在页面请求完成时,如果一些日志仍在内存中等待进行批量复制

我的
BulkLogger
类可以被密封,我没有任何资源可以清除或处置。因此,如果
BulkLogger
实现IDisposable,并且我实现Dispose,如下所示,这是否意味着在大多数情况下,所有日志都会写入数据库

public sealed class BulkLogger : IDisposable
{
    DataSet _logSet;
    DataTable _logTable;

    public BulkLogger() {
        // set up the dataset and datatable 
    }

    public void Log(string message, DateTime logTime) {
        // add message and time to _logTable
        // if _logTable.count > 100 call Flush()
    }

    public void Flush() {
        // Use SqlBulkCopy to insert logs
    }

    public void Dispose()
    {
        Flush();
    }
}

使用类似于
SQLBulkCopy
,只允许您按照MSDN文档()向db/表插入大量数据。因此,采用这种方法很可能意味着您必须首先将要登录到DB的调用存储在内存中,一旦它们达到某个阈值,就将它们写入DB

我也不认为这是一个很好的方法,因为您很可能会看到所使用资源的增加

考虑创建一个单独的线程将日志行写入数据库。 不需要更改任何代码,只需从实例化自身连接实例的线程调用DB即可

Thread t1 = new Thread(()=>{
SQLConnection con = new SQLConnection("yourConnection");
SqlCommand command = new SqlCommand("INSERT INTO LOG TABLE", con );
command.ExecuteNonQuery();
});
t1.Start();

请参见如何从以下内容创建线程:

下面是关于如何实现DB日志记录的示例项目的另一个链接。

如果要在数据库中插入太多线程化的任务,那么可能需要缩小到数据库的直接管道。不要启动线程来执行插入,而是使用1个线程或一个线程池来使用请求来插入数据库。您可以使用
ConcurrentQueue
在主应用程序线程上排队(生产者)请求,而专用线程监视此队列并使用请求。通过这种方式,插入的处理是同步的,而不是完全免费的

以下是指向生产者/消费者示例的链接:


IDisposable不是魔法。有人需要调用它的
Dispose
(通常是使用
语句的
结果)。您可能需要做的是,让您的正常调用模式强制执行Dispose调用,并在N个日志调用或X个时间量之后刷新Dispose调用。添加一个调用刷新的未处理异常处理程序。但是让你的班级成为一次性的,仅仅是为了叫同花顺,对你没什么好处。有责任心的用户,一看到IDispossble,就会将他们的呼叫包装在一个使用中,击败您的design@Flydog57我正计划用语句把我的电话包装起来。阅读你的回复让我意识到我的整个方法行不通。大问题,但你能看到一个好的替代方案吗?如果你在using中包装代码,那么在退出using:using(SQLConnection con=new-SQLConnection(“yourConnection”){}时对象会自动释放。你可能会遇到异常,using可能不会显示异常。您是否在使用中添加了异常处理程序?如果使用中的代码正在调用其他方法,则错误可能发生在较低级别的方法中,并且没有正确报告。您是否检查了表是如何被锁定的,您使用的是什么事务隔离级别?日志表应具有最小的索引以优化写入。您是否考虑过内存优化表?您是否关闭了调用之间的连接(最佳实践)?代码目前的工作方式有点像这样。不幸的是,我们最终会有大量被阻塞的线程等待数据库表解锁,这会导致站点关闭。为什么您认为存在死锁?是否有更新或选择轰炸同一个表?@jonaglon请查看更新的答案,以获取指向演示如何实现DB日志记录的项目的链接。@tmckown我在编写“解锁”时可能使用了错误的语言。当我们尝试执行过多插入时,我们在错误日志中看到的是“执行超时已过期。超时时间在操作完成之前已过,或者服务器没有响应”我还没有得到执行这个的许可,但是如果我是这样的话,我会尝试使用这个模式来解决我的问题,所以我接受这个答案。谢谢