![]() |
Web Development »
Web Services »
General
Intermediate
License: The Code Project Open License (CPOL)
Using the Web Services and COM+ Event System in the .Net Application.By Roman KissThis article describes how to subscribe the Web Service into the COM+ Event System using the C# language. |
C#, VC7, Windows, .NET 1.0, Dev
|
|
Advanced Search |
|
|
|
||||||||||||||||
The Web Services are a url-address driven resources to perform a specific application service. In the logical business model the Web services represent reusable components - processors to obtain or change the distributed business state. The application business behavior can be notified using a loosely coupled event (LCE) system supported by COM+ services. It's based on the Publisher/Subscriber design pattern. This article describes how to integrate the Web Services into the COM+ Event System in the .Net Application using the C# language. I am using a simple example of the Web Services in the Publisher/Subscriber scenario, which it will publish the Log Message to the subscriber.
The concept of the LCE Web Service is based on using the Web Service proxy as a subscriber to the Event System. The LCE system during the run-time is driven by the abstract (meta class) of the Event Class and data of the Subscriptions (which it represents a logical connectivity) stored in the Event System. From the application model viewpoint they are handled by the Publisher respectively Subscriber component. The Web Services are integral part of the .Net Framework with well-defined interfaces (contracts) that describe services provided. In this sample I am using two Web Services:

This scenario of the "fire & forget" event is very straightforward and full transparently between the Web Services and their consumers. In the .Net Application this "plumbing" is done using the Web Service Proxy on the consumer side. The Web Method is mapped back and forth between them using the SOAP technologies on the http communication channel. In the about design we can see two Web Service consumers:
The implementation of the LCE system using Web services is based on the loosely coupled design pattern contracted by the Event System interfaces and Event Meta class. This abstract is built into the common assembly and installed into the Global Assembly Cache. The solution has the following assemblies:
EventClassLogMsg, common abstract definitions (interfaces and meta class) WebServiceFireEvent, publisher web service SubscriberLogMsg, subscriber web service proxy WebServiceLogMsg, subscriber web service This assembly contains all abstract definitions useful for publisher and subscriber components. Note that assembly is used also to create a COM+ application included the Event Class wrapper - component to interoperate with unmanaged code, that's why the class is derived from the ServicedComponent and attributed with EventClassAttribute. It's a very elegant solution given by the .Net Framework architecture to access the Event Class from either an unmanaged or managed code.
using System;
using System.Diagnostics;
using System.EnterpriseServices;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Security.Permissions;
using System.Threading;
//
using COMPlusEventSystem;
[assembly: ApplicationName("EventClassLogMsg")]
[assembly: ApplicationActivation(ActivationOption.Server)]
[assembly: ApplicationAccessControl(Value = false,
Authentication = AuthenticationOption.None)]
#region COM+ Event System Interfaces
namespace COMPlusEventSystem
{
//--------------------------< EventSystem >---------------------------------
//
[ComImport, Guid("4E14FBA2-2E22-11D1-9964-00C04FBBB345")]
public class EventSystem {}
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch),
Guid("4E14FB9F-2E22-11D1-9964-00C04FBBB345")]
public interface IEventSystem
{
[PreserveSig]
object Query([In, MarshalAs(UnmanagedType.BStr)] string progId,
[In, MarshalAs(UnmanagedType.BStr)] string queryCriteria,
[Out] out Int32 errorIndex);
[PreserveSig]
void Store([In, MarshalAs(UnmanagedType.BStr)] string progId,
[In, MarshalAs(UnmanagedType.Interface)] object pInterface);
[PreserveSig]
void Remove([In, MarshalAs(UnmanagedType.BStr)] string progId,
[In, MarshalAs(UnmanagedType.BStr)] string queryCriteria,
[Out] out Int32 errorIndex);
[PreserveSig]
string get_EventObjectChangeEventClassID();
[PreserveSig]
object QueryS([In, MarshalAs(UnmanagedType.BStr)] string progId,
[Out] out Int32 errorIndex);
[PreserveSig]
void RemoveS([In, MarshalAs(UnmanagedType.BStr)] string progId,
[Out] out Int32 errorIndex);
}
//
[ComImport, Guid("7542E960-79C7-11D1-88F9-0080C7D771BF")]
public class EventSubcription {}
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch),
Guid("4A6B0E15-2E38-11D1-9965-00C04FBBB345")]
public interface IEventSubcription
{
string SubscriptionID { get; set; }
string SubscriptionName { get; set; }
string PublisherID { get; set; }
string EventClassID { get; set; }
string MethodName { get; set; }
string SubscriberCLSID { get; set; }
object SubscriberInterface { get; set; }
bool PerUser { get; set; }
string OwnerSID { get; set; }
bool Enabled { get; set; }
string Description { get; set; }
string MachineName { get; set; }
[PreserveSig]
object GetPublisherProperty([In, MarshalAs(UnmanagedType.BStr)] string PropertyName);
[PreserveSig]
void PutPublisherProperty([In, MarshalAs(UnmanagedType.BStr)] string PropertyName,
ref object propertyValue);
[PreserveSig]
void RemovePublisherProperty([In, MarshalAs(UnmanagedType.BStr)] string PropertyName);
[PreserveSig]
object GetPublisherPropertyCollection();
[PreserveSig]
object GetSubscriberProperty([In, MarshalAs(UnmanagedType.BStr)] string PropertyName);
[PreserveSig]
void PutSubscriberProperty([In, MarshalAs(UnmanagedType.BStr)] string PropertyName,
ref object propertyValue);
[PreserveSig]
void RemoveSubscriberProperty([In, MarshalAs(UnmanagedType.BStr)] string PropertyName);
[PreserveSig]
object GetSubscriberPropertyCollection();
string InterfaceID { get; set; }
}
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch),
Guid("4A6B0E16-2E38-11D1-9965-00C04FBBB345")]
public interface IEventSubcription2 : IEventSubcription
{
string FilterCriteria { get; set; }
string SubscriberMoniker { get; set; }
}
}
#endregion
namespace EventClassLogMsg
{
[Guid("32F5CB45-F0CB-41e2-919E-54D884905F13")]
public interface ILogMsg
{
void WriteLogMsg(string ticket, string msg);
}
// meta class
[Guid("5AA8C91D-EA0F-405a-8840-95344038AF5A")]
[EventClass]
[Transaction(TransactionOption.Disabled)]
[ObjectPooling(Enabled=true, MinPoolSize=10, MaxPoolSize=60)]
[EventTrackingEnabled]
public class EventClassLogMsg : ServicedComponent, ILogMsg
{
public void WriteLogMsg(string ticket, string msg){}
}
}
The Publisher is the Web Service created by the ASP.Net Web Service template. There is only one simple Web Method to fire the Event Class' method.
The method can be tested using the http://localhost/WebServiceFireEvent/Service.asmx?op=FireEvent page.
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Web;
using System.Web.Services;
using System.Runtime.InteropServices;
using System.Reflection;
using System.EnterpriseServices;
//
using EventClassLogMsg;
namespace WebServiceFireEvent
{
/// <summary>
/// Summary description for Service.
/// </summary>
public class Service : System.Web.Services.WebService
{
public Service()
{
//CODEGEN: This call is required by the ASP.NET Web Services Designer
InitializeComponent();
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
}
#endregion
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
}
[WebMethod]
public string FireEvent(string ticket, string msg)
{
int tsStart = Environment.TickCount;
using (EventClassLogMsg.EventClassLogMsg ec = new EventClassLogMsg.EventClassLogMsg())
{
ec.WriteLogMsg(ticket, msg);
}
long tsEnd = Environment.TickCount - tsStart;
Trace.WriteLine(string.Format("The LogMsg [{0}] has been published [{1}ms]",
ticket, tsEnd));
return ticket;
}
}
}
The Subscriber side is divided into two parts: Web Service a Web Service Proxy.
The Web Service is created by the ASP.Net Web Service template with the same way as the about Publisher. There is only one simple Web Method - WriteLogMsg, which has to have the same method signature as the Event Class (it simplified the SOAP formatting). We don't have any business for this service, just only to notify this action. In real product, this service might have an implementation of the business such as log messages into database, monitoring messages, tuning application knowledge base, etc. The method can be tested using the http://localhost/WebServiceLogMsg/Service.asmx?op=WriteLogMsg page.
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Web;
using System.Web.Services;
using System.Threading;
namespace WebServiceLogMsg
{
/// <summary>
/// Summary description for Service1.
/// </summary>
public class Service : System.Web.Services.WebService
{
public Service()
{
//CODEGEN: This call is required by the ASP.NET Web Services Designer
InitializeComponent();
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
}
#endregion
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
}
[WebMethod]
public void WriteLogMsg(string ticket, string msg)
{
// to do:
Trace.WriteLine(string.Format("[{0}] WebServiceLogMsg.WriteLogMsg({1}, {2})",
this.GetHashCode(), ticket, msg));
}
}
}
The Web Service Proxy is the key part of this solution. Its implementation covers the following features:
SoapHttpClientProtocol (wrapper to the Web Service) ILogMsg and interfaces of the poolable object design pattern such as IObjectControl and IObjectConstruct. WriteLogMsg has the same signature as the requested Web Method "queue:/new:RKiss.SubscriberLogMsg.Service" to initiate this proxy class. using System.Diagnostics;
using System.Xml.Serialization;
using System;
using System.Web.Services.Protocols;
using System.Web.Services;
using System.Runtime.InteropServices;
using System.EnterpriseServices;
using System.Reflection;
using COMPlusEventSystem;
using RKiss.SubscriberLogMsgInterfaces;
namespace RKiss.SubscriberLogMsgInterfaces
{
#region Interfaces: ILogMsg, IObjectControl, IObjectConstruct, IObjectConstructString
// The Interface of the EventClass
[Guid("32F5CB45-F0CB-41e2-919E-54D884905F13")]
[InterfaceQueuing]
public interface ILogMsg
{
void WriteLogMsg(string ticket, string msg);
}
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
Guid("51372aec-cae7-11cf-be81-00aa00a2fa25")]
public interface IObjectControl
{
[PreserveSig]
void Activate();
[PreserveSig]
void Deactivate();
[PreserveSig]
bool CanBePooled();
}
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
Guid("41C4F8B3-7439-11D2-98CB-00C04F8EE1C4")]
public interface IObjectConstruct
{
[PreserveSig]
void Construct([In, MarshalAs(UnmanagedType.IDispatch)] object pCtorObj);
}
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch),
Guid("41C4F8B2-7439-11D2-98CB-00C04F8EE1C4")]
public interface IObjectConstructString
{
string ConstructString { get; }
}
#endregion
}
namespace RKiss.SubscriberLogMsg {
[WebServiceBinding(Name="ServiceSoap", Namespace="http://tempuri.org/")]
public class Service : SoapHttpClientProtocol, ILogMsg, IObjectControl, IObjectConstruct
{
// Subscription ID
static string guidSub = "{0150BA01-1CF4-4431-A775-A30353E7BD20}";
[DebuggerStepThrough()]
public Service()
{
this.Url = "http://localhost/WebServiceLogMsg/Service.asmx";
}
[ComRegisterFunction]
public static void RegisterSubscription(Type t)
{
IEventSystem es = new EventSystem() as IEventSystem;
IEventSubcription2 sub = new EventSubcription() as IEventSubcription2;
sub.Description = "Subscription for WebServiceLogMsg";
sub.SubscriptionName = "WebServiceLogMsg";
sub.SubscriptionID = guidSub;
sub.Enabled = true;
sub.EventClassID = "{" +
Type.GetTypeFromProgID("EventClassLogMsg.EventClassLogMsg").GUID.ToString() + "}";
sub.MethodName = "WriteLogMsg";
sub.SubscriberMoniker = @"queue:/new:RKiss.SubscriberLogMsg.Service";
es.Store("EventSystem.EventSubscription", sub);
Trace.WriteLine(string.Format("Subscription {0} has been registered", guidSub));
}
[ComUnregisterFunction]
public static void UnregisterSubscription(Type t)
{
int errorIndex = 0;
IEventSystem es = new EventSystem() as IEventSystem;
string strCriteria = "SubscriptionID=" + guidSub;
es.Remove("EventSystem.EventSubscription", strCriteria, out errorIndex);
Trace.WriteLine(string.Format("Subscription {0} has been unregistered", guidSub));
}
//IEventLog
[DebuggerStepThrough()]
[SoapDocumentMethod("http://tempuri.org/WriteLogMsg",
Use=System.Web.Services.Description.SoapBindingUse.Literal,
ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
public void WriteLogMsg(string ticket, string msg)
{
try
{
this.Invoke("WriteLogMsg", new object[] { ticket, msg});
}
catch(Exception ex)
{
Trace.WriteLine(string.Format("[{0}] SubscriberLogMsg.WriteLogMsg catch Err = {1}",
this.GetHashCode(), ex.Message));
throw ex;
}
}
//IObjectConstruct
public void Construct(object pCtorObj)
{
string constr = (pCtorObj as IObjectConstructString).ConstructString;
this.Url = constr;
Trace.WriteLine(string.Format("[{0}] SubscriberLogMsg.Construct = {1}", this.GetHashCode(),
constr));
}
//IObjectControl
public void Activate()
{
Trace.WriteLine(string.Format("[{0}] SubscriberLogMsg.Activate", this.GetHashCode()));
}
public void Deactivate()
{
Trace.WriteLine(string.Format("[{0}] SubscriberLogMsg.Deactive", this.GetHashCode()));
}
public bool CanBePooled()
{
Trace.WriteLine(string.Format("[{0}] SubscriberLogMsg.CanBePooled", this.GetHashCode()));
return true;
}
}
}
The proxy class can be derived only from a single class SoapHttpClientProtocol and there is no way to attribute the class to perform its registry in the COM+ catalog (it requested to use the ServiceComponent class), so we have to do it manually using the following steps:
WebServiceSubscriber. regasm.exe tool to registry proxy as a COM component and generating its typelib file: c:\regasm SubscriberLogMsg.dll /tlb:SubscriberLogMsg.tlb. Note that the Event Class component has to be installed in prior (see the proxy static method RegisterSubscription implementation). SubsciberLogMsg.tlb into the WebServiceSubscriber COM+ application http://localhost/WebServiceLogMsg/Service.asmx then follow the next step otherwise skip it. ILogMsg interface The LCE Web Services can be simple tested using the Publisher Web Service test page and DebugView utility from http://www.sysinternals.com. Before this test be sure that:
10. This means, that components have been constructed properly and they are ready to work. Now, we can perform the test as is shown in the following picture:

Each time when you click the Invoke button (with some parameters) on the Publisher page you can see the Trace messages on the DebugView screen. You can play more with the Publisher and Subscriber to verify capability of the loosely coupled design pattern and asynch processing in the local, over Internet and mix environments.
In this example is shown how simple can be the Web Services integrated into the Event System using the LCE design pattern. The .Net Framework makes an easy implementation of the Web Services - Publisher/Subscriber skeleton. Using the LCE Web Services in the distributed event driven architecture is full transparent to the application components, which they can be developed incrementally and independent.
| You must Sign In to use this message board. | |||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||
General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 11 Oct 2001 Editor: Sean Ewington |
Copyright 2001 by Roman Kiss Everything else Copyright © CodeProject, 1999-2009 Web20 | Advertise on the Code Project |