Desidero utilizzare SqlDependency
per ottenere notifiche quando alcuni dati vengono modificati da altre applicazioni che utilizzano il database.SqlDependency si attiva immediatamente
public class DatabaseChangesNotification : IDisposable
{
private static string chaineDeConnexion = ConfigurationManager.ConnectionStrings["TransfertContext"].ConnectionString;
private static readonly Lazy<DatabaseChangesNotification> _instance = new Lazy<DatabaseChangesNotification>(() => new DatabaseChangesNotification());
private DatabaseChangesNotification()
{
System.Diagnostics.Trace.WriteLine("--- SqlDependency START ---");
SqlDependency.Start(chaineDeConnexion);
}
public void Dispose()
{
System.Diagnostics.Trace.WriteLine("--- SqlDependency STOP ---");
SqlDependency.Stop(chaineDeConnexion);
}
public static DatabaseChangesNotification Instance
{
get
{
return _instance.Value;
}
}
public void AbonnerNotification(string requete, OnChangeEventHandler eventhandler)
{
using (SqlConnection connection = new SqlConnection(chaineDeConnexion))
{
using (SqlCommand command = new SqlCommand(requete, connection) { Notification = null }) // clear existing notifications
{
connection.Open();
var sqlDependency = new SqlDependency(command);
OnChangeEventHandler delegateAutoRemove = null;
delegateAutoRemove = (sender, e) => {
var dependency = sender as SqlDependency;
dependency.OnChange -= delegateAutoRemove;
eventhandler(sender, e);
};
sqlDependency.OnChange += delegateAutoRemove;
command.ExecuteNonQuery();
}
}
}
}
Così, con una singola linea posso registrare un gestore di eventi:
DatabaseChangesNotification.Instance.AbonnerNotification(@"SELECT IdUtilisateur, Code, Nom, Prenom, NomComplet, Login, Synchroniser FROM dbo.Utilisateur", OnChanges);
public void OnChanges(object sender, SqlNotificationEventArgs e){
System.Diagnostics.Trace.WriteLine("------------------------------ UPDATTEEEE -------------------------");
System.Diagnostics.Trace.WriteLine("Info: " + e.Info.ToString());
System.Diagnostics.Trace.WriteLine("Source: " + e.Source.ToString());
System.Diagnostics.Trace.WriteLine("Type: " + e.Type.ToString());
GlobalHost.ConnectionManager.GetHubContext<TransfertClientHub>().Clients.All.hello("users modified !");
//AbonnementChanges();
}
Ma il mio problema è che la notifica è immediatamente licenziato:
--- ABONNEMENT ---
------------------------------ UPDATTEEEE -------------------------
Info: Query
Source: Statement
Type: Subscribe
Ecco perché ho commentato AbonnementChanges
nel mio event handler OnChanges
(o loop infinitamente).
Non so da dove proviene il problema perché ho resettato le notifiche ({ Notification = null }
) e la mia richiesta rispetta i requisiti (https://msdn.microsoft.com/en-us/library/ms181122.aspx).
Modifica: Desidero aggiungere che select * from sys.dm_qn_subscriptions
non restituisce nulla.
Edit: Sembra che proviene dalla configurazione del database, e non dal mio L'implementazione, come ho provato un altro L'implementazione che provocano lo stesso comportamento: http://www.codeproject.com/Articles/144344/Query-Notification-using-SqlDependency-and-SqlCach
Edit: non vedo dove viene da quando uso SA che è sysadmin e ha tutti i diritti, non è vero?
Edit: ho cercato di definire un altro connessione al database seguendo questo tutorial: http://www.codeproject.com/Articles/12862/Minimum-Database-Permissions-Required-for-SqlDepen
Così ho creato 2 ruoli:
EXEC sp_addrole 'sql_dependency_subscriber'
EXEC sp_addrole 'sql_dependency_starter'
-- Permissions needed for [sql_dependency_starter]
GRANT CREATE PROCEDURE to [sql_dependency_starter]
GRANT CREATE QUEUE to [sql_dependency_starter]
GRANT CREATE SERVICE to [sql_dependency_starter]
GRANT REFERENCES on
CONTRACT::[http://schemas.microsoft.com/SQL/Notifications/PostQueryNotification]
to [sql_dependency_starter]
GRANT VIEW DEFINITION TO [sql_dependency_starter]
-- Permissions needed for [sql_dependency_subscriber]
GRANT SELECT to [sql_dependency_subscriber]
GRANT SUBSCRIBE QUERY NOTIFICATIONS TO [sql_dependency_subscriber]
GRANT RECEIVE ON QueryNotificationErrorsQueue TO [sql_dependency_subscriber]
GRANT REFERENCES on
CONTRACT::[http://schemas.microsoft.com/SQL/Notifications/PostQueryNotification]
to [sql_dependency_subscriber]
e poi ho aggiunto l'utente (production
) di questa ruoli:
- Assicurarsi che i miei utenti siano membri del ruolo corretto.
EXEC sp_addrolemember 'sql_dependency_starter', 'production'
EXEC sp_addrolemember 'sql_dependency_subscriber', 'production'
Ma con questa connessione ho lo stesso comportamento di prima. Notifica sono licenziati imediatly:
------------------------------ UPDATTEEEE -------------------------
Info: Query
Source: Statement
Type: Subscribe
Edit: ho provato con le richieste più semplici come: SELECT Nom, Prenom FROM dbo.Utilisateur
. Ecco i dettagli della tabella, che deve essere controllato:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[Utilisateur](
[IdUtilisateur] [uniqueidentifier] ROWGUIDCOL NOT NULL CONSTRAINT [DF_Utilisateur_IdUtilisateur] DEFAULT (newid()),
[Code] [varchar](10) NOT NULL,
[Nom] [varchar](100) NOT NULL,
[Prenom] [varchar](100) NULL,
[NomComplet] AS (([Prenom]+' ')+[Nom]),
[Login] [varchar](50) NULL,
[Synchroniser] [bit] NOT NULL CONSTRAINT [DF_Utilisateur_Synchroniser] DEFAULT ((1)),
[DATE_CREATION] [datetime] NOT NULL CONSTRAINT [DF__Utilisate__DATE___2AA1E7C7] DEFAULT (getdate()),
[DATE_DERNIERE_MODIF] [datetime] NOT NULL CONSTRAINT [DF__Utilisate__DATE___2B960C00] DEFAULT (getdate()),
[Desactive] [bit] NOT NULL CONSTRAINT [DF_Utilisateur_Desactive] DEFAULT ((0)),
CONSTRAINT [PK_Utilisateur] PRIMARY KEY CLUSTERED
(
[IdUtilisateur] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
Come possiamo vedere ci sono alcune colonne che non possono essere richiesti. Ecco perché non lo uso.
Ora controlliamo con SELECT Nom, Prenom FROM dbo.Utilisateur
:
- Le colonne proiettate in SELECT deve essere esplicitamente dichiarato , e nomi delle tabelle devono essere qualificati con i nomi in due parti.Si noti che significa che tutte le tabelle a cui si fa riferimento nella dichiarazione devono essere nello stesso database. OK
- La dichiarazione non può utilizzare l'asterisco () o table_name. sintassi su specificare colonne. OK
- L'istruzione non può utilizzare colonne senza nome o nomi di colonne duplicati. OK
- La dichiarazione deve fare riferimento a una tabella di base. OK
- Le colonne proiettate nell'istruzione SELECT non possono contenere espressioni aggregate a meno che l'istruzione non utilizzi un'espressione GROUP BY . Quando viene fornita un'espressione GROUP BY, l'elenco di selezione può contenere le funzioni aggregate COUNT_BIG() o SUM(). Tuttavia, SUM() non può essere specificato per una colonna nullable. OK
- L'istruzione non può specificare HAVING, CUBE o ROLLUP. Una colonna proiettata nell'istruzione SELECT utilizzata come espressione semplice non deve apparire più di una volta. OK
- La dichiarazione non deve includere operatori PIVOT o UNPIVOT. OK
- La dichiarazione non deve includere gli operatori INTERSECT o EXCEPT. OK
- L'istruzione non deve fare riferimento a una vista. OK
- La dichiarazione non deve contenere nessuno dei seguenti elementi: DISTINCT, COMPUTE o COMPUTE BY o INTO. OK
- L'istruzione non deve fare riferimento alle variabili globali del server (@@ nome_variabile). OK
- L'istruzione non deve fare riferimento a tabelle derivate, tabelle temporanee o variabili di tabella . OK
- L'istruzione non deve fare riferimento a tabelle o viste da altri database o server. OK
- L'istruzione non deve contenere subquery, outer join o self-join. OK
- L'istruzione non deve fare riferimento ai tipi di oggetto di grandi dimensioni: testo, ntext, e immagine. OK
- L'istruzione non deve utilizzare i predicati full-text CONTAINS o FREETEXT . OK
- L'istruzione non deve utilizzare le funzioni del set di righe, tra cui OPENROWSET e OPENQUERY. OK
- L'istruzione non deve utilizzare nessuna delle seguenti funzioni di aggregazione: AVG, COUNT (*), MAX, MIN, DEV.ST, STDEVP, VAR o VARP.OK
- La dichiarazione non deve utilizzare funzioni non deterministiche, tra cui posizionamento e funzioni di finestre. OK
- L'istruzione non deve contenere aggregati definiti dall'utente. OK
- L'istruzione non deve fare riferimento alle tabelle di sistema o alle viste, incluse le viste del catalogo e le viste di gestione dinamica . OK
- La dichiarazione non deve includere PER INFORMAZIONI DI SFOGLIA. OK
- L'istruzione non deve fare riferimento a una coda. OK
- L'istruzione non deve contenere istruzioni condizionali che non possono cambiare e non possono restituire risultati (ad esempio, WHERE 1 = 0). OK
Ma questo ancora non funziona ... = (
montaggio finale - Soluzione: Come ha detto Jon Tirjan, è stato causato da mia colonna calcolata NomComplet
che non è valido il Service Broker (anche se non chiedo di essere informato sulle modifiche su questa colonna, che è strano per me)
* Voglio aggiungere che 'select * da sys.dm_qn_subscriptions' non restituisce nulla * Beh sì,' SqlDependency' annulla l'iscrizione una volta ricevuta una notifica. Il tuo evento è stato licenziato, nessun abbonamento in più. –
Esattamente. Quello che ho capito anche io. –
Si prega di non [cross post this] (http://dba.stackexchange.com/questions/96886/sqldependency-fires-immediately) la prossima volta su DBA.SE. – LowlyDBA