Giter Club home page Giter Club logo

servicebrokerlistener's People

Contributors

dmigo avatar dyatchenko avatar hamidrazeghi avatar nullcosmos avatar predatorfromekb avatar siliconrob avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

servicebrokerlistener's Issues

deployment may fail but broker actually works.

i had no issues with the code on my local computer but the following exception was thrown on the production server: Current user doesn''t have CONTROL SERVER permission to enable service broker. Grant sufficient permissions to current user or execute ALTER DATABASE [] SET ENABLE_BROKER with admin rights.
sql server installation adds by default CONTROL SERVER permissions to user installing the database service, but this is actually not required to enable the broker service, ENABLE_BROKER will run just fine without CONTROL SERVER permissions on some configurations. so i had to do this...

private const string SQL_FORMAT_INSTALL_SEVICE_BROKER_NOTIFICATION = @ -- Setup Service Broker IF EXISTS (SELECT * FROM sys.databases WHERE name = {0} AND (is_broker_enabled = 0 )) BEGIN ALTER DATABASE [{0}] SET SINGLE_USER WITH ROLLBACK IMMEDIATE ALTER DATABASE [{0}] SET ENABLE_BROKER; ALTER DATABASE [{0}] SET MULTI_USER WITH ROLLBACK IMMEDIATE END -- Create a queue which will hold the tracked information IF NOT EXISTS (SELECT * FROM sys.service_queues WHERE name = {1}) CREATE QUEUE {3}.[{1}] -- Create a service on which tracked information will be sent IF NOT EXISTS(SELECT * FROM sys.services WHERE name = {2}) CREATE SERVICE [{2}] ON QUEUE {3}.[{1}] ([DEFAULT]) ";

Problem getting notifications from changes not directly done on SQL Server Studio

I have implemented the SqlDependencyEx class in windows service on windows 10. It works fine, as long as I make those changes in SQL Server Studio. When I do the changes with a program, on the same database I get no notification.
The Service is not on the same node as the database.
As the user in the service and the Program making the changes are different it might be a problem with access rights.

Any hints?

No events raised when DB is on different physical / logical server

SQLDependency and Service broker works perfect in an architecture that has all the application tiers hosted on the same server, but presents a problem once the architecture is spread across multiple servers.

I have implemented both the standard .NET SqlDependency and the service broker listener (I prefer the later) and they both work perfectly when the connection string is pointing to the same server that the client (subscriber/consumer) is running on, but as soon as the connection string points to a different server no notification callbacks gets raised.

This may not be a very common problem but affects applications that have to be scaled out or that need the different tiers to be logically segregated due to standards or policies .i.e. PCI.

My last resort would be to help the service broker cross tiers by having the subscriber sitting in the same server as the DB then connecting to the intended subscriber through a keep alive connection of some sort .i.e a socket connection. But before going through this direction, I need to know if anyone has been successful at having the database running at server @server1 and the service broker running @server2 and events still being able to be received?

Error on Installation script procedure (tr_Listener_1 failed)

Hi,

I have a little problem when I try to start the listener on SQL Server 2012, when i try to execute [sp_InstallListenerNotification_1]

I have this error :

Msg 156, Niveau 15, État 1, Procédure tr_Listener_1, Ligne 21
Syntaxe incorrecte vers le mot clé 'Not'.

You can try :

USE [MyDatabase]
GO

DECLARE @return_value int

EXEC    @return_value = [dbo].[sp_InstallListenerNotification_1]

SELECT  'Return Value' = @return_value

GO

DEBUG :

private void InstallNotification()
        {
            string execInstallationProcedureScript = string.Format(
                SQL_FORMAT_EXECUTE_PROCEDURE,
                this.DatabaseName,
                this.InstallListenerProcedureName,
                this.SchemaName);
            ExecuteNonQuery(GetInstallNotificationProcedureScript(), this.ConnectionString); **#OK**
            ExecuteNonQuery(GetUninstallNotificationProcedureScript(), this.ConnectionString); **#OK**
            ExecuteNonQuery(execInstallationProcedureScript, this.ConnectionString); **#FAILED**
        }

Are you an idea ?
Adw.

Require the functionality to be used with webpage (optionally with SignalR)

Hi,

I am looking forward to implement this cool SQLDependencyEx module in one of my project.
I created the POC with the sample code and it works (I mean debug the values are returned correctly -event fires every time) fine-but one thing, I can not write the received values on aspx response in a DIV or with the plain text (I guess because the DOM would already be built). Can I use your code with AJAX/SignalR for some real-time monitoring capabilities?

I might need the sample code to do that. OR
OR
you can suggest the remedy for the below code:

public partial class Default : System.Web.UI.Page, IDisposable
{
    SqlDependencyEx sqlDependency;

    public Default()
    {
        string DBConn = ConfigurationManager.ConnectionStrings["DBConn"].ToString();
        sqlDependency = new SqlDependencyEx(DBConn, "TestDB", "tbl_MessageDetail", "dbo");
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        RegisterNotification();
        /*outputResult is a DIV - this is shown*/
        outputResult.InnerHtml += ("Registered Notifier: " + DateTime.Now.ToShortTimeString() +  "<br/>");
    }

