Реализация Dispose для класса, который использует SqlBulkCopy - PullRequest
2 голосов
/ 26 сентября 2019

У меня есть сайт ac #, который регистрирует вызовы API, он вставляет журнал как одну строку в базу данных SQL Server.Когда сайт становится действительно занятым, это крайняя точка, таблица журнала блокируется, и мы видим тайм-ауты, ожидающие соединения с базой данных.Я пытаюсь исправить это с помощью SqlBulkCopy.Исходный метод журнала заменяется одним в новом классе BulkLogger, и когда DataTable, содержащий журналы, достигает предела (на данный момент 100), вызывается метод Flush() и все журналы записываются в базу данных.

Исходный метод журнала вызывался из всего кода, это старый код без DI, поэтому мне пришлось создать new BulkLogger в нескольких местах.Я обеспокоен тем, что некоторые журналы не будут записываться, потому что исключения обрабатываются плохо, и BulkLogger может быть утерян, или просто, когда запрос страницы завершится, если некоторые журналы все еще находятся в памяти и ожидают, что их будет BulkCopied.

Мой 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();
    }
}

Ответы [ 2 ]

1 голос
/ 26 сентября 2019

Если вы перегружаете БД слишком большим количеством потоковых задач для вставки в БД, возможно, вам нужно сузить прямой конвейер к БД.Вместо того, чтобы запускать потоки для вставки, используйте 1 поток или пул потоков, использующих запросы на вставку в БД.Вы можете использовать ConcurrentQueue<T> для постановки в очередь (Producer) запроса в главном потоке приложения, в то время как выделенный поток (ы) отслеживает эту очередь и потребляет запросы.Таким образом, вставки обрабатываются синхронно, а не бесплатно для всех.

Вот ссылка на пример производителя / потребителя:

https://docs.microsoft.com/en-us/dotnet/standard/collections/thread-safe/blockingcollection-overview?redirectedfrom=MSDN

C # производитель / потребитель

0 голосов
/ 26 сентября 2019

Используя что-то вроде SQLBulkCopy, вы просто можете вставить большую нагрузку данных в базу данных / таблицу в соответствии с документацией MSDN (https://docs.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlbulkcopy?view=netframework-4.8).). Таким образом, такой подход, скорее всего, будет означать, что вы сначала должны сохранитьВаши звонки, которые вы хотите записать в БД в памяти и как только они достигнут определенного порога, записывают их в БД.

Я также не думаю, что это отличный подход, поскольку вы, скорее всего, увидите увеличениеиспользуемые ресурсы.

Рассмотрите возможность создания отдельного потока для записи строк журнала в БД. Не нужно менять код, просто сделайте вызов из потока, который создает свой экземпляр подключения к БД.

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

Посмотрите, как создать поток из: https://docs.microsoft.com/en-us/dotnet/api/system.threading.thread.start?view=netframework-4.8

Вот еще одна ссылка с примером проекта, как реализовать ведение журнала БД. https://code.msdn.microsoft.com/How-to-implement-logging-4cbcfc64

...