-----------------------------------------------------------

New Message on BDOTNET

-----------------------------------------------------------
From: SitaramanM
Message 8 in Discussion

Hi   Thts quite and interesting problem. Have given two solutiions below, but thought 
i might as well describe how i reached the solutions.   a) I basically created a 
singleton class as i wrote in my previous posr that is, with a private constructor and 
a public Shared ReadOnly Object. Works fine in a normal mode b) Tried installing it in 
COM+ c) As u said, i got an error, as We cannot use  private contructor if it is to be 
installed in COM+ d) I made the constructor public e) But now the prob is that the 
outside world can instantiate this object as the constructor is public.  But This 
should not be the case in a singlton right f) So in the public constructor, i just 
threw a NotSupported Exception like this <Transaction(TransactionOption.Required)> 
Public Class Numbering
    Inherits ServicedComponent     Public Shared ReadOnly Instance As Numbering = New 
Numbering
    Public Sub New()
            Throw New NotSupportedException("Direct Instantiation Not Supported. Use 
Instance method to get an Instance of this class")
    End Sub g) Was a stupid attempt as, Now if someone instantiates the object of this 
class  then he will get an exception , which is fine and required, but at the same 
time even if the Shared member variable is internally initialized then it will throw 
exception only   h) tried to be smart and made a patch.  Added a private constructor 
which takes an int as parameter and then initialized my Shared Reradonly Instance 
variable with that constructor by passing a int parameter. Outside world will use the 
public constructor and get an exception, but i will be able to internally initialize 
it with the overloaded constructor. Code is as follows  
<Transaction(TransactionOption.Required)> Public Class Numbering
    Inherits ServicedComponent     Public Shared ReadOnly Instance As Numbering = New 
Numbering(1)
    Public Sub New()
            Throw New NotSupportedException("Direct Instantiation Not Supported. Use 
Instance method to get an Instance of this class")
    End Sub     Private sub New(p_intDummy as integer)         'Dummy     end sub i) 
MS turned out to be smarter.  Got a runtime exception saying that Serviced Components 
cannot have parameterized constructor calls :(   Solution 1 Took a couple of minutes 
and thought it over.  Basically the key is the way the Shared variables are 
initialized.  In .Net when u declare a Shared member variable,  the compiler adds a 
Type Initializer(also called Type Constructor or Class Constructor, is compiled to a 
function called .cctor). The runtime will call this Class Constructor internally. When 
it is called varied, but one thing is that it will DEFINITELY be called before the 
variable is used.   This is the key here.  So if a cll to initialize the object is 
issued,  first the .cctor(class constructor) WILL be called which will initialize th 
Shared Variable.  Only then is the .ctor(Constructor/public sub New) will be called.  
So a small trick as below will do the job Public Sub New()
        If Not Instance Is Nothing Then
            Throw New NotSupportedException("Direct Instantiation Not Supported. Use 
Instance method to get an Instance of this class")
        End If
    End Sub ;).   Note that the client issues a call to instantiate the object.  
Runtime will supersede it and instead of calling New it will call the .cctor, which 
will initialize the Instance variable. So my singleton is initialized.  then it will 
call the New(.ctor).  In the New im, checking the Instance variable.  As it is already 
initialized it will throw the Not Supported Exception.   So Noone can instantiate this 
object and can access only the Shared Readonly variable, which is what u wanted :)   
Soultion2 This is for the more adventrous ones. Note that afterStep h), my main 
requirement is that the local Shared variable should be initialized, but any outside 
call to initialize should be rejected.  Right!!!  So what i need to know in New is 
that who is instantiating the object or to be precise,  what is the method which 
called the new.  if new was called due to the local shared readonly variable 
initialization then i should allow it else i should throw the exception. Right.   
Enter StackFrame Class.  In the New i traverse thru the calling function backwards and 
keep checking whether the called function in the chain is .cctor.  If so then i know 
tht it is the type initializer which is initializing.  Now I also need to know whether 
it is the type initializer of this class only or not(as it is possible that you can 
initialize the object from the type initializer of a different object of your own and 
hack the code).  This i find out by doing a DeclaringType ofthe .cctor method. If it 
is the same as current class, then i can be SURE that this new resulted from the 
Shared Readonly variable initialization,which is  OK, so i do an exit sub. If i do not 
get a .cctor in the method call chain (or) i do get a .cctor, but the declaring type 
is different, then i throw the exception. Tricky byut works   Public Sub New()
        Dim iCtr As Integer = 1         While True             Dim sf As StackFrame = 