    private void RegisterNotification()
    {
        sqlDependency.Stop();
        sqlDependency.TableChanged += OnDataChange;
        Thread.Sleep(2000);
        sqlDependency.Start();
        /*outputResult is a DIV - this is shown*/
        outputResult.InnerHtml += ("SQL Dependency Started: " + DateTime.Now.ToShortTimeString() +  "<br/>");
    }

    private void UnregisterNotification()
    {
        sqlDependency.Stop();
        sqlDependency.TableChanged -= OnDataChange;
    }

    private void OnDataChange(object sender, SqlDependencyEx.TableChangedEventArgs e)
    {
        string result = e.Data.FirstNode.ToString();
        /*outputResult is a DIV - where the result is NOT showing on change*/
        outputResult.InnerHtml += result + "<br/>";
    }

    public override void Dispose()
    {
        // Clean up the resources!
        UnregisterNotification();
    }
}

Please suggest the work-around

ObjectDisposedException while calling Stop()

Hi,

I'm using the following code to react to the NotificationProcessStopped event:

// Listener contains the SqlDependencyEx instance
Listener.NotificationProcessStopped += (sender, args) =>
{
   Task.Run(async () =>
   {
      await Task.Delay(5000);
      AttemptReconnect();
   });
};

private void AttemptReconnect()
{
   try
   {
      Listener.Stop();
      Listener.Start();
   }
   catch
   {
      Task.Run(async () =>
      {
         await Task.Delay(5000);
         AttemptReconnect();
      });
   }
}

If I run this code and stop the SQL Server to simulate a disconnect, I am receiving an ObjectDisposedException: The CancellationTokenSource has been disposed in the Stop() method when the _threadSource.Token.IsCancellationRequested property is read in line 567 on the second reconnect attempt (and every reconnect attempt thereafter).

When I change this to _threadSource.IsCancellationRequested everything works fine.

Dynamically create listeners

Hello,

Would appreciate any guidance on this.

My scenario:
I have a lot of databases, each one it's a exact replica but for a different customer. Each DB can be on multiple servers. I can have more than one instance of my webapp.

Currently I have a job, that finds all my databases, servers, and connection strings and runs parallel certain tasks every 2 minutes in all DBs. I would like to change it to 30 min to reduce stress in some of the DBs and then add Service Broker Listener that run those tasks on only on those inserts.

Whats the best approach here? D:

Thanks

Sql permissions

I noticed the sql permissions used to setup the listener are a little too broad. I would recommend following the answer provided here. I have verified that these permissions do in fact work with sqldependency so I assume they work with your code as well.

Starting two listener in same app, Second one is not working

I have got triggers, service, queues, storedprocedures added as it has to be, but second trigger not firing,
iam using latest code. thanks

static void Main(string[] args)
{
try
{

            SqlDependencyExNew sqlDependency = new SqlDependencyExNew(
               connectionString: conx, databaseName: "db", tableName: "table", listenerType: NotificationTypes.Update, identity: 1);
            sqlDependency.TableChanged += (o, e) => OnChange(o, e);


            SqlDependencyExNew sqlDependency2 = new SqlDependencyExNew(
               connectionString: conx, databaseName: "db", tableName: "table", listenerType: NotificationTypes.Update, identity: 2);
            sqlDependency.TableChanged += (o, e) => OnChangeA(o, e);


            sqlDependency.Start();
            sqlDependency2.Start();
            Console.WriteLine("______________Escuchando_______________");

            Console.ReadKey();
            sqlDependency.Stop();
            sqlDependency2.Stop();
        }
        catch (Exception exp)
        {
            if (listener.Active == true)
            {
                listener.Stop();
            }
            if (listener2.Active == true)
            {
                listener2.Stop();
            }
            throw;
        }
    }

    private static async void OnChangeA(object o, SqlDependencyExNew.TableChangedEventArgs e)
    {
        await SOMETHING(o, e);
    }

    private static Task<XElement> SOMETHING(object o, TableChangedEventArgs e)
    {
        var xxx = e.Data;
        return Task.Run(() =>
        {
            return xxx;
        });
    }

    private static async void OnChange(object o, SqlDependencyExNew.TableChangedEventArgs e)
    {
        await SOMETHING(o, e);
    }

}

User does not have permission to perform this action

I have an exception when the code try to execute this :

"\r\n                USE [<database>]\r\n                IF OBJECT_ID ('dbo.sp_InstallListenerNotification_1', 'P') IS NOT NULL\r\n                    EXEC dbo.sp_InstallListenerNotification_1\r\n            "
System.Data.SqlClient.SqlException was unhandled
  Class=16
  ErrorCode=-2146232060
  HResult=-2146232060
  LineNumber=29
  Message=User does not have permission to perform this action.
