1. | Open the report properties using the Report, Report Properties menu. |
2. | In the properties dialog box, click the References tab. |
3. | Click "..." and navigate to the library. See Figure 23.2 for details. Figure 23.2. Reference custom assembly. Developers can navigate to any location where the library is present, such as the bin directory of the library project. This operation only records the reference to the assembly and not a specific location of this assembly. Report Designer adds the following RDL to reference an assembly: <CodeModules> <CodeModule>RSCustomLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null </CodeModule> </CodeModules> |
4. | Enter a Class Name and an Instance Name. Filling in a Class Name and an Instance Name is optional for static methods. When you specify a Class Name and an Instance Name, Report Designer creates an instance of the specified class and, subsequently, you can access the class inside of a report using the class' instance name. Report Designer adds the following RDL: <Classes> <Class> <ClassName>RsCustomLibrary.MainClass</ClassName> <InstanceName>myMainClass</InstanceName> </Class> </Classes> When specifying a Class Name, you need to prefix the name of the class with its assembly, such as RSCustomLibrary.MainClass . Otherwise , the SSRS compiler returns an error: [rsCompilerErrorInClassInstanceDeclaration] Error in class instance declaration for class MainClass: [BC30002] Type 'MainClass' is not defined. |
5. | Call the assembly from one of the report's expressions. A static method can be called as =<AssemblyName>.<ClassName>.<StaticMethodName>; in this case, =RSCustomLibrary.MainClass.GetLibraryInfo() . An instance method can be called as =<Code>.<InstanceName>.<PublicMethodName>; in this case, =Code.myMainClass.GetLibraryInfo() . A static method does not require an instance, but can still be accessed through the instance if so desired. |
Now that you have referenced a custom assembly in a report and copied binaries to the Report Designer's directory, the assembly can be called from the report in preview mode of the Report Designer. However, calling the assembly will not work if you try to debug or deploy this assembly to a Report Server if no additional steps are taken.
This chapter explains the additional steps needed to make a deployed assembly available to reports.
Note
The procedures that you have seen thus far allow referencing a custom assembly, but because you have not yet set security for the assembly, the assembly can only be called in preview mode.
Sometimes, you might need to pass initialization parameters to a class in a custom assembly. This is done by overriding the OnInit() method of the Code object of a report. This can be done by editing the RDL directly or using the code editor. To open code editor, use the Report, Report Properties menu and click the Code tab in the Report Properties dialog box.
See Figure 23.3 for details.
To initialize a class, you can either create a new instance of the class inside of OnInit and pass parameters to a class constructor or write a public initialization method for a class and call this method from OnInit .
When you create a new instance of the class, make sure that the instance name used in the OnInit method does not conflict with the instance name you have created when you referenced an assembly.
<Code> Dim Public myMainClass1 As RSCustomLibrary.MainClass Protected Overrides Sub OnInit() myMainClass1 = new RSCustomLibrary.MainClass(Report.Parameters!Period.Value) End Sub </Code>
To invoke this initialization method, from a report you can use the following expression: =Code.myMainClass1.GetLibraryInfo()
Note
If there is a conflict between the instance created in a report's reference and any of the instances generated in the code, SSRS generates an error: [rsCompilerErrorInClassInstanceDeclaration] Error in class instance declaration for class RsCustomLibrary.MainClass: [BC30260] 'myMainClass' is already declared as 'Private Dim myMainClass As <unrecognized type>' in this class .
When you call a public initialization function, create an instance of the class using the Report, Report Properties menu and then click the References tab.
Then call the initialization function from OnInit . Make sure that the instance name used in OnInit corresponds to the instance name used when you referenced an assembly.
<Code> Protected Overrides Sub OnInit() myMainClass.InitializeClass(Report.Parameters!Period.Value) End Sub </Code>
To invoke this initialization method, you can use the following expression: =Code.myMainClass.GetLibraryInfo()
Within the OnInit method, you can use items from the Globals, Parameters, and User collections. The Fields and ReportItems collections are not available when the OnInit method is invoked.
Note
Do not forget to prefix the collection name with Report (such as Report.Parameters ); otherwise, you will receive an error: [rsCompilerErrorInExpression] The Value expression for the textbox 'textbox2' contains an error: [BC42024] Access of shared member, constant member, enum member or nested type through an instance; qualifying expression will not be evaluated .
To take advantage of initialization, you need to add a constructor to the assembly. The updated assembly may have the following code:
using System; using System.Reflection; //Need to have this so we can get the Assembly information namespace RSCustomLibrary { public class MainClass { static int mPeriod = -1; public MainClass() {} public MainClass(int Period) { mPeriod = Period; } public void InitializeClass(int Period) { mPeriod = Period; } //Method GetLibraryInfo() returns this custom Assembly information //RSCustomLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null //AND initialization status: // public static string GetLibraryInfo() { return Assembly.GetExecutingAssembly().GetName().ToString() + ( (mPeriod != -1) ? "Initialized with value=" + mPeriod.ToString() : " Not initialized" ); } } }
Note the operator "?" (question mark) usage in the code. If you are not familiar with this operator, it is similar to the IF function or the IF-THEN-ELSE statement.
Note
If you choose to use a constructor to initialize the class, an explicit default constructor (constructor with no parameters) is required. If no default constructor is defined, SSRS returns an error: [rsCompilerErrorInClassInstanceDeclaration] Error in class instance declaration for class RsCustomLibrary.MainClass: [BC30455] Argument not specified for parameter 'Period' of 'Public Sub New(Period As Integer)' .
It is very likely that the first deployed version of an assembly will not be perfect and one day you will need to update an assembly. You can update an assembly using one of four ways:
1. | Maintain the same assembly attributes (such as Version and Culture) and replace an assembly in Report Designer and SSRS directories. Maintaining assembly attributes is a key for this method because the report's RDL contains this information in the <CodeModule> descriptor. If an assembly's attributes change, the reports can no longer call it. This method is the best for frequent assembly updates, while maintaining classes and method signatures. This method of updates is especially relevant during debugging and testing. |
2. | Update the assembly attributes and update all the references (using Report Designer or directly editing the <CodeModule> tags) to reference the new version of the assembly. |
3. | Create a strong-named assembly (see the next section, "Strong-named Custom Assemblies," for more details) and store it in the Global Assembly Cache (GAC). The GAC allows multiple versions of an assembly. Reports can call any of the versions stored in the GAC. Thus, you can keep both versions of the assembly and refer to either one. |
4. | As in the previous method, create a strong-named assembly, store a version of an assembly in the GAC, and force SSRS to redirect all the calls to the new assembly. In this case, an administrator would need to modify the Web.config and ReportService.exe configuration files to add the following entry: <configuration> <runtime> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <dependentAssembly> <assemblyIdentity name="RSCustomLibrary" publicKeyToken="..." culture="..." /> <bindingRedirect oldVersion="1.0.0.0" newVersion="2.0.0.0"/> </dependentAssembly> </assemblyBinding> </runtime> </configuration> |