New StackFrame(iCtr)
            Dim l_objCallingMethod As MethodBase = sf.GetMethod()             If 
l_objCallingMethod Is Nothing Then
                Exit While
            Else
                If l_objCallingMethod.Name = ".cctor" Then
                    If l_objCallingMethod.DeclaringType.ToString = Me.GetType.ToString 
Then
                        Exit Sub
                    End If
                End If
            End If
            iCtr = iCtr + 1
        End While
        Throw New NotSupportedException("Direct Instantiation Not Supported. Use 
Instance method to get an Instance of this class")   Need to test more, but basically 
you can circumvent the limitation of COM Server.  So this is an approach tht u can 
follow.  The complete code for the Solution1(Numbering.vb) and 
Solution2(Numbering2.vb) is as follows. Check it out and do get back in case of any 
problems.      Imports System.Diagnostics
Imports System.Reflection
Imports System.Threading
Imports System.EnterpriseServices <Transaction(TransactionOption.Required)> Public 
Class Numbering
    Inherits ServicedComponent     Public Shared ReadOnly Instance As Numbering = New 
Numbering
    Private m_intCtr As Integer
    Private m_objLockObject As New Object
    Public Sub New()
        If Not Instance Is Nothing Then
            Throw New NotSupportedException("Direct Instantiation Not Supported. Use 
Instance method to get an Instance of this class")
        End If
    End Sub
    Public ReadOnly Property Counter() As Integer
        Get
            While True
                If Now.Hour = 16 AndAlso Now.Minute = 0 Then
                    Exit While
                End If
                Thread.Sleep(100)
            End While
            SyncLock (m_objLockObject)
                m_intCtr = m_intCtr + 1
            End SyncLock
            Return m_intCtr
        End Get
    End Property
End Class   <Transaction(TransactionOption.Required)> Public Class Numbering2
    Inherits ServicedComponent     Public Shared ReadOnly Instance As Numbering2 = New 
Numbering2
    Private m_intCtr As Integer
    Private m_objLockObject As New Object 
    Public Sub New()
        Dim iCtr As Integer = 1         While True             Dim sf As StackFrame = 
New StackFrame(iCtr)
            Dim l_objCallingMethod As MethodBase = sf.GetMethod()             If 
l_objCallingMethod Is Nothing Then
                Exit While
            Else
                If l_objCallingMethod.Name = ".cctor" Then
                    If l_objCallingMethod.DeclaringType.ToString = Me.GetType.ToString 
Then
                        Exit Sub
                    End If
                End If
            End If
            iCtr = iCtr + 1
        End While
        Throw New NotSupportedException("Direct Instantiation Not Supported. Use 
Instance method to get an Instance of this class")
    End Sub 
    Public ReadOnly Property Counter() As Integer
        Get
            While True
                If Now.Hour = 16 AndAlso Now.Minute = 0 Then
                    Exit While
                End If
                Thread.Sleep(100)
            End While
            SyncLock (m_objLockObject)
                m_intCtr = m_intCtr + 1
            End SyncLock
            Return m_intCtr
        End Get
    End Property
End Class   Would appreciate any comments from the group(Spark/Raj/Gaurav/Mahesh!!!)   
  regards,   sr   p.s. you can probably even try out Mahesh's article/post which talks 
of getting the COM+ functionality wihout registering it on the Server!!!!

-----------------------------------------------------------

To stop getting this e-mail, or change how often it arrives, go to your E-mail 
Settings.
http://groups.msn.com/BDotNet/_emailsettings.msnw

Need help? If you've forgotten your password, please go to Passport Member Services.
http://groups.msn.com/_passportredir.msnw?ppmprop=help

For other questions or feedback, go to our Contact Us page.
http://groups.msn.com/contact

If you do not want to receive future e-mail from this MSN group, or if you received 
this message by mistake, please click the "Remove" link below. On the pre-addressed 
e-mail message that opens, simply click "Send". Your e-mail address will be deleted 
from this group's mailing list.
mailto:[EMAIL PROTECTED]

Reply via email to