Changed database context to '<database>'.
  Number=15247
  Procedure=sp_InstallListenerNotification_1
  Server=<serverip>
  Source=.Net SqlClient Data Provider
  State=1
  StackTrace:
       à System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
       à System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
       à System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
       à System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
       à System.Data.SqlClient.SqlCommand.RunExecuteNonQueryTds(String methodName, Boolean async, Int32 timeout, Boolean asyncWrite)
       à System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource`1 completion, String methodName, Boolean sendToPipe, Int32 timeout, Boolean asyncWrite)
       à System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
       à ServiceBrokerListener.Domain.SqlDependencyEx.ExecuteNonQuery(String commandText, String connectionString) dans C:\Users\bbigras\Documents\Visual Studio 2015\Projects\WindowsFormsApplication1\WindowsFormsApplication1\SqlDependencyEx.cs:ligne 632
       à ServiceBrokerListener.Domain.SqlDependencyEx.InstallNotification() dans C:\Users\bbigras\Documents\Visual Studio 2015\Projects\WindowsFormsApplication1\WindowsFormsApplication1\SqlDependencyEx.cs:ligne 751
       à ServiceBrokerListener.Domain.SqlDependencyEx.Start() dans C:\Users\bbigras\Documents\Visual Studio 2015\Projects\WindowsFormsApplication1\WindowsFormsApplication1\SqlDependencyEx.cs:ligne 532
       à WindowsFormsApplication1.YourClass.RegisterNotification() dans C:\Users\bbigras\Documents\Visual Studio 2015\Projects\WindowsFormsApplication1\WindowsFormsApplication1\YouClass.cs:ligne 33
       à WindowsFormsApplication1.YourClass.Go() dans C:\Users\bbigras\Documents\Visual Studio 2015\Projects\WindowsFormsApplication1\WindowsFormsApplication1\YouClass.cs:ligne 27
       à WindowsFormsApplication1.Form1..ctor() dans C:\Users\bbigras\Documents\Visual Studio 2015\Projects\WindowsFormsApplication1\WindowsFormsApplication1\Form1.cs:ligne 85
       à WindowsFormsApplication1.Program.Main() dans C:\Users\bbigras\Documents\Visual Studio 2015\Projects\WindowsFormsApplication1\WindowsFormsApplication1\Program.cs:ligne 28
       à System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
       à System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
       à Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       à System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       à System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
       à System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
       à System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       à System.Threading.ThreadHelper.ThreadStart()
  InnerException: 

I did run the GRANTs I saw in the code :

GRANT CREATE PROCEDURE TO <user>;
GRANT CREATE SERVICE TO <user>;
GRANT CREATE QUEUE  TO <user>;
GRANT REFERENCES ON CONTRACT::[DEFAULT] TO <user>;
GRANT SUBSCRIBE QUERY NOTIFICATIONS TO <user>;
GRANT CONTROL ON SCHEMA::[dbo] TO <user>;
GRANT EXEC ON SCHEMA::[dbo] TO <user>;

Start/Stop and then Start again?

I am trying to use this in an "off-label" application to notify my application when new rows are written so I can process the rows. During the processing more rows may be inserted, and I am not clear how to prevent those events from firing.
I can call Stop() and then call Start() although I did try that and it does not seem to re-start notifications. So I think I'm not using it correctly, but I'm not sure what the correct way to handle this is.

How to use your library

i am interested to know how to use your library instead of MS provided. it would be nice for all if you come with a tutorial like how to use this in any application and where it is different from built-in sql dependency.

thanks

SqlDependencyEx.Stop method causes problems in load balanced environment

Is it possible for a flag to be passed into the Stop method that is used to control the UninstallNotification logic? The reason being is that, in a clustered environment, if Stop is invoked from Server 1 then the SQL listeners are dropped and this causes Server 2 to start failing. Ideally it would be possible to control whether the objects get dropped.

Insert,Update and Delete not firing up change event

Hello,

I am trying to use and library and it seems unless I have separate listeners for each event (Insert, Update and Delete) it does not fire up the change event. In the below sample code I have 2 listeners one for Insert and one for update this fires up but if I have only one listener only the Insert event fires up the e update and delete does not fire up any event. I am inserting the records directly using SSMS

        string connectionString = ConfigurationManager.ConnectionStrings["EmployeeEntityModel"].ConnectionString;
       


        // See constructor optional parameters to configure it according to your needs
        var listener = new SqlDependencyEx(connectionString, "SignalRDemo", "Employee", listenerType: SqlDependencyEx.NotificationTypes.Update, receiveDetails: true, identity: 1);

        // e.Data contains actual changed data in the XML format
        listener.TableChanged += Listener_TableChanged;

        // After you call the Start method you will receive table notifications with 
        // the actual changed data in the XML format
        listener.Start();


        var listener1 = new SqlDependencyEx(connectionString, "SignalRDemo", "Employee", listenerType: SqlDependencyEx.NotificationTypes.Insert, receiveDetails: true, identity: 2);

        // e.Data contains actual changed data in the XML format
        listener1.TableChanged += Listener1_TableChanged;

        // After you call the Start method you will receive table notifications with 
        // the actual changed data in the XML format
        listener1.Start();
       
    }

    private static void Listener1_TableChanged(object sender, SqlDependencyEx.TableChangedEventArgs e)
    {
        throw new NotImplementedException();
    }

    private static void Listener_TableChanged(object sender, SqlDependencyEx.TableChangedEventArgs e)
    {
        throw new NotImplementedException();
    }

Problems with unit tests

Hi,

I have a couple of issues here, some of which may be my own stupidity. I'm aware that the whole purpose of the library is to replicate SqlDependency callback in non SQL 2005 versions, but is there anything that would prevent your SqlDependencyEx class from working on a SQL 2005 database?

When running against SQL 2005 I receive the following error with the unit tests:

System.Data.SqlClient.SqlException: Cannot drop the service 'ListenerService_1', because it does not exist or you do not have permission.
Changed database context to 'TestDatabase'.
Errors: System.Data.SqlClient.SqlErrorCollection

This appears to be triggered by this statement:

USE [TestDatabase]
            IF OBJECT_ID ('temp.sp_UninstallListenerNotification_1', 'P') IS NOT NULL
                EXEC temp.sp_UninstallListenerNotification_1

If I change my connection string to use SQL 2012 then this issue goes away but the unit test doesn't pass because the expected table changes don't seem to materialize.

I have looked at SqlDependencyTest and SqlDependencyExTest but the former I believe is for the legacy native implementation of Sql Dependency callbacks and the latter cannot be run from within Visual Studio because it's configured to use NUnit.

Forgive the lengthy post, but here is the test case I'm using (which is mostly the same as what is in your project):

    [TestFixture]
    public class SqlDependencyExTest
    {
        private const string MASTER_CONNECTION_STRING =
            "Data Source=localhost\\sql2005;Initial Catalog=master;Integrated Security=True";

        private const string TEST_CONNECTION_STRING =
            "Data Source=localhost\\sql2005;Initial Catalog=TestDatabase;User Id=TempLogin;Password=8fdKJl3$nlNv3049jsKK;";

        private const string ADMIN_TEST_CONNECTION_STRING =
            "Data Source=localhost\\sql2005;Initial Catalog=TestDatabase;Integrated Security=True";

        private const string INSERT_FORMAT =
            "USE [TestDatabase] INSERT INTO temp.[TestTable] (TestField) VALUES({0})";

        private const string REMOVE_FORMAT =
            "USE [TestDatabase] DELETE FROM temp.[TestTable] WHERE TestField = {0}";

        private const string TEST_DATABASE_NAME = "TestDatabase";

        private const string TEST_TABLE_NAME = "TestTable";

        private const string TEST_TABLE_1_FULL_NAME = "temp.TestTable";

        private const string TEST_TABLE_2_FULL_NAME = "temp2.TestTable";

        [SetUp]
        public void TestSetup()
        {
            const string CreateDatabaseScript = @"
                CREATE DATABASE TestDatabase;

                ALTER DATABASE [TestDatabase] SET SINGLE_USER WITH ROLLBACK IMMEDIATE
                ALTER DATABASE [TestDatabase] SET ENABLE_BROKER; 
                ALTER DATABASE [TestDatabase] SET MULTI_USER WITH ROLLBACK IMMEDIATE

                -- FOR SQL Express
                ALTER AUTHORIZATION ON DATABASE::[TestDatabase] TO [sa]
                ALTER DATABASE [TestDatabase] SET TRUSTWORTHY ON; ";
            const string CreateUserScript = @"
                CREATE LOGIN TempLogin 
                WITH PASSWORD = '8fdKJl3$nlNv3049jsKK', DEFAULT_DATABASE=TestDatabase;

                USE [TestDatabase];
                CREATE USER TempUser FOR LOGIN TempLogin;

                GRANT CREATE PROCEDURE TO [TempUser];
                GRANT CREATE SERVICE TO [TempUser];
                GRANT CREATE QUEUE  TO [TempUser];
                GRANT REFERENCES ON CONTRACT::[DEFAULT] TO [TempUser]
                GRANT SUBSCRIBE QUERY NOTIFICATIONS TO [TempUser];
                GRANT CONTROL ON SCHEMA::[temp] TO [TempUser]
                GRANT CONTROL ON SCHEMA::[temp2] TO [TempUser];  
                ";
            const string CreateTable1Script = @"                
                CREATE SCHEMA Temp
                    CREATE TABLE TestTable (TestField int, StrField NVARCHAR(MAX));
                ";
            const string CreateTable2Script = @"
                CREATE SCHEMA Temp2
                    CREATE TABLE TestTable (TestField int, StrField NVARCHAR(MAX));";

            TestCleanup();

            ExecuteNonQuery(CreateDatabaseScript, MASTER_CONNECTION_STRING);
            ExecuteNonQuery(CreateTable1Script, ADMIN_TEST_CONNECTION_STRING);
            ExecuteNonQuery(CreateTable2Script, ADMIN_TEST_CONNECTION_STRING);
            ExecuteNonQuery(CreateUserScript, MASTER_CONNECTION_STRING);
        }

        [TearDown]
        public void TestCleanup()
        {
            const string DropTestDatabaseScript = @"
                IF (EXISTS(select * from sys.databases where name='TestDatabase'))
                BEGIN
                    ALTER DATABASE [TestDatabase] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
                    DROP DATABASE [TestDatabase]
                END
                IF (EXISTS(select * from master.dbo.syslogins where name = 'TempLogin'))
                BEGIN
                    DECLARE @loginNameToDrop sysname
                    SET @loginNameToDrop = 'TempLogin';

                    DECLARE sessionsToKill CURSOR FAST_FORWARD FOR
                        SELECT session_id
                        FROM sys.dm_exec_sessions
                        WHERE login_name = @loginNameToDrop
                    OPEN sessionsToKill

                    DECLARE @sessionId INT
                    DECLARE @statement NVARCHAR(200)

                    FETCH NEXT FROM sessionsToKill INTO @sessionId

                    WHILE @@FETCH_STATUS = 0
                    BEGIN
                        PRINT 'Killing session ' + CAST(@sessionId AS NVARCHAR(20)) + ' for login ' + @loginNameToDrop

                        SET @statement = 'KILL ' + CAST(@sessionId AS NVARCHAR(20))
                        EXEC sp_executesql @statement

                        FETCH NEXT FROM sessionsToKill INTO @sessionId
                    END

                    CLOSE sessionsToKill
                    DEALLOCATE sessionsToKill

                    PRINT 'Dropping login ' + @loginNameToDrop
                    SET @statement = 'DROP LOGIN [' + @loginNameToDrop + ']'
                    EXEC sp_executesql @statement
                END
                ";
            ExecuteNonQuery(DropTestDatabaseScript, MASTER_CONNECTION_STRING);
        }

        [Test]
        public void NotificationTestWith10ChangesAnd60SecDelay()
        {
            NotificationTest(10, 60);
        }

        [Test]
        public void NotificationTestWith10Changes()
        {
            NotificationTest(10);
        }

        private void NotificationTest(
            int changesCount,
            int changesDelayInSec = 0,
            string connStr = TEST_CONNECTION_STRING)
        {
            int changesReceived = 0;

            using (SqlDependencyEx sqlDependency = new SqlDependencyEx(
                        connStr,
                        TEST_DATABASE_NAME,
                        TEST_TABLE_NAME, "temp")) 
            {
                sqlDependency.TableChanged += (o, e) => changesReceived++;
                sqlDependency.Start();

                Thread.Sleep(changesDelayInSec * 1000);
                MakeTableInsertDeleteChanges(changesCount);

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

            Assert.AreEqual(changesCount, changesReceived);
        }

        private static void MakeTableInsertDeleteChanges(int changesCount)
        {
            for (int i = 0; i < changesCount / 2; i++)
            {
                ExecuteNonQuery(string.Format(INSERT_FORMAT, i), MASTER_CONNECTION_STRING);
                ExecuteNonQuery(string.Format(REMOVE_FORMAT, i), MASTER_CONNECTION_STRING);
            }
        }

        private static void ExecuteNonQuery(string commandText, string connectionString)
        {
            using (SqlConnection conn = new SqlConnection(connectionString))
            using (SqlCommand command = new SqlCommand(commandText, conn))
            {
                conn.Open();
                command.CommandType = CommandType.Text;
                command.CommandTimeout = 60000;
                command.ExecuteNonQuery();
            }
        }

    }

Does Identity need to be unique to database?

If in a client i use SqlDependencyEx to monitor multiple tables, and then have multiple instances of that client on different computers using the same database - do i need to make sure that the Identity parameter of the SqlDependencyEx constructor is unique in the database context, or just for application instance?

I.e. Is it a problem if two programs track changes in the same database, and use the same Identity numbers?

Permission error for drop ListenerService_1

The errors I received when running the program include instruction telling me to run this script to grant the needed permissions:

GRANT CREATE PROCEDURE TO [MyUser];
GRANT CREATE SERVICE TO [MyUser];
GRANT CREATE QUEUE TO [MyUser];
GRANT REFERENCES ON CONTRACT::[DEFAULT] TO [MyUser];
GRANT SUBSCRIBE QUERY NOTIFICATIONS TO [MyUser];
GRANT CONTROL ON SCHEMA::[dbo] TO [MyUser];

This script has been run and the permissions have been granted but I’m still getting an error when I run the program:

System.Data.SqlClient.SqlException
HResult=0x80131904
Message=Cannot drop the service 'ListenerService_1', because it does not exist or you do not have permission.

It would be great if you can help on this.

Regards.

table with text, ntext or image datatypes

Hi,
when you try to trigger a table with text, ntext or image datatype, you get an exception, because it is not possible to trigger this datatypes.

Solution is, to change this const to:
( WHERE DATA_TYPE NOT IN (''text'',''ntext'',''image'') AND ...)

private const string SQL_FORMAT_CREATE_INSTALLATION_PROCEDURE = @"
                USE [{0}]
                " + SQL_PERMISSIONS_INFO + @"
                IF OBJECT_ID ('{6}.{1}', 'P') IS NULL
                BEGIN
                    EXEC ('
                        CREATE PROCEDURE {6}.{1}
                        AS
                        BEGIN
                            -- Service Broker configuration statement.
                            {2}
                            -- Notification Trigger check statement.
                            {4}
                            -- Notification Trigger configuration statement.
                            DECLARE @triggerStatement NVARCHAR(MAX)
                            DECLARE @select NVARCHAR(MAX)
                            DECLARE @sqlInserted NVARCHAR(MAX)
                            DECLARE @sqlDeleted NVARCHAR(MAX)

                            SET @triggerStatement = N''{3}''

                            SET @select = STUFF((SELECT '','' + COLUMN_NAME
                                                 FROM INFORMATION_SCHEMA.COLUMNS
                                                 WHERE DATA_TYPE NOT IN  (''text'',''ntext'',''image'') AND TABLE_NAME = ''{5}'' AND TABLE_CATALOG = ''{0}''
                                                 FOR XML PATH ('''')
                                                 ), 1, 1, '''')
                            SET @sqlInserted = 
                                N''SET @retvalOUT = (SELECT '' + @select + N'' 
                                                     FROM INSERTED 
                                                     FOR XML PATH(''''row''''), ROOT (''''inserted''''))''
                            SET @sqlDeleted = 
                                N''SET @retvalOUT = (SELECT '' + @select + N'' 
                                                     FROM DELETED 
                                                     FOR XML PATH(''''row''''), ROOT (''''deleted''''))''                            
                            SET @triggerStatement = REPLACE(@triggerStatement
                                                     , ''%inserted_select_statement%'', @sqlInserted)
                            SET @triggerStatement = REPLACE(@triggerStatement
                                                     , ''%deleted_select_statement%'', @sqlDeleted)
                            EXEC sp_executeSql @triggerStatement
                        END
                        ')
                END
            ";

Sqldependencyex not working after datatable update

Hi ,
I am using signalr to push notifications to user .Whenever any changes in table happens i am pushing notifications to frontend.But the sqldependencyex is not firing the onchange event.Please let me know if i made any mistakes.

        using (SqlDependencyEx sqlDependency =  new SqlDependencyEx(ConfigurationManager.ConnectionStrings["dbConnectionstring"].ConnectionString, "EIS_BMT", "EIS_Notifications"))
        {


            sqlDependency.TableChanged += dependency_OnChange;
            // Make table changes.
            sqlDependency.Start();

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


            IHubContext context = GlobalHost.ConnectionManager.GetHubContext<NotificationHub>();
            return context.Clients.All.RecieveNotification(totalNewMessages, Messages);
        }

private void dependency_OnChange(object sender, SqlDependencyEx.TableChangedEventArgs e)
{
NotificationHub nhub = new NotificationHub();
nhub.SendNotifications();
}

SqlDependencyEx event inside Controller not getting fired

Hi Devs,
I´m trying to use SqlDependencyEx the problem is that the OnDataChange event is not getting fired inside the controller, I already enabled Service Broker. Here´s is the code:

`

private const string CONNECTION_STRING  = "Server=LFTCMCPTP83;Database=Database;Trusted_Connection=True;MultipleActiveResultSets=true;  Integrated Security=false;User ID=used_id;Password=password";
private const string DATABASE_NAME = "db_name";

private const string TABLE_NAME = "table_name";

private const string SCHEMA_NAME = "dbo";

private SqlDependencyEx sqlDependency = new SqlDependencyEx(CONNECTION_STRING, DATABASE_NAME,
    TABLE_NAME, SCHEMA_NAME);

private void RegisterNotification()
{
    sqlDependency.TableChanged += OnDataChange;
    sqlDependency.Start();
}

private void OnDataChange(object sender, SqlDependencyEx.TableChangedEventArgs e)
{
    //Code
}

public IActionResult Create(){
     RegisterNotification();
}`

Best Regards

ActiveEntities list memory leak into concurrent multithread context

Hi @dyatchenko

First of all this library is great !! :)

I am to use your library in a multithreaded environment. During performance optimization I came across a problem that seems to belong to the type of memory leaks. Using the diagnostic tool of visual studio I am to have this situation (after causing an intense dependency from sql server db):

image

in the picture the application is in waiting mode. As you can see there is a reference to the List of ActiveEntities.

Inserting using ConcurrentDictionary(.net 4.0) it appears that the behavior has improved and the reference is no longer present.

This is the list of the changes I've made to the code:

  1. (line 437)
    from
private static readonly List<int> ActiveEntities = new List<int>(); 

to

private static readonly ConcurrentDictionary<int, byte> ActiveEntities = new ConcurrentDictionary<int, byte>();
  1. (line 525)
    from
lock (ActiveEntities)
            {
                if (ActiveEntities.Contains(this.Identity))
                    throw new InvalidOperationException("An object with the same identity has already been started.");
                ActiveEntities.Add(this.Identity);
            }

to

ActiveEntities.TryAdd(this.Identity, byte.MinValue);
  1. (line 570)
    from
lock (ActiveEntities)
                if (ActiveEntities.Contains(Identity)) ActiveEntities.Remove(Identity);

to

byte itemRemoved;
            ActiveEntities.TryRemove(Identity, out itemRemoved);

that's all

How to detect changes ?

suppose some new records has been added to customer table or existing few changes then how could get those rows with more information like is it added or update?

share some code snippet to achieve it. thanks

Working with Azure WebJobs with Hybrid SQL Connection

I have been using SqlDependencyEx with a console App successfully. I get the notifications, the actual data changes etc.

I have to move this executable to an Azure WebJob, and need to work with an on-prem SQL server database for now. Would SqlDependencyEx still work in this scenario?

Awesome project, thanks.

Swanand

How to use in ASP.NET MVC

Hello, your library is quit useful but I want to try to integrate it to an existing asp.net mvc application.
I want to call the start and stop methods in my Global.aspx file in the Application_Start() and Application_End() respectively. However, the anonymous function listener.TableChanged += (o, e) => Console.WriteLine("Your table was changed!") to be in another class which is not in the Global.aspx file.

Please can you elaborate with code snippets on how to do this? Additionally, can you direct me to a more elaborate documentation of your library?

Thank you.

Problem with case-sensitive mssql server (express)

Hi there. Great plugin so far :-) Works like a charm right now, thank you!

I just had some struggle because in our environment, for some reason the SQL Server is set to look for matching case in SQL statements. (more info here: http://stackoverflow.com/questions/3296428/changing-sql-server-collation-to-case-insensitive-from-case-sensitive - but please don't ask me why! i don't know either and for 99% of time i'm happy to let other people deal with the sql machine itself, but I needed the event broker mechanism ;) )

So now the "Issue" is a very minor one. But it has cost me a few hours to find out. So...:

You are using sp_executeSql in your SQL_FORMAT_CREATE_INSTALLATION_PROCEDURE which made the created procedure fail "inside the sql-server" here. So I'd suggest you just change it to sp_executesql which worked out to be the solution for me. I'd do it myself, but no time at the moment ;)

When not receiving details issue with XML format

Hello,

I had a problem turning off the XML details only on production server on the hosting service for which i do not have much control. I got a malformed XML exception with the default XML message:

SET @message = N'<root/>'

So the shorter valid message i could send on the trigger was this one:

SET @message = N'<root><inserted><row></row></inserted><deleted><row></row></deleted></root>'

Great component, very well done, thank you very much!!!

"Data?" that breaks the compiler in SqlDependencyEx.cs

Hi,

In SqlDependencyEx.cs at lines 53, 54, and 57 there is a strange notation "Data?" that breaks the compiler.

return Data?.Element(INSERTED_TAG) != null
? Data?.Element(DELETED_TAG) != null
? NotificationTypes.Update
: NotificationTypes.Insert
: Data?.Element(DELETED_TAG) != null
? NotificationTypes.Delete
: NotificationTypes.None;

I guess this is a typo.

Load balanced environment and use of END CONVERSATION causes issue

Hi Dmitrii,

If I have more than 1 server listening for the event notification, I am finding that the first server "wins" and other servers do not receive the notification event.

Is this because of:

BEGIN TRY END CONVERSATION @ConvHandle; END TRY BEGIN CATCH END CATCH

Should that line be removed so that all servers receive the message or is there a setting I am missing?

Doesn't seem to be working with Sql Express

Currently I'm trying to use this solution on two DBs. The first one is MS SQL Standard and another is Express. The solution works with Standard version like a charm, but seems to be not compatible with Express version, since I receive no notifications at all.
Am I doing something wrong, or it is supposed to work that way?

Reserved names causes SQL_FORMAT_CREATE_INSTALLATION_PROCEDURE to fail

I have a table called dbo.Order but this appears to cause problems during the set up.

SET @select = STUFF((SELECT '','' + COLUMN_NAME
                                                FROM INFORMATION_SCHEMA.COLUMNS
                                                WHERE TABLE_NAME = ''{5}'' AND TABLE_CATALOG = ''{0}''
                                                FOR XML PATH ('''')
                                                ), 1, 1, '''')

I believe that square brackets are required around {5}.

Error at opening database SqlConnection

Hello, I have been working with sqldependencyEx for a time in a Console Application and now I have to port it in a Windows Service. While debugging in Windows Service, throws me an exception that's an error of logging into the database. In the console Application I didn't get this problem and I copied the files from that project.

This is the error message that displays me:

An exception of type 'System.Data.SqlClient.SqlException' occurred in System.Data.dll but was not handled in user code
Additional information: No se puede abrir la base de datos "LINX" solicitada por el inicio de sesión. Error de inicio de sesión.
Error de inicio de sesión del usuario 'NT AUTHORITY\SYSTEM'.*

*I added after the last line because I didn't see it.

I'm using windows authentication in Microsoft Sql Server 2014, so I don't have user and password, any clue about this error? I'm working with sql express if it helps.

Xml parse error

XElement.Parse throws an XmlException if it encounters invalid charactes in the notificationMessage string here

I encountered this on a table that had a char column that contained a null character.

Patched my local copy by wrapping the Parse in a try-catch and discarding results on error since I don't really need it.

Exception message:
Additional information: '.', hexadecimal value 0x00, is an invalid character.

ListenerServices and ListenerQueues

After a couple of days experimenting with ServiceBrokerListener I have found out that I have about 100-200 ListenerServices and ListenerQueues in my DB.
I'll try to find a solution a bit later, right now I am too busy cleaning the db. Deleting a service takes a lot of time. =)

A small mistake in a query

There is a query:

    private const string SQL_FORMAT_DROP_PROCEDURE = @"
            USE [{0}]
            IF OBJECT_ID ('{2}.{1}', 'P') IS NOT NULL
                DROP PROCEDURE temp.[{1}]
        ";

That should probably look like this:

    private const string SQL_FORMAT_DROP_PROCEDURE = @"
            USE [{0}]
            IF OBJECT_ID ('{2}.{1}', 'P') IS NOT NULL
                DROP PROCEDURE {2}.[{1}]
        ";

Better example

Can you provide a little more thorough sample application? This doesn't really show us how to use your class...

image

Installation fails with multiple databases on the same server

If there is more than 1 database on the same server, the code seems to fail during the installation process with the error:

The Service Broker in database "XXX" cannot be enabled because there is already an enabled Service Broker with the same ID.

The problem seems to be in the SQL_FORMAT_INSTALL_SEVICE_BROKER_NOTIFICATION template:

ALTER DATABASE [{0}] SET ENABLE_BROKER; 

Changing to the following will ensure a new GUID is used for each database's service broker.

ALTER DATABASE [{0}] SET NEW_BROKER; 

different schema

hi i tried the dll on default schema dbo and it works perfectly, but when i specify a different schema seems its not working

Message=Cannot drop the service 'ListenerService_1', because it does not exist or you do not have permissi

Multiple subscription

I have already checked out the library itself and it works like a charm on SQL Server Express 2014, with obviously the fact of enabling the Service Broker feature on the target database which I initially missed, but I have the express necessity to track updates on multiple tables. How can I manage them? What I have seen is like Service Broker only supports to track your initial subscription until you ask yourself for an immediate rollback. Any help would be appreciated. Thank you.

Problems when using multiple SqlDependenciesEx

I am using concurrently two instances of SqlDependencyEx with two different callback. It seems however that

  1. when the 1° dependency fires, both the 1° and 2° callbacks are called
  2. the 2° dependency never fires (even if the db has changed)

This is not what I am expecting from the code below. Where am I wrong?

NOTE: I have forced the two dependencies to have different Identity-es

this.sqlDependencyMailSent = new SqlDependencyEx(
                           ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString,
                           "costerdb",
                           "PLAN_AlarmRelayQueue",
                           "dbo",
                           SqlDependencyEx.NotificationTypes.Delete,true,0);
            this.sqlDependencyMailSent.TableChanged += onMailSent;
            this.enqueuedId = -1;
            if(!this.sqlDependencyMailSent.Active)
                this.sqlDependencyMailSent.Start();

            this.sqlDependencyMailAcked = new SqlDependencyEx(
                                               ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString,
                                               "costerdb",
                                               "PLAN_AlarmRelayAck",
                                               "dbo",
                                               SqlDependencyEx.NotificationTypes.Insert,true,1);

            this.sqlDependencyMailAcked.TableChanged += onMailNotified;
            if (!this.sqlDependencyMailAcked.Active)
                `this.sqlDependencyMailAcked.Start();

