Dailycode.info

Short solution for short problems

Implement Auditing using MessageQueue and WCF

In my new application, I started to implement extensive auditing, for this was required by the business. After some thoughts and a course of WCF, I discoverd that MessageQueue combined with WCF is a perfect solution for auditing. Since auditing can have a lot of inserts, but doens't need a return, we can implement this as one way traffic. From client to server.

So I created a function that writed audit records that have following information: User, Message, Extra information (Original and changed values, or a guid to the record or ...) and a state. The state will make sure the audit records end up in the corresponding audit tables.

If you decorate the operation contract with the IsOneWay=true flag, you can use it for message queue. The service interface looks like this:

 

    [ServiceContract]
    public interface IAuditManager
    {
        [OperationContract(IsOneWay=true)]
        void Audit(AuditRecord auditRec);
    }
 

Then the implementation looks like this: 

public void Audit(LabCollect.ServerComponents.SharedClasses.AuditRecord auditRec)
{
    ColumnCollection columns = new ColumnCollection();
    columns.Add("Message", auditRec.Message);
    columns.Add("EXTRAINFO", auditRec.ExtraInfo);
    columns.Add("TIME_STAMP", auditRec.TimeStamp);
 
    GLPDA.Insert("LA_AUD_"+auditRec.State, columns, _dbString, auditRec.User);
}

The last update is automatically logged by the server. This can be different then the time stamp of the audit record, because in message queue the server time is not always the same time as the record was created by the client.

I used a channelfactory on the client to address the WCF service. I'm not using transactions for writing single audit records. You can initiate the AuditManager like this: 

string auditEndPoint = "net.msmq://localhost/private/LabCollectAudit";
NetMsmqBinding
bindings = new NetMsmqBinding();

 

bindings.ExactlyOnce=false;
_auditMgrProxy = ChannelFactory<IAuditManager>.CreateChannel(bindings, new EndpointAddress(auditEndPoint));

The ExactlyOnce property needs to be set to false when you are not using transactions. So All that is left is to configure the endpoints on the host, which is very simple. By example I show you how to do it using the configuration file:

<system.serviceModel>
    <services>
      <service behaviorConfiguration="LabCollectWCFAuditService.Service1Behavior"
        name="LabCollectWCFAuditService.AuditManager">
        <endpoint address="net.msmq://localhost/private/LabCollectAudit" binding="netMsmqBinding" 
contract="LabCollectWCFAuditService.IAuditManager" bindingConfiguration="MSQueueBinding">
          <identity>
            <dns value="localhost" />
          </identity>
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:7100/LabCollectWCFAuditService/AuditManager/" />
          </baseAddresses>
        </host>
      </service>
    </services>
    <bindings>
      <netMsmqBinding>
        <binding name="MSQueueBinding" exactlyOnce="false"></binding>
      </netMsmqBinding>
    </bindings>
    <behaviors>
      <serviceBehaviors>
        <behavior name="LabCollectWCFAuditService.Service1Behavior">
          <!-- To avoid disclosing metadata information, 
          set the value below to false and remove the metadata endpoint above before deployment -->
          <serviceMetadata httpGetEnabled="True"/>
          <!-- To receive exception details in faults for debugging purposes, 
          set the value below to true.  Set to false before deployment 
          to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="True" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>

Do not forget to create the Message queue on your machine or server. When you insert records using the clients and make sure the services on the "server-side" are not running, then you can see them in your mesage queue: