Generic Root Object

Generic Root Object

Old forum URL: forums.lhotka.net/forums/t/10512.aspx


Randar posted on Monday, July 11, 2011

For various reasons, I am trying to create 3 base objects that all root, list and business objects inherit from.  So I would have:

For Business Objects: BusinessBase(Of T)<-CSLAComponentBase(Of T As BusinessBase(Of T))<-ConcreteBO
For Lists of Business Objects: BusinessBindingListBase(Of L, C)<-CSLAListBase(Of L As BusinessBindingListBase(Of L, C), C As CSLAComponentBase(Of C))<-ConcreateListOfBOs
For Root Business Object: BusinessBase(Of CSLARootBase(Of L, C))<-CSLARootBase(Of L As {CSLARootBase(Of L, C), New}, C As CSLAComponentBase(Of C))<-ConcreateRootBO

I have confirmed that when I want to load just a BO, it works.  But when I try to load the RootBO, it fails.
Exception: {"The type initializer for 'JonasNET.Master.CSLARootBase`2' threw an exception."}
Inner Exception: {"The type initializer for 'JonasNET.Master.CSLARootBase`2' threw an exception."}
Inner Inner Exception: {"Not a member access Parameter name: member"}

I get this when calling rootBO.MaintainList, which calls FieldManager.FieldExists(MaintainListProperty).  I've included all the important bits of the CSLARootBase.  If there are other bits anybody needs, I can add them to the post.

Public MustInherit Class CSLARootBase(Of L As {CSLARootBase(Of L, C), New}, C As CSLAComponentBase(Of C))
    Inherits BusinessBase(Of CSLARootBase(Of L, C))

    Public Sub New(Optional ByVal RootCompany As Long = Nothing)
        If (Not RootCompany = 0) Then
            CompanyId = RootCompany
        End If
    End Sub

    Public Shared CompanyIdProperty As PropertyInfo(Of Integer) = RegisterProperty(Of Integer)(Function(p) p.CompanyId)
    ''' <summary>
    ''' Gets or sets the company id.
    ''' </summary>
    ''' <value>
    ''' The company id.
    ''' </value>
    Public Property CompanyId() As Integer
        Get
            Return GetProperty(CompanyIdProperty)
        End Get
        Set(ByVal value As Integer)
            SetProperty(CompanyIdProperty, value)
        End Set
    End Property

    ''' <summary>
    ''' Gets the children.  Called from CSLA
    ''' </summary>
    '''
    Public ReadOnly Property Children() As SortedBindingList(Of C)
        Get
            Return New SortedBindingList(Of C)(MaintainList)
        End Get
    End Property

    Public Shared MaintainListProperty As PropertyInfo(Of L) = RegisterProperty(Of L)(Function(p) p.MaintainList, RelationshipTypes.LazyLoad)

    ''' <summary>
    ''' Gets the list of BOs which is accessed by the maintain controls.
    ''' </summary>
    Public Overridable Function MaintainList(Optional ByVal CompanyID As Long = Nothing, Optional ByVal SortColumn As String = Nothing, Optional ByVal StartRecord As Integer = 0, Optional ByVal MaxRecords As Integer = 0) As L
        ' TODO: Do something with all the optional parameters.  Add criteria
        If Not FieldManager.FieldExists(MaintainListProperty) Then
            LoadProperty(Of L)(MaintainListProperty, New L())
        End If
        Return GetProperty(Of L)(MaintainListProperty)
    End Function

End Class

Randar replied on Monday, July 11, 2011

So as an experiment, I actually changed my code so that it no longer used the Generic root object, just to see if it worked.  So now it (temporarily) looks like:

For Lists of Business Objects: BusinessBindingListBase(Of L, C)<-CSLAListBase(Of L As BusinessBindingListBase(Of L, C), C As CSLAComponentBase(Of C))<-ConcreateListOfBOs
For Root Business Object: BusinessBase(Of ConcreateRootBO)<-ConcreteRootBO.

I was surprised, but I actually got a similar exception.

Exception: {"DataPortal.Create failed (System.ArgumentException: Not a member access
Parameter name: member
   at Csla.Reflection.Reflect`1.GetMemberInfo(Expression member)
   at Csla.Reflection.Reflect`1.GetProperty(Expression`1 property)
   at Csla.BusinessBase`1.RegisterProperty[P](Expression`1 propertyLambdaExpression, RelationshipTypes relationship)
   at JonasNET.GL.BL.GLAccountTypeRoot..cctor() in C:\TFS\JonasNET-CSLA\JonasNET.GL.BL\Classes\GLAccountTypeRoot.vb:line 79)"}

Inner Exception: {"The type initializer for 'JonasNET.GL.BL.GLAccountTypeRoot' threw an exception."}

Inner Inner Exception: {"Not a member access
Parameter name: member"}

So I guess the error is not because it's generic, but something else.

JonnyBee replied on Saturday, July 16, 2011

You should always make sure to call the default constructor  (that in turn should call base class contructor).
This is vital to init the object like registering properties/business rules and initializing FieldDataManager.

    Public Sub New(Optional ByVal RootCompany As Long = Nothing)
        Me.New()  ' or MyBase.New()
        If (Not RootCompany = 0) Then
            CompanyId = RootCompany
        End If
    End Sub

 

Copyright (c) Marimer LLC