2011-12-14 2 views
9

Как очистить SQL Server, чтобы избавиться от истекших объектов SqlDependency? После того, как я получаю событие от объекта SqlDepedency, мне нужно создать новый, прежде чем я смогу получить новое событие. Тем не менее, использование SQL Server процессом памяти увеличивается, пока не закончится разрешенная память (SQL Server Express). Как мне избавиться от старых запросов?Как очистить SqlDependency от памяти SQL Server?

Код:

// Func: RegisterTableListener 
using (SqlConnection cn = new SqlConnection(Properties.Settings.Default.DatabseEventConnectionString)) 
{ 
if (cmd == null) 
{ 
    cmd = cn.CreateCommand(); 

    cmd.CommandType = CommandType.Text; 
    cmd.CommandText = "SELECT HostName, LastStatus, LastDetails, xml FROM dbo.[SystemTable]"; 
} 

lock (cmd) 
{ 
    cmd.Connection = cn; 
    cn.Open(); 
    cmd.Notification = null; 

    // creates a new dependency for the SqlCommand 
    if (dep == null) 
     dep = new SqlDependency(cmd); 
    // creates an event handler for the notification of data 
    //  changes in the database. 
    dep.OnChange += new OnChangeEventHandler(dependency_OnChange); 


    using (SqlDataReader reader = cmd.ExecuteReader()) 
    { 
    // code here to read 
    } 
} 
} 

// Func dependency_OnChange 
//SqlDependency dep = sender as SqlDependency; 
dep.OnChange -= dependency_OnChange; 
RegisterTableListener(); 
+0

Как вы создаете объекты 'SqlDependency'? Отправьте свой код. Вы распоряжаетесь ими должным образом? – Oded

+0

Я обновляю свой комментарий с кодом, когда буду работать завтра. Sudo: SqlDependency dep = new SqlDependency (cmd); dep.OnChange + = забава; SqlDependency не реализует IDisposable – JeremyK

+0

Я обновил код. Даже когда я запускаю только один экземпляр SqlDepdency и каждый раз нажимаю Stop и Start, память поднимается. Я не знаю, что происходит. – JeremyK

ответ

12

Существует определенное поведение класса Microsoft SqlDependency. Даже если вы вызываете метод SqlDependency.Stop(), выпускайте SqlCommand и SqlConnection - он по-прежнему сохраняет группы разговора (sys.conversation_groups) и конечные точки беседы (sys.conversation_endpoints) в базе данных. Похоже, что SQL Server загружает конечную точку разговора и использует всю разрешенную память. Here тесты, подтверждающие это. Таким образом, чтобы очистить все неиспользуемые разговорные конечные точки и освободить всю занятую память, вы должны начать эту SQL-код для вашей базы данных:

DECLARE @ConvHandle uniqueidentifier 
DECLARE Conv CURSOR FOR 
SELECT CEP.conversation_handle FROM sys.conversation_endpoints CEP 
WHERE CEP.state = 'DI' or CEP.state = 'CD' 
OPEN Conv; 
FETCH NEXT FROM Conv INTO @ConvHandle; 
WHILE (@@FETCH_STATUS = 0) BEGIN 
    END CONVERSATION @ConvHandle WITH CLEANUP; 
    FETCH NEXT FROM Conv INTO @ConvHandle; 
END 
CLOSE Conv; 
DEALLOCATE Conv; 

Кроме того, SqlDependency не дает вам возможность получить все изменения таблицы. Таким образом, вы не получаете уведомления об изменениях во время повторной подписки SqlDependency.

Чтобы избежать всех этих проблем, я использовал другую реализацию с открытым исходным кодом класса SqlDependency - SqlDependencyEx. Он использует триггер базы данных и собственное уведомление Service Broker для получения событий об изменениях таблицы. Это пример использования:

int changesReceived = 0; 
using (SqlDependencyEx sqlDependency = new SqlDependencyEx(
      TEST_CONNECTION_STRING, TEST_DATABASE_NAME, TEST_TABLE_NAME)) 
{ 
    sqlDependency.TableChanged += (o, e) => changesReceived++; 
    sqlDependency.Start(); 

    // Make table changes. 
    MakeTableInsertDeleteChanges(changesCount); 

    // Wait a little bit to receive all changes. 
    Thread.Sleep(1000); 
} 

Assert.AreEqual(changesCount, changesReceived); 

Надеюсь, что это поможет.

+1

В чем смысл этой строки WHERE CEP.state = 'DI' или CEP.state = 'CD'. что вы пытаетесь сделать. пожалуйста, помогите мне понять. спасибо – Mou

+0

@Mou 'DI' означает« DisconnectedInbound »,' CD' означает «Closed». Конечные точки бесед с этими метками не имеют времени жизни, заданного 'SqlDependency'. Это означает, что они будут находиться в базе данных, пока вы их не очистите. Согласно статье http://rusanu.com/2014/03/31/how-to-prevent-conversation-endpoint-leaks/ (в конце) это правильный способ очистки старых конечных точек разговора. – dyatchenko

+0

это означает, что если я использую этот sql 'WHERE CEP.state = 'DI' или CEP.state = 'CD'', тогда старый разговор удалит? – Mou

0

Я столкнулся точно с такой же проблемой. Я создаю компонент доступа к данным, который кэширует некоторые запросы из базы данных SQL Server 2005. Кэш недействителен, используя этот новый, но не новый, но не новый, подход SqlDependency.

Поскольку этот компонент будет использоваться в ASP.NET, а также в приложениях Forms и Windows Service, я ищу общий способ (внутри) вызова SqlDependency.Stop().

Использование финализатора было моей первой идеей, и это не сработало. Моя вторая попытка заключалась в использовании обработчика событий для AppDomain.DomainUnload.

В конце концов, это похоже на работу ... Но встроенный веб-сервер в VS 2005 будет висеть в течение 4-5 минут со 100% процессором, выполняя SqlDependy.Stop(). На самом деле, я не могу вспомнить о каком-либо другом процессе, блокирующем мою машину (ноутбук Pentium M), настолько воспроизводимой, что я вряд ли мог вызвать диспетчер задач ... Я не ожидал, что это возможно из пользовательского пространства и даже управляемого кода (SQL Server работает в другом окне.) В течение этого времени даже Performance Monitor отказывается записывать что-либо, поэтому я не могу сказать, существует ли много ручек Windows или .NET-исключений или что-то еще ...

Вызов его из события Application_End работает отлично (и занимает всего несколько миллисекунд), однако это специфично для ASP.NET.

Любые идеи

+0

В итоге я отказался от использования сервисного брокера. Я не мог понять, что вызывает утечку. – JeremyK

Смежные вопросы