Error on Service Stopping

Hi Dmitriy,
Firstly, nice work, this could help us out a lot!
We are just running it in a console application to do some testing and so far seems to work really well however on stopping we get an unhandled exception during cleanup. What looks to be happening is that the listener thread is attempting to access the Queue from the ReceiveEvent method after the Queue has already been deleted by the main thread and obviuosly before the listener thread is aborted.
So Error occurs while the object is disposing on line:
using (var reader = command.ExecuteReader())

when passing the command:
DECLARE @ConvHandle UNIQUEIDENTIFIER
DECLARE @message VARBINARY(MAX)
USE [Assetic_Local]
WAITFOR (RECEIVE TOP(1) @ConvHandle=Conversation_Handle
, @message=message_body FROM dbo.[ListenerQueue_1]), TIMEOUT 30000;
BEGIN TRY END CONVERSATION @ConvHandle; END TRY BEGIN CATCH END CATCH
SELECT CAST(@message AS NVARCHAR(MAX))

We can work around it, just wanted to see if you had come across it or had any insight on it?

Also, just as a side question, not an issue, just wondering the reason behind the 30sec timeout in the above procedure? It removes and starts a new conversation every 30secs?

Thanks!

Exception in closing op...

HI, I tried both your "version" and also the one with async and await through a simple console program. just for watch "How To Use".
Sure, everything works, but when the test program stops (ie after a simple Console.ReadLine) and performs the

//sqlDependency.TableChanged -= OnDataChangeAsync;
sqlDependency.Stop();

in the output window VS2015 I find these errors.

The generated exception: 'System.Data.SqlClient.SqlException' in System.Data.dll
The generated exception: 'System.Data.SqlClient.SqlException' in System.Data.dll
The generated exception: 'System.Data.SqlClient.SqlException' in System.Data.dll
The generated exception: 'System.Data.SqlClient.SqlException' in mscorlib.dll

My environment W8.1x64, VS2015 SQL2014 Express.
Any thoughts?
Thanks in advance.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.