How To: Develop an Instance Control Provider

The following procedure provides steps to create a custom instance control provider.

  1. Create a Class Library project.

  2. Add a reference to Microsoft.ApplicationServer.StoreManagement. In addition, add a reference to the System.Configuration assembly to compile the sample code in this topic.

  3. Add the following statement at the beginning of the source file.

    using Microsoft.ApplicationServer.StoreManagement.Control;
    using Microsoft.ApplicationServer.StoreManagement.Control;
    using System.Data.SqlClient;
    using System.Collections.Specialized;
    using System.Threading;
    using System.Data;
    
  4. Create a class for the instance control provider deriving from the InstanceControlProvider class.

        public class MySqlInstanceControlProvider : InstanceControlProvider
        {
        }
    
  5. Implement the Initialize method. This method accepts a property bag that corresponds to configuration information specified in the configuration file. The data in this property bag is used to construct the provider. The Initialize method is called before the CreateInstanceControl or UniqueProviderIdentifier method is called.

    Note

    In remoting scenarios, the name-value collection would contain an item named “EnableServiceModelMetadata”. The provider can choose to ignore and remove this parameter before invoking the base.Initialize method. This property is typically used to determine whether to invoke SetMetadata(“ServiceModel”, true) on the Microsoft.Web.Administration.ServerManager object.

    
            string ConnectionString { get; set; }
    
            public override void Initialize(string name, NameValueCollection config)
            {
    
                this.ConnectionString = config["connectionString"];
    
                // Initialize the base class
                base.Initialize(name, config);
            }
    
  6. Implement the CreateInstanceControl method of the InstanceControlProvider class to return a custom InstanceControl object, which the client will use to access a CommandSend object or a CommandReceive object.

            public override InstanceControl CreateInstanceControl()
            {
                SqlConnectionStringBuilder connectionStringBuilder = new SqlConnectionStringBuilder(this.ConnectionString);
                connectionStringBuilder.AsynchronousProcessing = true;
                return new MySqlInstanceControl(connectionStringBuilder.ConnectionString);
            }
    

    Note

    See the next section for the implementation of the MySqlInstanceControl type.

  7. Implement the UniqueProviderIdentifier method. The unique provider ID that this method returns is used to determine whether different provider objects resolve to the same underlying store.

            string UniqueStoreIdentifier { get; set; }
    
            public override string UniqueProviderIdentifier()
            {   
                this.UniqueStoreIdentifier = GetUniqueStoreIdentifier(this.ConnectionString); 
                return this.UniqueStoreIdentifier;
            }
    
            private string GetUniqueStoreIdentifier(string connectionString)
            {
                using (SqlConnection connection = new SqlConnection(connectionString))
                {
                    using (SqlCommand command = new SqlCommand())
                    {
                        command.CommandType = System.Data.CommandType.Text;
                        command.CommandText = "SELECT TOP (1) [StoreIdentifier] FROM [Microsoft.ApplicationServer.DurableInstancing].[StoreVersion]";
                        command.Connection = connection;
    
                        command.Connection.Open();
    
                        Guid identifier = (Guid)command.ExecuteScalar();
                        return identifier.ToString();
                    }
                }
            }
    

Implementing InstanceControl

The following procedure provides steps to create a custom InstanceControl type.

  1. Create a class deriving from the InstanceControl class.

        public class MySqlInstanceControl : InstanceControl
        {
            readonly string connectionString;
             public MySqlInstanceControl(string connectionString)
             {
                 this.connectionString = connectionString;
             }
        }
    
  2. Implement the CommandReceive property. The Get accessor method of the CommandReceive property should return a CommandReceive object. A client invokes methods on this object to asynchronously receive commands from the command queue.

            MySqlCommandReceive commandReceive;
            public override CommandReceive CommandReceive
            {
                get 
                {
                    if (this.commandReceive == null)
                    {
                        MySqlCommandReceive tmp = new MySqlCommandReceive(this.connectionString);
                        Interlocked.CompareExchange(ref this.commandReceive, tmp, null);
                    }
                    return this.commandReceive;
                }
            }
    
  3. Implement the CommandSend property and return a custom CommandSend object from the Get accessor method. The Get method of the CommandSend property should return a CommandSend object. A client invokes methods of this object to asynchronously send commands to the command queue.

            MySqlCommandSend commandSend;
            public override CommandSend CommandSend
            {
                get
                {
                    if (this.commandSend == null)
                    {
                        MySqlCommandSend tmp = new MySqlCommandSend(this.connectionString);
                        Interlocked.CompareExchange(ref this.commandSend, tmp, null);
                        return this.commandSend;
                    }
                    return this.CommandSend;
                }
            }
    

    The client uses these two objects to send commands to the command queue (enqueue) and receive commands from the command queue (dequeue). For example, an instance control cmdlet enqueues a command to the command queue by using the CommandSend object and the Workflow Management Service (WMS) dequeues the command from the command queue by using the CommandReceive object. In some circumstances, such as when the Remove-ASAppServiceInstance cmdlet is executed, the store itself processes the command to remove the instance from the instance store.

    Note

    See the following sections for implementations of the MySqlCommandSend and MySqlCommandReceive types.

