DataDynamics Report/Analysis Suite

DataDynamics Report/Analysis Suite

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


Jack posted on Saturday, October 10, 2009

Has anybody used CSLA with the DataDynamic's products?  If so how was the experience?  Any tips?  Any recommendations or pitfalls.

Thanks

jack

JoeFallon1 replied on Monday, October 12, 2009

In my web app I use DataDynamics Active Reports with CSLA.

It works great! I designed a framework for my reports where a ReadOnly BO is always fetched for header data and a ROC is used for the detail data. Then I can set the report datasource to this BO and have it run until the collection has reached its last record.

As I recall the main issue was that the BO had to already be in sorted order. The report designer/engine would not sort it for you.

Each report then had to fetch its own BO and since they had a common interface, the reports all ran with very little additional code outside the base classes of my framework.

Joe

 

Jack replied on Tuesday, October 13, 2009

So you put all the parameter, title, desc, etc. logic in the ROB and then it has a basically lazy loaded ROC property with the data?

 

Do you have anything that is easy to share or pm me?  Only if it is simple for you.

 

Thanks

 

jack

 

 

From: JoeFallon1 [mailto:cslanet@lhotka.net]
Sent: October-12-09 10:15 AM
To: jaddington@alexandergracie.com
Subject: Re: [CSLA .NET] DataDynamics Report/Analysis Suite

 

In my web app I use DataDynamics Active Reports with CSLA.

It works great! I designed a framework for my reports where a ReadOnly BO is always fetched for header data and a ROC is used for the detail data. Then I can set the report datasource to this BO and have it run until the collection has reached its last record.

As I recall the main issue was that the BO had to already be in sorted order. The report designer/engine would not sort it for you.

Each report then had to fetch its own BO and since they had a common interface, the reports all ran with very little additional code outside the base classes of my framework.

Joe

 



JoeFallon1 replied on Wednesday, October 14, 2009

You have the basic idea. I have a base class that does most of the work for each report.

Some base class variables are:

Protected mIndex As Integer
Protected mReadOnlyBO As IPNIReadOnlyObject
Protected mROC As IReadOnlyList
Protected flags As BindingFlags = BindingFlags.Public Or BindingFlags.IgnoreCase Or BindingFlags.Instance
Protected mProps As List(Of PropertyInfo)
Protected mRptTitle As String = ""
Protected mRptCode As String = ""
Protected mFromTo As String = ""
Protected mDateStart As String = ""
Protected mDateEnd As String = ""

Some methods include:

Public Overridable Function GetReadOnlyBO() As IMyReadOnlyObject
  Dim mReportHdrRO As ReportHdrRO
  mReportHdrRO = ReportHdrRO.NewReportHdrRO
  mReportHdrRO.RptCode =
Me.RptCode
  mReportHdrRO.RptTitle =
Me.RptTitle
  mReportHdrRO.FromTo =
Me.FromTo
  Return mReportHdrRO
End Function

Public Overridable Function GetROC() As IReadOnlyList
  Return Nothing
End Function

Protected Overridable Function GetSQLStatement() As String
 
Return ""
End Function

Each report then overrides GetROC and GetSQLStatement to gather up the data for that report.

I use reflection to match the text box names to the BO property values with the assumption that txtPropertyName is how each text box is named.

Public Overridable Function GetDetailFieldList() As List(Of String)
Dim result As New List(Of String)
Dim txtBoxName As String
Dim BOPropName As String
Dim currentTextBox As DataDynamics.ActiveReports.TextBox
Dim rptProp As PropertyInfo

'Only find the TextBox members in the Report. Assumes they all start with "txt".
Dim rptMembers As MemberInfo() = Me.GetType.FindMembers(MemberTypes.Property, flags Or BindingFlags.NonPublic, Type.FilterNameIgnoreCase, "txt*")

For Each info As MemberInfo In rptMembers
'Since we know that we found only MemberTypes.Property we can safely cast the MemberInfo to a PropertyInfo.
rptProp = CType(info, PropertyInfo)
'verify that we are in fact dealing with a TextBox and not a mis-named control.
If rptProp.PropertyType.Name = "TextBox" Then
 
currentTextBox = CType(rptProp.GetValue(Me, Nothing), DataDynamics.ActiveReports.TextBox)
 
