18.3. Programming Notification ServicesYou can build a Notification Services solution by configuring the Notification Services instance in the IDF file and defining an ADF for the application, as described in the preceding section. Alternatively, you can use NMO classes to configure the instance and define the instance. Programming with NMO classes is the focus of this section. NMO contains classes that are used to programmatically create and administer Notification Services instances and applications. Figure 18-2 shows the relationship between NMO classes. The NMO namespace Microsoft.SqlServer.Management.Nmo contains classes used to develop Notification Services instances and applications. The Microsoft.SqlServer.NotificationServices namespace contains classes and interfaces for developing custom event providers, content formatters, and delivery protocols. It also contains the subscription management interfaces. The remainder of this chapter describes the NMO classes and provides examples that show how to use them. You need a reference to the following assemblies to compile and run the examples:
NotificationServices is the top-level class in the NMO class hierarchy and represents a Notification Services server. Figure 18-2. NMO class hierarchy![]() 18.3.1. Creating a Notification Services Application and ServiceThis section creates a complete Notification Services application that creates file and email notifications based on changes in a stock price. For the purpose of this example, the stock price information is limited to a stock ticker symbol and price. Two subscribers are createdone that is notified by entries in a text file and one that is notified using email. Each of the two users monitors a single stockticker symbols ABC and DEF. This section shows the application that creates the StockWatch Notification Services application and adds two subscribers and a subscription for each. Subsequent sections discuss the different parts of the application in detail. Follow these steps to create the StockWatch Notification Services application with two subscribers and a subscription for each, start the service, and generate notifications:
The remainder of this chapter discusses the code that creates the StockWatch service, subscribers, and subscriptions. 18.3.2. Creating a Notification Services Instance and ApplicationThe Notification Services instance named StockWatch and a Notification Services application named StockWatchApp are created in the Main( ) method of Example 18-1. The code followsthe code that creates the Notification Services instance and application is highlighted: static void Main(string[] args) { Server server = new Server("(local)"); Figure 18-8. StockWatch notification email![]() // create a new instance NotificationServices ns = server.NotificationServices; nsi = new Instance(ns, "StockWatch"); CreateDeliveryChannel( ); // create a new application in the StockWatch instance a = new Application(nsi, "StockWatchApp"); a.BaseDirectoryPath = baseDirectoryPath; CreateEventClass( ); CreateSubscriptionClass( ); CreateNotificationClass( ); CreateHostedEventProvider( ); CreateGenerator( ); CreateDistributor( ); CreateVacuumSchedule( ); a.QuantumDuration = new TimeSpan(0, 0, 15); a.PerformanceQueryInterval = new TimeSpan(0, 0, 5); a.SubscriptionQuantumLimit = 1; a.ChronicleQuantumLimit = 1; a.VacuumRetentionAge = new TimeSpan(0, 0, 1); nsi.Applications.Add(a); Console.WriteLine("Added application."); nsi.Create( ); nsi.RegisterLocal(serviceUserName, servicePassword); nsi.Enable( ); Console.WriteLine("Application enabled." + Environment.NewLine); CreateSubscriber( ); CreateSubscription( ); Console.WriteLine(Environment.NewLine + "Press any key to continue."); Console.ReadKey( ); } The NotificationServices object represents a Notification Services server. The Instances property of the NotificationServices class returns an InstanceCollection object containing a collection of Notification Services instances as Instance objects. At a minimum, you must define a DeliveryChannel object and an Application object to create an Instance objectin this example, this is done by the CreateDeliveryChannel( ) and Main( ) methods. The RegisterLocal( ) method of the Instance class registers an instance of Notification Services on the local computer. This is the same as registering a Notification Services instance using SQL Server Management Studio by right-clicking the Notification Services instance and selecting Tasks The Enable( ) method of the Instance class enables all instance and application components, allowing event collection, notification generation, notification distribution, and subscription management. A NotificationServices instance is disabled when you create it. The NMO classes used to manage Notification Services instances are described in Table 18-1.
The Application object represents a Notification Services application. At a minimum, you must define a Generator object and a Distributor object for an Application objectin this example, this is done by the CreateGenerator( ) and CreateDistributor( ) methods. This example configures the Application object by setting the following properties:
The NMO classes used to manage Notification Services applications are described in Table 18-2.
18.3.3. Creating a Delivery ChannelA file delivery channel named StockWatchFileDeliveryChannel and an email delivery channel named StockWatchEmailDeliveryChannel are created in the CreateDeliveryChannel( ) method of Example 18-1. The code follows: private static void CreateDeliveryChannel( ) { DeliveryChannelArgument dca; // add file delivery channel DeliveryChannel dcFile = new DeliveryChannel(nsi, "StockWatchFileDeliveryChannel"); dcFile.ProtocolName = "File"; dca = new DeliveryChannelArgument(dcFile, "FileName"); dca.Value = baseDirectoryPath + @"\Notifications\FileNotifications.txt"; dcFile.DeliveryChannelArguments.Add(dca); nsi.DeliveryChannels.Add(dcFile); Console.WriteLine("Added delivery channel: " + dcFile.Name); // add email delivery channel DeliveryChannel dcEmail = new DeliveryChannel(nsi, "StockWatchEmailDeliveryChannel"); dcEmail.ProtocolName = "SMTP"; nsi.DeliveryChannels.Add(dcEmail); Console.WriteLine("Added delivery channel: " + dcEmail.Name); } You have to add at least one delivery channel to a Notification Services instance before creating it. The ProtocolName property of the DeliveryChannel object must be set to SMTP, File, or the name of a custom delivery protocol. The NMO classes used to manage delivery channels are described in Table 18-3.
18.3.4. Creating an Event ClassAn event class named StockWatchEvents is created in the CreateEventClass( ) method of Example 18-1. The code follows: private static void CreateEventClass( ) { EventClass ec = new EventClass(a, "StockWatchEvents"); EventField ef; ef = new EventField(ec, "Symbol"); ef.Type = "nvarchar(6)"; ec.EventFields.Add(ef); ef = new EventField(ec, "Price"); ef.Type = "float"; ec.EventFields.Add(ef); a.EventClasses.Add(ec); Console.WriteLine("Added event class: " + ec.Name); } An event class represents a type of event used by a Notification Services application. The event class has two fieldsSymbol of type nvarchar(6) and Price of type float. The NMO classes for managing event classes, fields, and chronicles are described in Table 18-4.
18.3.5. Creating a Subscription Class and Subscription Event RuleA subscription class with a single subscription event rule is created in the CreateSubscriptionClassMethod( ) method of Example 18-1. The code follows: private static void CreateSubscriptionClass( ) { SubscriptionClass sc = new SubscriptionClass(a, "StockWatchSubscriptions"); SubscriptionField sf; sf = new SubscriptionField(sc, "DeviceName"); sf.Type = "nvarchar(255)"; sc.SubscriptionFields.Add(sf); sf = new SubscriptionField(sc, "SubscriberLocale"); sf.Type = "nvarchar(10)"; sc.SubscriptionFields.Add(sf); sf = new SubscriptionField(sc, "Symbol"); sf.Type = "nvarchar(6)"; sc.SubscriptionFields.Add(sf); sf = new SubscriptionField(sc, "Price"); sf.Type = "float"; sc.SubscriptionFields.Add(sf); SubscriptionEventRule ser = new SubscriptionEventRule(sc, "StockWatchSubscriptionsEventRule"); ser.Action = @"INSERT INTO StockWatchNotifications (" + "SubscriberId, DeviceName, SubscriberLocale, Symbol, Price) " + "SELECT s.SubscriberId, s.DeviceName, s.SubscriberLocale, " + "e.Symbol, e.Price " + "FROM StockWatchEvents e, StockWatchSubscriptions s " + "WHERE e.Symbol = s.Symbol"; ser.EventClassName = "StockWatchEvents"; sc.SubscriptionEventRules.Add(ser); a.SubscriptionClasses.Add(sc); Console.WriteLine("Added subscription class: " + sc.Name); } A SubscriptionClass object defines a type of subscription within a Notification Services application. The SubscriptionField objects added to the Subscription object represent fields in the subscription class schema. A SubscriptionChronicle object lets you store subscription information outside of tables used by the subscription classthis example does not use a subscription chronicle. The NMO classes for managing, subscription chronicles subscription classes, and subscription fields are described in Table 18-5.
A SubscriptionEventRule object represents a rule that uses T-SQL queries to generate notifications when event batches arrive. The Action property represents the T-SQL query for the SubscriptionEventRule object. In this example, notifications are generated when the ticker symbol of an event matches the ticker symbol specified in a subscription. The NMO classes for managing the different types of subscription rules are described in Table 18-6.
18.3.6. Creating a Notification Class, Content Formatter, and Notification Class ProtocolA notification class named StockWatchNotifications, a content formatter named XsltFormatter, and two notification class protocols named File and SMTP are created in the CreateNotificationClass( ) method of Example 18-1. The code follows: private static void CreateNotificationClass( ) { NotificationClass nc = new NotificationClass(a, "StockWatchNotifications"); NotificationField nf; nf = new NotificationField(nc, "Symbol"); nf.Type = "nvarchar(6)"; nc.NotificationFields.Add(nf); nf = new NotificationField(nc, "Price"); nf.Type = "float"; nc.NotificationFields.Add(nf); ContentFormatter cf = new ContentFormatter(nc, "XsltFormatter"); ContentFormatterArgument cfa; cfa = new ContentFormatterArgument(cf, "XsltBaseDirectoryPath"); cfa.Value = a.BaseDirectoryPath + @"\AppDefinition"; cf.ContentFormatterArguments.Add(cfa); cfa = new ContentFormatterArgument(cf, "XsltFileName"); cfa.Value = "StockWatch.xslt"; cf.ContentFormatterArguments.Add(cfa); nc.ContentFormatter = cf; nc.DigestDelivery = true; ProtocolField pf; // add file notification class protocol NotificationClassProtocol ncpFile = new NotificationClassProtocol(nc, "File"); pf = new ProtocolField(ncpFile, "Symbol"); pf.FieldReference = "Symbol"; ncpFile.ProtocolFields.Add(pf); pf = new ProtocolField(ncpFile, "Price"); pf.FieldReference = "Price"; ncpFile.ProtocolFields.Add(pf); nc.NotificationClassProtocols.Add(ncpFile); // add email notification class protocol NotificationClassProtocol ncpEmail = new NotificationClassProtocol(nc, "SMTP"); pf = new ProtocolField(ncpEmail, "Subject"); pf.SqlExpression = "'Stock watch: ' + CONVERT(nvarchar(30), GETDATE( ))"; ncpEmail.ProtocolFields.Add(pf); pf = new ProtocolField(ncpEmail, "BodyFormat"); pf.SqlExpression = "'html'"; ncpEmail.ProtocolFields.Add(pf); pf = new ProtocolField(ncpEmail, "From"); pf.SqlExpression = "'notification@StockWatchService.com'"; ncpEmail.ProtocolFields.Add(pf); pf = new ProtocolField(ncpEmail, "Priority"); pf.SqlExpression = "'Normal'"; ncpEmail.ProtocolFields.Add(pf); pf = new ProtocolField(ncpEmail, "To"); pf.SqlExpression = "DeviceAddress"; ncpEmail.ProtocolFields.Add(pf); nc.NotificationClassProtocols.Add(ncpEmail); nc.ExpirationAge = new TimeSpan(1, 0, 0); a.NotificationClasses.Add(nc); Console.WriteLine("Added notification class: " + nc.Name); } The NotificationClass object represents a type of notification supported by a Notification Services application. A NotificationField object represents a field in a notification class schema. The notification class in this example has two fieldsSymbol of type nvarchar(6) and Price of type float. The NMO classes for managing notification classes and fields are described in Table 18-7.
A content formatter formats notifications for a notification class. Each notification class has one content formatter that can perform different formatting based on field values in the notification. The content formatter takes three arguments:
The NMO classes for managing content formatters are described in Table 18-8.
A notification class protocol represents a delivery protocol for a notification class. A ProtocolField object represents a protocol header field used by some delivery protocols . Protocol field headers are different for file and email notificationexamine the code and examine the file and email notifications shown in Figure 18-7 and Figure 18-8 to see the result of setting the protocol fields. The value of the ProtocolField object is set using either the SqlExpression or FieldReference property. This lets you use either a T-SQL expression or a notification field to define a protocol field value. The NMO classes for managing protocols are described in Table 18-9.
18.3.7. Creating an Event ProviderA hosted event provider is created in the CreateHostedEventProvider( ) method of Example 18-1. The code follows: private static void CreateHostedEventProvider( ) { HostedEventProvider hep = new HostedEventProvider(a, "StockWatchHEP"); hep.ClassName = "FileSystemWatcherProvider"; hep.SystemName = nsServer; HostedEventProviderArgument hepa; hepa = new HostedEventProviderArgument(hep, "WatchDirectory"); hepa.Value = baseDirectoryPath + @"\Events"; hep.HostedEventProviderArguments.Add(hepa); hepa = new HostedEventProviderArgument(hep, "SchemaFile"); hepa.Value = baseDirectoryPath + @"\AppDefinition\EventsSchema.xsd"; hep.HostedEventProviderArguments.Add(hepa); hepa = new HostedEventProviderArgument(hep, "EventClassName"); hepa.Value = "StockWatchEvents"; hep.HostedEventProviderArguments.Add(hepa); a.HostedEventProviders.Add(hep); } Event providers collect event data and submit it to an event class. Hosted event providers are run by the Notification Services engine. Nonhosted event providers, on the other hand, run outside of the engine, and have no interaction with Notification Services. The ClassName property of the HostedEventProvider class specifies the class that implements the event provider. Notification Services has three built-in event providers, described in the "Architecture" section earlier in this chapter. You can also use a custom event provider. This example uses the File System Watcher event provider (with a ClassName of FileSystemWatcherProvider), which gathers events of the StockWatchEvents event class from a file in the C:\PSS2005\NotificationServices\Events directory. The event file must conform to the schema defined in the C:\PSS2005\NotificationServices\AppDefinition\EventsSchema.xsd file. The NMO classes for managing hosted and nonhosted event providers are described in Table 18-10.
18.3.8. Creating a GeneratorA generator named StockWatchGenerator is created in the CreateGenerator( ) method of Example 18-1. The code follows: private static void CreateGenerator( ) { // create a new generator for the application Generator g = new Generator(a, "StockWatchGenerator"); g.SystemName = nsServer; a.Generator = g; Console.WriteLine("Created generator: " + g.Name); } The Generator object represents the generator for a Notification Services application. The Generator property of the Application class returns the generator for the application. You have to specify the Generator property of the Application object before adding the application to the Notification Services instance. An application has only one generator, which handles rule processing for the application. The SystemName property of the Generator class must be specified, and cannot be any of the following: localhost, ., an IP address, or any string containing a backslash (\). 18.3.9. Creating a DistributorA distributor named StockWatchDistributor is created in the CreateDistributor( ) method of Example 18-1. The code follows: private static void CreateDistributor( ) { Distributor d = new Distributor(a, "StockWatchDistributor"); d.SystemName = nsServer; d.QuantumDuration = new TimeSpan(0, 0, 15); a.Distributors.Add(d); Console.WriteLine("Added distributor: " + d.Name); } The Distributor object represents a distributor for a Notification Services application. Each application must have one distributor that controls formatting and distribution of notifications. If an application has multiple distributors, each one must be installed on a different server. The QuantumDuration property specifies the distributor work item polling interval, which is 15 seconds in this example. The NMO classes for managing distributors are described in Table 18-11.
18.3.10. Creating a Vacuum ScheduleA vacuum schedule named StockWatchVacuumSchedule is created in the CreateVacuumSchedule( ) method of Example 18-1. The code follows: private static void CreateVacuumSchedule( ) { VacuumSchedule vs = new VacuumSchedule(a, "StockWatchVacuumSchedule"); vs.StartTime = new TimeSpan(0, 0, 0); a.VacuumSchedules.Add(vs); Console.WriteLine("Added vacuum schedule: " + vs.Name); } Vacuuming removes old event and notification data from the application database on a daily schedule. The StartTime property specifies the daily time in Universal Coordinated Time (UTC) for the data removal process to start. Although specifying a vacuum schedule is optional, old data is not removed from the database if a vacuum schedule is not specified. The NMO classes for managing vacuum schedules are described in Table 18-12.
18.3.11. Creating a Subscriber and Subscriber DeviceTwo subscribers, each with a single subscriber device, are created in the CreateSubscriber( ) method of Example 18-1. The code follows: private static void CreateSubscriber( ) { ns.NSInstance swnsi = new ns.NSInstance("StockWatch"); ns.Subscriber s; ns.SubscriberDevice sd; // create a subscriber s = new ns.Subscriber(swnsi); s.SubscriberId = @"KristinHamilton"; s.Add( ); Console.WriteLine("Added subscriber: " + s.SubscriberId); // create a file subscriber device sd = new ns.SubscriberDevice( ); sd.Initialize(swnsi); sd.DeviceName = "StockWatchSubscriberDevice"; sd.SubscriberId = "KristinHamilton"; sd.DeviceTypeName = "File"; sd.DeviceAddress = "KristinH@StockWatch.ns"; sd.DeliveryChannelName = "StockWatchFileDeliveryChannel"; sd.Add( ); Console.WriteLine("Added subscriber file device."); // create a subscriber s = new ns.Subscriber(swnsi); s.SubscriberId = @"TonyHamilton"; s.Add( ); Console.WriteLine("Added subscriber: " + s.SubscriberId); // create an email subscriber device sd = new ns.SubscriberDevice ( ); sd.Initialize(swnsi); sd.DeviceName = "StockWatchSubscriberDevice"; sd.SubscriberId = "TonyHamilton"; sd.DeviceTypeName = "Email"; sd.DeviceAddress = "TonyH@StockWatchNS.ns"; sd.DeliveryChannelName = "StockWatchEmailDeliveryChannel"; sd.Add( ); Console.WriteLine("Added subscriber email device."); } The NSInstance class represents a Notification Services instance that is registered in the Windows registry. The Subscriber class represents a subscriber in a Notification Services instance. The SubscriberEnumeration class represents a collection of subscribers as Subscriber objects in a Notification Services instance. The SubscriberEnumeration class is used to retrieve a Subscriber object from the collection of subscribers to the Notification Services instance. The Delete( ) method removes a subscriber, the Add( ) method adds a subscriber, and the Update( ) method updates the information for an existing subscriber. The following code deletes the subscriber AnySubscriber from a Notification Service instance named HelloWorld: NSInstance nins = new NSInstance("HelloWorld"); SubscriberEnumeration se = new SubscriberEnumeration(nins); se["AnySubscriber"].Delete( ); The SubscriberDevice class represents a device that can receive notifications. A single subscriber device is added to each subscriber. Each subscriber device specifies the following properties:
The SubscriberDeviceEnumeration class is used to retrieve a SubscriberDevice object from the collection of devices for a subscriber. The Delete( ) method removes a device, the Add( ) method adds a device, and the Update( ) method updates the information for an existing device. 18.3.12. Creating a SubscriptionTwo subscriptions are created in the CreateSubscription( ) method of Example 18-1. The code follows: private static void CreateSubscription( ) { ns.NSInstance swnsi = new ns.NSInstance("StockWatch"); ns.NSApplication a = new ns.NSApplication(swnsi, "StockWatchApp"); ns.Subscription s; // add subscriptions s = new ns.Subscription( ); s.Initialize(a, "StockWatchSubscriptions"); s.SetFieldValue("DeviceName", "StockWatchSubscriberDevice"); s.SetFieldValue("SubscriberLocale", "en-us"); s.SubscriberId = "KristinHamilton"; s.SetFieldValue("Symbol", "ABC"); s.SetFieldValue("Price", "0.00"); s.Add( ); Console.WriteLine("Added subscription: " + s.SubscriberId); s = new ns.Subscription( ); s.Initialize(a, "StockWatchSubscriptions"); s.SetFieldValue("DeviceName", "StockWatchSubscriberDevice"); s.SetFieldValue("SubscriberLocale", "en-us"); s.SubscriberId = "TonyHamilton"; s.SetFieldValue("Symbol", "DEF"); s.SetFieldValue("Price", "0.00"); s.Add( ); Console.WriteLine("Added subscription: " + s.SubscriberId); } The NSApplication class represents a Notification Services application. The Subscription class represents a subscription in a Notification Services application. Both subscriptions use the subscriber device named StockWatchSubscriberDevice created in the "Creating a Subscriber and Subscriber Device" section earlier in this chapter. The subscription for KristinHamilton is for events for ticker symbol ABC, while the subscription for subscriber TonyHamilton is for events for ticker symbol DEF. The results can be seen in the notifications shown in Figures 18-7 and 18-8. The SubscriptionEnumeration class represents a collection of subscriptions as Subscription objects in a Notification Services application. Use the Subscrip-tionEnumeration class to iterate over a collection of subscriptions for an application and manage the individual subscriptions in the collection. These two classes are implemented in the Microsoft.SqlServer.NotificationServices namespace. 18.3.13. Enumerating a Notification Services Instance DatabaseThis example enumerates the filegroup, database files, and logfiles for the StockWatch Notification Services instance created in Example 18-1: using System; using Microsoft.SqlServer.Management.Smo; using Microsoft.SqlServer.Management.Nmo; class Program { static void Main(string[] args) { Server server = new Server("(local)"); NotificationServices ns = server.NotificationServices; Instance ins = ns.Instances["StockWatch"]; InstanceDatabaseOptions ido = ins.InstanceDatabaseOptions; Console.WriteLine("INSTANCE DATABASE OPTIONS:"); Console.WriteLine(" Name: " + ido.Name); Console.WriteLine(" DefaultFileGroup: " + ido.DefaultFileGroup); Console.WriteLine(Environment.NewLine + "FILE GROUPS:"); foreach (InstanceDatabaseFileGroup idfg in ido.InstanceDatabaseFileGroups) { Console.WriteLine(" Name: " + idfg.Name); Console.WriteLine(Environment.NewLine + "DATABASE FILES"); foreach (InstanceDatabaseFile idf in idfg.InstanceDatabaseFiles) Console.WriteLine(" Name: " + idf.Name); } Console.WriteLine(Environment.NewLine + "LOG FILES:"); foreach (InstanceDatabaseLogFile idlf in ido.InstanceDatabaseLogFiles) Console.WriteLine(" Name: " + idlf.Name); Console.WriteLine(Environment.NewLine + "Press any key to continue."); Console.ReadKey( ); } } Results are shown in Figure 18-9. Each Notification Services instance has one database that can optionally contain one or more filegroups. If you define filegroups, one must be called PRIMARY. Use SQL Server Management Studio if you need to alter the Notification Services instance database after creating the instance. Figure 18-9. Results for enumerating a Notification Services instance database example![]() The NMO classes for managing the instance database, filegroups, files, and logfiles are described in Table 18-13.
18.3.14. Enumerating a Notification Services Application DatabaseThis example enumerates the filegroup, database files, and logfiles for the HelloWorldApp Notification Services application created in Example 18-1: using System; using Microsoft.SqlServer.Management.Smo; using Microsoft.SqlServer.Management.Nmo; class Program { static void Main(string[] args) { Server server = new Server("(local)"); NotificationServices ns = server.NotificationServices; Instance ins = ns.Instances["StockWatch"]; Application a = ins.Applications["StockWatchApp"]; ApplicationDatabaseOptions ado = a.ApplicationDatabaseOptions; Console.WriteLine("APPLICATION DATABASE OPTIONS:"); Console.WriteLine(" Name: " + ado.Name); Console.WriteLine(" DefaultFileGroup: " + ado.DefaultFileGroup); Console.WriteLine(Environment.NewLine + "FILE GROUPS:"); foreach (ApplicationDatabaseFileGroup adfg in ado.ApplicationDatabaseFileGroups) { Console.WriteLine(" Name: " + adfg.Name); Console.WriteLine(Environment.NewLine + "DATABASE FILES"); foreach (ApplicationDatabaseFile adf in adfg.ApplicationDatabaseFiles) Console.WriteLine(" Name: " + adf.Name); } Console.WriteLine(Environment.NewLine + "LOG FILES:"); foreach (ApplicationDatabaseLogFile adlf in ado.ApplicationDatabaseLogFiles) Console.WriteLine(" Name: " + adlf.Name); Console.WriteLine(Environment.NewLine + "Press any key to continue."); Console.ReadKey( ); } } Results are shown in Figure 18-10. Figure 18-10. Results for enumerating a Notification Services application database example![]() Each Notification Services application has one database that can optionally contain one or more filegroups. If you define filegroups, one must be called PRIMARY. Use SQL Server Management Studio if you need to alter the application database after creating the instance. The NMO classes for managing the application database, filegroups, files, and logfiles are described in Table 18-14.
|