Implementing CommandSend

To create a custom CommandSend type:

  • Create a class deriving from the CommandSend class and implement the BeginTrySend and EndTrySend methods.

        public class MySqlCommandSend : CommandSend
        {
            readonly string connectionString;
    
            public MySqlCommandSend(string connectionString)
            {
                this.connectionString = connectionString;
            }
    
            public override IAsyncResult BeginSend(InstanceCommand command, TimeSpan timeout, AsyncCallback callback, object state)
            {
                throw new NotImplementedException();
            }
    
            public override void EndSend(IAsyncResult result)
            {
                throw new NotImplementedException();
            }
        }
    

Implementing CommandReceive

To create a custom CommandReceive type:

  • Create a class deriving from the CommandReceive class and implement the BeginTryReceive and EndTryReceive methods.

        public class MySqlCommandReceive : CommandReceive
        {
            readonly string connectionString;
    
            Queue<MySqlReceivedInstanceCommand> receivedInstanceCommands;
    
            public MySqlCommandReceive(string connectionString)
            {
                this.connectionString = connectionString;
                this.receivedInstanceCommands = new Queue<MySqlReceivedInstanceCommand>();          
            }
    
            public override IAsyncResult BeginTryReceive(TimeSpan timeout, AsyncCallback callback, object state)
            {
                throw new NotImplementedException();
            }
    
            public override bool EndTryReceive(IAsyncResult result, out ReceivedInstanceCommand command)
            {
                throw new NotImplementedException();
            }
        }
    

    Note

    See the following section for implementation of the MySqlReceivedInstanceCommand class.

Implementing ReceivedInstanceCommand

To create a custom ReceivedInstanceCommand type:

  • Create a class deriving from the ReceivedInstanceCommand class and implement the InstanceCommandContext property.

        class MySqlReceivedInstanceCommand : ReceivedInstanceCommand
        {
            long commandId;
            MySqlCommandReceive receiver;
            MySqlInstanceCommandContext context;
    
            internal MySqlReceivedInstanceCommand(long commandId,
                 Guid instanceId, Microsoft.ApplicationServer.StoreManagement.Control.CommandType commandType, IDictionary<string, string> serviceIdentifier, TimeSpan timeout,
                MySqlCommandReceive receiver) : base()
            {
                this.commandId = commandId;
                base.CommandType = commandType;
                base.InstanceId = instanceId;
                base.ServiceIdentifier = serviceIdentifier;
                //this.CommandTimeout = new TimeoutHelper(timeout, true);
                this.receiver = receiver;
                this.context = new MySqlInstanceCommandContext(this);
            }
            public override InstanceCommandContext InstanceCommandContext
            {
                get
                {
                    return this.context;
                }
            }
        }
    

    Note

    See the following section for implementation of the MySqlInstanceCommandContext type.

Implementing InstanceCommandContext

To create a custom InstanceCommandContext type:

  • Create a class deriving from the InstanceCommandContext class and implement the BeginComplete, EndComplete, BeginAbandon, and EndAbandon methods.

    A client invokes the BeginComplete method on the InstanceCommandContext object to notify that the command execution succeeded. Then the control provider should remove the command from the command queue. The WMS invokes this method if the command execution succeeds.

    A client invokes the BeginAbandon method on the InstanceCommandContext object to notify that the command execution failed. It is up to the instance control provider to decide what to do with the command. For example, a control provider could retry the command for a specific number of times before removing the command from the command queue. The WMS invokes this method if the command execution fails.

        class MySqlInstanceCommandContext : InstanceCommandContext
        {
            MySqlReceivedInstanceCommand command;
    
            internal MySqlInstanceCommandContext(MySqlReceivedInstanceCommand command)
            {
                this.command = command;
            }
    
            public override IAsyncResult BeginAbandon(Exception exception, TimeSpan timeout, AsyncCallback callback, object state)
            {
                throw new NotImplementedException();
            }
    
            public override IAsyncResult BeginComplete(TimeSpan timeout, AsyncCallback callback, object state)
            {
                throw new NotImplementedException();
            }
    
            public override void EndAbandon(IAsyncResult result)
            {
                throw new NotImplementedException();
            }
    
            public override void EndComplete(IAsyncResult result)
            {
                throw new NotImplementedException();
            }
        }
    

See Also

Concepts

How To: Develop an Instance Store Provider
How To: Develop an Instance Query Provider
How To: Configure Instance Store, Query, and Control Providers
Instance Store, Query, and Control Providers

  2012-09-12