Search code examples
.netactivereports

How to detect that code is executed in design-time?


ActiveReports 10 Extension for Visual Studio have designer for reports.
Designer windows contains tab "Preview" where designer report can be viewed without running whole application

I want create "testing" data which will be used only in design-time.
Setting data in the code works fine.

Private Sub TestReport_ReportStart(sender As Object, e As EventArgs) Handles Me.ReportStart
  Me.DataSource = TestModule.GetData() ' - Test data
  'Me.DataSource = _MyDataService.GetData() ' - Production data
End Sub

But after working with reports you need to remember remove testing data and set "production" code, which very easily can be forgotten.

It seems like "Preview"-code executing in run-time mode, see used testing code below

Private Sub TestReport_ReportStart(sender As Object, e As EventArgs) Handles Me.ReportStart
    Dim isDebug As Boolean = System.Diagnostics.Debugger.IsAttached 'Return false
    Dim mode As LicenseUsageMode = LicenseManager.UsageMode 'Return Runtime       
End Sub

Solution

  • After some reading and tests I came up with two solutions for that problem.

    Solution 1 - Assembly.GetEntryAssembly()
    From MSDN:

    Gets the process executable in the default application domain. In other application domains, this is the first executable that was executed by AppDomain.ExecuteAssembly.

    Seems like "Preview" not running in the default application domain and ActiveReports not calling AppDomain.ExecuteAssembly(). So method Assembly.GetEntryAssembly() will return null

    Public Class TestReport
    
        Private _DataService As IDataService
    
        Public Sub New(dataService As IDataService)
            Me.InitializeComponent()
            _DataService = dataService
        End Sub
    
        Private Sub Me_ReportStart(sender As Object, e As EventArgs) Handles Me.ReportStart
            If Me.IsDesignMode = True Then
                Me.DataSource = TestModule.GetData()
            Else
                Me.DataSource = _DataService.GetData()
            End If
        End Sub
    
        Protected ReadOnly Property IsDesignMode As Boolean
            Get
                Return Assembly.GetEntryAssembly() Is Nothing
            End Get
        End Property
    
    End Class
    

    Solution 2 - Parameterless constructor
    - Report class use interface IDataService for getting data.
    - Create implementation of IDataService which return testing data
    - Create parameterless constructor where original constructor which takes instance of IDataService will be called with instance of testing implementation of interface as parameter.
    - Mark parameterless constructor with Obsolete attribute to tell other developers not use it
    - Optional: use first solution here and throw exception if test data service was using in the run-time

    Public Class TestReport
    
        Private _DataService As IDataService
    
        Public Sub New(dataService As IDataService)
            Me.InitializeComponent()
            _DataService = dataService
        End Sub
    
        <Obsolete("Parameterless consrtuctor only for designer usage.")>
        Public Sub New()
            MyClass.New(New TestDataService())
        End Sub
    
        Private Sub Me_ReportStart(sender As Object, e As EventArgs) Handles Me.ReportStart
            Me.DataSource = _DataService.GetData()
        End Sub
    
    End Class
    
    Public Class TestDataService
        Implements IDataService
    
        Public Sub New()
            If Assembly.GetEntryAssembly() IsNot Nothing Then
                Throw New InvalidOperationException("Service cannot be used in run-time")
            End If
        End Sub
    
        Public Function GetData() As List(Of CustomItem) Implements IDataService.GetData
            'Generate test data
        End Function
    End Class