'find all text boxes that are NOT in the page header or page footer.
 
If Not (currentTextBox.Parent.GetType Is GetType(DataDynamics.ActiveReports.PageHeader) OrElse currentTextBox.Parent.GetType Is GetType(DataDynamics.ActiveReports.PageFooter)) Then
 
txtBoxName = rptProp.Name
'assumes all TextBox controls have a 3 letter prefix of txt and that the rest of the Name is the same as the BO Property. Case sensitive!
 
BOPropName = txtBoxName.Substring(3)
  result.Add(BOPropName)
End If
End If
Next
Return result
End Function

Private ReadOnly Property Props() As List(Of PropertyInfo)
Get
If mProps Is Nothing Then
mProps = New List(Of PropertyInfo)
Dim infoType As Type = mROC.GetType().GetProperty("Item", New Type() {GetType(Integer)}).PropertyType
'get an array of all Properties of the ROC (could be 50 or more)
Dim tempProps() As PropertyInfo = infoType.GetProperties(flags)
'get the list of fields in the specific report detail section. (typically, can be less than 10)
Dim detailFields As List(Of String) = GetDetailFieldList()
If detailFields Is Nothing Then
'if the developer does not provide a list of fields then add all Properties to mProps.
For Each prop As PropertyInfo In tempProps
mProps.Add(prop)
Next
Else
For Each prop As PropertyInfo In tempProps
'This is case sensitive. So the BO Property names must be spelled the same way in the GetDetailFieldList method.
If detailFields.Contains(prop.Name) Then
mProps.Add(prop)
End If
Next
End If
End If
Return mProps
End Get
End Property

Then write code for each of the Active Report methods:

#Region " Active Reports Methods "

Protected Sub rpt_ReportStart(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.ReportStart
mIndex = 0
mHoldString =
String.Empty
mFirstLine =
True
'passes criteria values from speciality ReportBaseFrame to ReportHeaderRO
'for data binding to Page Header/Footer bands
mReadOnlyBO = GetReadOnlyBO()
'to populate ROC using SQL Statement
mROC = GetROC()
End Sub

'set txtTextBox.Value for all sections except the Detail section which is set in rpt_FetchData.
Protected Sub rpt_DataInitialize(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.DataInitialize
Dim txtBoxName As String
Dim BOPropName As String
Dim BOType As Type = mReadOnlyBO.GetType
Dim currentTextBox As DataDynamics.ActiveReports.TextBox
Dim rptProp As PropertyInfo
'Only find the TextBox members in the Report. Assumes they all start with "txt".
Dim rptMembers As MemberInfo() = Me.GetType.FindMembers(MemberTypes.Property, flags Or BindingFlags.NonPublic, Type.FilterNameIgnoreCase, "txt*")

For Each info As MemberInfo In rptMembers
'Since we know that we found only MemberTypes.Property we can safely cast the MemberInfo to a PropertyInfo.
rptProp = CType(info, PropertyInfo)
'verify that we are in fact dealing with a TextBox and not a mis-named control.
If rptProp.PropertyType.Name = "TextBox" Then
currentTextBox = CType(rptProp.GetValue(Me, Nothing), DataDynamics.ActiveReports.TextBox)
'set currentTextBox.Value for PageHeader and PageFooter only.

'If currentTextBox.Parent.GetType IsNot GetType(DataDynamics.ActiveReports.Detail) Then
If (currentTextBox.Parent.GetType Is GetType(DataDynamics.ActiveReports.PageHeader) OrElse currentTextBox.Parent.GetType Is GetType(DataDynamics.ActiveReports.PageFooter)) Then
txtBoxName = rptProp.Name
'assumes all TextBox controls have a 3 letter prefix of txt and that the rest of the Name is the same as the BO Property.
BOPropName = txtBoxName.Substring(3)
Dim BOprop As PropertyInfo = BOType.GetProperty(BOPropName, flags)
If BOprop IsNot Nothing Then
'the BO and the TextBox w/o the prefix are the same name so set TextBox.Value = BO.Property.
currentTextBox.Value = BOprop.GetValue(mReadOnlyBO, Nothing)
End If
End If
End If
Next
'see if there is a Picture in the report Header. If there is, set the Image property to the BO.Logo value
'which is a System.Drawing.Image created from an array of Bytes.
'Assumes the name of the control has a prefix of "img".
rptMembers = Me.GetType.FindMembers(MemberTypes.Property, flags Or BindingFlags.NonPublic, Type.FilterNameIgnoreCase, "img*")

For Each info As MemberInfo In rptMembers
rptProp =
CType(info, PropertyInfo)
'verify that we are in fact dealing with a Picture and not a mis-named control.
If rptProp.PropertyType.Name = "Picture" Then
Dim currentControl As DataDynamics.ActiveReports.Picture = CType(rptProp.GetValue(Me, Nothing), DataDynamics.ActiveReports.Picture)

'set currentControl.Image for all sections except the Detail section which is set in rpt_FetchData.
If currentControl.Parent.GetType IsNot GetType(DataDynamics.ActiveReports.Detail) Then
txtBoxName = rptProp.Name
'assumes all Picture controls have a 3 letter prefix of img and that the rest of the Name is the same as the BO Property.

'In this case the name is imgLogo.
BOPropName = txtBoxName.Substring(3)
Dim BOprop As PropertyInfo = BOType.GetProperty(BOPropName, flags)
If BOprop IsNot Nothing Then
'the BO and the Picture control w/o the prefix are the same name so set Control.Image = BO.Property.
currentControl.Image = CType(BOprop.GetValue(mReadOnlyBO, Nothing), Drawing.Image)
End If
End If
End If
Next

'define Detail fields collection
For Each prop As PropertyInfo In Props
  Me.Fields.Add(prop.Name)
Next
End Sub

'Add data to the Fields in the Detail section using 1 row at a time from the ROC.
'mIndex is incremented each time FetchData runs.
'This will fetch the next row from the ROC.
'It will run until we set eArgs.EOF = True.
Protected Sub rpt_FetchData(ByVal sender As Object, ByVal eArgs As DataDynamics.ActiveReports.ActiveReport3.FetchEventArgs) Handles Me.FetchData

Dim target As Csla.Core.IReadOnlyObject
Dim holdValue As String = String.Empty
If mIndex = mROC.Count Then
'stops firing this event
eArgs.EOF = True
Else
target = mROC.Item(mIndex, True)
For Each prop As PropertyInfo In Props
 
Me.Fields(prop.Name).Value = prop.GetValue(target, Nothing)
Next

'increment the index for the ROC.
mIndex += 1
'continue to fire rpt_FetchData event
eArgs.EOF = False
End If
End Sub
#End
Region

Joe

Jack replied on Thursday, October 15, 2009

Joe

 

Thank you very much.  One additional question ... was that a pain for testing the formatting / outline of the report?  Without a direct database connection how would you preview the report while design it?  I could see with a simple grid report it wouldn't be a big deal but if you had a large report with grouping and nested reports etc. it would be cumbersome to deploy/run/test over and over???

 

Thanks

 

jack

From: JoeFallon1 [mailto:cslanet@lhotka.net]
Sent: October-14-09 8:03 AM
To: jaddington@alexandergracie.com
Subject: Re: [CSLA .NET] RE: DataDynamics Report/Analysis Suite

 

You have the basic idea. I have a base class that does most of the work for each report.

Some base class variables are:

Protected mIndex As Integer
Protected
mReadOnlyBO As IPNIReadOnlyObject
Protected mROC As IReadOnlyList
Protected flags As BindingFlags = BindingFlags.Public Or BindingFlags.IgnoreCase Or BindingFlags.Instance
Protected mProps As List(Of PropertyInfo)
Protected mRptTitle As String = ""
Protected mRptCode As String = ""
Protected mFromTo As String = ""
Protected mDateStart As String = ""
Protected mDateEnd As String = ""

Some methods include:

Public Overridable Function GetReadOnlyBO() As IMyReadOnlyObject
  Dim mReportHdrRO As ReportHdrRO
  mReportHdrRO = ReportHdrRO.NewReportHdrRO
  mReportHdrRO.RptCode = Me.RptCode
  mReportHdrRO.RptTitle = Me.RptTitle
  mReportHdrRO.FromTo = Me.FromTo
  Return mReportHdrRO
End Function

Public Overridable Function GetROC() As IReadOnlyList
  Return Nothing
End
Function

Protected Overridable Function GetSQLStatement() As String
  Return
""
End Function

Each report then overrides GetROC and GetSQLStatement to gather up the data for that report.

I use reflection to match the text box names to the BO property values with the assumption that txtPropertyName is how each text box is named.

Public Overridable Function GetDetailFieldList() As List(Of String)
Dim result As New List(Of String)
Dim txtBoxName As String
Dim
BOPropName As String
Dim
currentTextBox As DataDynamics.ActiveReports.TextBox
Dim rptProp As PropertyInfo

'Only find the TextBox members in the Report. Assumes they all start with "txt".
Dim rptMembers As MemberInfo() = Me.GetType.FindMembers(MemberTypes.Property, flags Or BindingFlags.NonPublic, Type.FilterNameIgnoreCase, "txt*")

For Each info As MemberInfo In rptMembers
'Since we know that we found only MemberTypes.Property we can safely cast the MemberInfo to a PropertyInfo.
rptProp = CType(info, PropertyInfo)
'verify that we are in fact dealing with a TextBox and not a mis-named control.
If rptProp.PropertyType.Name = "TextBox" Then
 
currentTextBox = CType(rptProp.GetValue(Me, Nothing), DataDynamics.ActiveReports.TextBox)
  'find all text boxes that are NOT in the page header or page footer.
 
If Not (currentTextBox.Parent.GetType Is < FONT color=#0000ff size=2>GetType(DataDynamics.ActiveReports.PageHeader) OrElse currentTextBox.Parent.GetType Is GetType(DataDynamics.ActiveReports.PageFooter)) Then
 
txtBoxName = rptProp.Name
'assumes all TextBox controls have a 3 letter prefix of txt and that the rest of the Name is the same as the BO Property. Case sensitive!
 
BOPropName = txtBoxName.Substring(3)
  result.Add(BOPropName)
End If
End
If
Next
Return
result
End Function

Private ReadOnly Property Props() As List(Of PropertyInfo)
Get
If mProps Is Nothing Then
mProps = New List(Of PropertyInfo)
Dim infoType As Type = mROC.GetType().GetProperty(
"Item", New Type() {GetType(Integer)}).PropertyType
'get an array of all Properties of the ROC (could be 50 or more)
D im tempProps() As PropertyInfo = infoType.GetProperties(flags)
'get the list of fields in the specific report detail section. (typically, can be less than 10)
Dim detailFields As List(Of String) = GetDetailFieldList()
If detailFields Is Nothing Then
'if the developer does not provide a list of fields then add all Properties to mProps.
For Each prop As PropertyInfo In tempProps
mProps.Add(prop)
Next
Else
For Each prop As PropertyInfo In tempProps
'This is case sensitive. So the BO Property names must be spelled the same way in the GetDetailFieldList method.
If detailFields.Contains(prop.Name) Then
mProps.Add(prop)
End If
Next
End If
End If
Return mProps
End Get
End Property

Then write code for each of the Active Report methods:

#Region " Active Reports Methods "

Protected Sub rpt_ReportStart(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.ReportStart
mIndex = 0
mHoldString = String.Empty
mFirstLine = True
'passes criteria values from speciality ReportBaseFrame to ReportHeaderRO
'for data binding to Page Header/Footer bands
mReadOnlyBO = GetReadOnlyBO()
'to populate ROC using SQL Statement
mROC = GetROC()
End Sub

'set txtTextBox.Value for all sections except the Detail section which is set in rpt_FetchData.
Protected Sub rpt_DataInitialize(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.DataInitialize
Dim txtBoxName As String
Dim
BOPropName As String
Dim
BOType As Type = mReadOnlyBO.GetType
Dim currentTextBox As DataDynam ics.ActiveReports.TextBox
Dim rptProp As PropertyInfo
'Only find the TextBox members in the Report. Assumes they all start with "txt".
Dim rptMembers As MemberInfo() = Me.GetType.FindMembers(MemberTypes.Property, flags Or BindingFlags.NonPublic, Type.FilterNameIgnoreCase, "txt*")

For Each info As MemberInfo In rptMembers
'Since we know that we found only MemberTypes.Property we can safely cast the MemberInfo to a PropertyInfo.
rptProp = CType(info, PropertyInfo)
'verify that we are in fact dealing with a TextBox and not a mis-named control.
If rptProp.PropertyType.Name = "TextBox" Then
currentTextBox = CType(rptProp.GetValue(Me, Nothing), DataDynamics.ActiveReports.TextBox)
'set currentTextBox.Value for PageHeader and PageFooter only.

'If currentTextBox.Parent.GetType IsNot GetType(DataDynamics.ActiveReports.Detail) Then
If (currentTextBox.Parent.GetType Is GetType(DataDynamics.ActiveReports.PageHeader) OrElse currentTextBox.Parent.GetType Is GetType(DataDynamics.ActiveReports.PageFooter)) Then
txtBoxName = rptProp.Name
'assumes all TextBox controls have a 3 letter prefix of txt and that the rest of the Name is the same as the BO Property.
BOPropName = txtBoxName.Substring(3)
Dim BOprop As PropertyInfo = BOType.GetProperty(BOPropName, flags)
If BOprop IsNot Nothing Then
'the BO and the TextBox w/o the prefix are the same name so set TextBox.Value = BO.Property.
currentTextBox .Value = BOprop.GetValue(mReadOnlyBO, Nothing)
End If
End
If
End
If
Next
'see if there is a Picture in the report Header. If there is, set the Image property to the BO.Logo value
'which is a System.Drawing.Image created from an array of Bytes.
'Assumes the name of the control has a prefix of "img".
rptMembers = Me.GetType.FindMembers(MemberTypes.Property, flags Or BindingFlags.NonPublic, Type.FilterNameIgnoreCase, "img*")

For Each info As MemberInfo In rptMembers
rptProp = CType(info, PropertyInfo)
'verify that we are in fact dealing with a Picture and not a mis-named control.
If rptProp.PropertyType.Name = "Picture" Then
Dim
currentControl As DataDynamics.ActiveReports.Picture = CType(rptProp.GetValue(Me, Nothing), DataDynamics.ActiveReports.Picture)

'set currentControl.Image for all sections except the Detail section which is set in rpt_FetchData.
If currentControl.Parent.GetType IsNot GetType(DataDynamics.ActiveReports.Detail) Then
txtBoxName = rptProp.Name
'assumes all Picture controls have a 3 letter prefix of img and that the rest of the Name is the same as the BO Property.

'In this case the name is imgLogo.
BOPropName = txtBoxName.Substring(3)
Dim BOprop As PropertyInfo = BOType.GetProperty(BOPropName, flags)
If BOprop IsNot Nothing Then
'the BO and the Picture control w/o the prefix are the same name so set Control.Image = BO.Property.
currentControl.Image = CType(BOprop.GetValue(mReadOnlyBO, Nothing), Drawing.Image)
End If
End
If
End
If
Next

'define Detail fields collection
For Each prop As PropertyInfo In Props
  Me.Fields.Add(prop.Name)
Next
End
Sub

'Add data to the Fields in the Detail section using 1 row at a time from the ROC.
'mIndex is incremented each time FetchData runs.
'This will fetch the next row from the ROC.
'It will run until we set eArgs.EOF = True.
Protected Sub rpt_FetchData(ByVal sender As Object, ByVal eArgs As DataDynamics.ActiveReports.ActiveReport3.FetchEventArgs) Handles Me.FetchData

Dim target As Csla.Core.IReadOnlyObject
Dim holdValue As String = String.Empty
If mIndex = mROC.Count Then
'stops firing this event
eArgs.EOF = True
Else
target = mROC.Item(mIndex, True)
For Each prop As PropertyInfo In Props
  Me.Fields(prop.Name).Value = prop.GetValue(target, Nothing)
Next

'increment the index for the ROC.
mIndex += 1
'continue to fire rpt_FetchData event
eArgs.EOF = False
End
If
End
Sub
#End
Region

Joe



JoeFallon1 replied on Friday, October 16, 2009

I built the framework and turned it over to someone else to build all the reports. A lot of them were written in another language so they had to be translated. But the layouts were all pretty simple - a few columns and some subtotals. Some reports were more complex but they all ended up working this way.

BTW - the code I shared is alot of the Base class but not all of it. There are other things that need to be done too. But that should get you started diwn the right path of using CSLA and Active Reports together in a general way.

Joe

 

Copyright (c) Marimer LLC