クラウド上で帳票ツールを動かしてみる
帳票作成コンポーネント「ActiveReports」は、2010年6月30日に提供されたActiveReports for .NET 6.0J SP2で、Visual Studio 2010に対応しました。このVisual Studio 2010対応版を使って何か新しいことができないかと考えた時に、マイクロソフトが提供するクラウド環境「Windows Azure」上で、ActiveReportsを動かしてみようと思いつきました。
Windows AzureではASP.NET Webアプリケーションが動作しますし、ActiveReportsもASP.NET Webアプリケーション上で動かしてPDFを生成できるので、この2つを組み合わせれば、Windows Azureの鬼門の1つである印刷系機能への解決策を見つけることができるのでは、という発想です。
今回は、ActiveReportsをWindows Azure上で動作させる際に、私が検証した手順をひとつひとつ紐解きながら、実装方法を解説していきます。
今回の記事は、グレープシティの保証対象外の使い方をしています。また、筆者としても実際の業務アプリケーション構築案件としては未使用のため、業務アプリケーションとしての使用に耐えうるかは未知数です。Windows AzureのアーキテクチャーとActiveReportsの特性を踏まえて「動くかも!」と試してみた結果、予想以上にきちんと動いたために記事化に至ったというのが経緯である事を、あらかじめご了承ください。
公開当初に紹介していた実装方法では、ActiveReportsをワーカーロール側で動作させるには、不十分だということが判明したため、一部文章とサンプルファイルを更新しました。
Windows Azureとは?
Windows Azureの詳しい解説は、ActiveReportsの使い方とは別の話になってしまうので割愛しますが、ActiveReportsをWindows Azure上で動作させるために必要な概念について、簡単に説明しておきます。
Windows Azure Platformは、AppFablic、Compute Service、Storage Service、SQL Azureなどから構成されています。今回は、この中のCompute Serviceを使ってActiveReportsを動作させます。
Windows Azure Compute Serviceは、IISが使える「Webロール」とIISが搭載されていない「ワーカーロール」があり、ASP.NET WebアプリケーションであればWebロールだけでも作成できます。しかし、ビジネスロジックなどはワーカーロールに実装して、それをWebロールから呼び出して使う方がよいため、今回のサンプルでは、次のようなデータの流れで実装していきます。理由は後述します。
- ブラウザからWebロール上のASP.NET Webアプリケーションに接続
- Webロールからワーカーロールを呼び出す
- ワーカーロールでActiveReportsを動作させて、PDFのバイナリデータを作成
- 作成されたPDFバイナリデータをWebRoleからブラウザに応答
- ブラウザ側のAdobe Readerプラグインで表示
Windows Azure環境の認証レベルについて
ActiveReportsは、Full認証レベル環境での動作を前提としています。
今回、Windows Azure環境で動作させるにあたり、Windows Azureの認証レベルについて調べてみると、『MSDN:Windows Azure SDK Trust Policy Reference』内に、「Windows Azure trust」として詳しく定義されていました。これによると、コードアクセスセキュリティ(CAS)のレベルで、ASP.NET medium trust相当(部分的には相違)になっていることが分かります。
ActiveReportsのヘルプには、「Medium信頼レベル環境でPDFエクスポートを使用する場合、セキュリティの制限によりシステムフォントフォルダにアクセスできない」ため、指定したフォントや位置が正しく印字されない可能性があると記載されています。解決策として、「PDFフォントファクトリ」を使う方法が記載されているので、この方法で回避できるかどうかも確認していきたいと思います。
Windows Azure側の機能として、「ServiceConfiguration.cscfg」ファイルのロール定義にenableNativeCodeExecution="true"
を追記すればFull認証にできるようなので、こちらの方法でも認証レベルの問題は回避できるかもしれません。
ASP.NET medium trustでのActiveReportsの動作を試す
Windows Azure trustがASP.NET medium trust相当だとするならば、まずはActiveReportsをASP.NET medium trustで動作させ、どのような制限が加わるか、そして、それをどのように解決すればよいかを調べてみます。
ASP.NET 4でのActiveReports for .NET 6.0Jの使用は、Visual Studio 2010対応版で動作保障されています。そのため、ASP.NET medium trustの動きを事前に把握することで、動作保障のないWindows Azure環境での動きで不具合が生じたときに、Windows Azure環境特有の問題かどうかの判断ができるはずです。
プログラムの流れ
- 「Default.aspx」で[PDF]ボタンをクリックし、「CZ1009Reports.aspx」にリダイレクトする
- 「CZ1009Reports.aspx」では、PDF形式のバイナリデータを「CZ1009Reports.aspx」からブラウザへのレスポンスとする
- ブラウザ側でAdobe ReaderによりPDF表示
Imports DataDynamics.ActiveReports Imports DataDynamics.ActiveReports.Export.Pdf Public Class ActiveReportsClass Private block As Object = Nothing ''' <summary> ''' PDFデータを取得する ''' </summary> Public Function GetPdfDatas() As Byte() Dim memStream As System.IO.MemoryStream Dim ds As DataSet = GetRecords() Using _rpt As New CZ1009ReportsClass _rpt.Document.Printer.PrinterName = "" _rpt.PageSettings.PaperKind = System.Drawing.Printing.PaperKind.A4 _rpt.PageSettings.Orientation = Document.PageOrientation.Portrait _rpt.PageSettings.Margins.Top = ActiveReport.CmToInch(0.5F) _rpt.PageSettings.Margins.Bottom = ActiveReport.CmToInch(0.5F) 'データを割り当てる _rpt.DataSource = ds.Tables("Enviroment") ' レポートを作成 _rpt.Run(False) 'pdfエクスポートオブジェクトを生成します Using _pdf = New PdfExport ' PDFの出力用のメモリストリームを作成します memStream = New System.IO.MemoryStream ' メモリストリームにPDFエクスポートを行います _pdf.Security.Use128Bit = True _pdf.Security.OwnerPassword = "hatsune" _pdf.Security.Permissions = PdfPermissions.AllowPrint _pdf.Security.Encrypt = True _pdf.Export(_rpt.Document, memStream) End Using End Using Return memStream.ToArray() End Function End Class
medium trust制限をかけないときの動作
Visual Studio 2010のASP.NET Webアプリケーションテンプレートで新規作成したASP.NET Webアプリケーションは、medium trust制限がかかっていません。この状態でまずは動作させて、プログラムコードが意図したとおりに動くかを確認します。
ASP.NET medium trustでの動作
ASP.NET medium trustに設定するためには、「Web.config」にtrust要素を追加します。
<?xml version="1.0" encoding="utf-8"?> <configuration> <system.web> <trust level="Medium"></trust> <compilation debug="true" strict="false" explicit="true" targetFramework="4.0" /> </system.web> </configuration>
図4のとおり、medium trustの場合、指定したフォントと異なるフォントが印字され、位置なども正しくありません。
PDFフォントファクトリを設定する
ActiveReportsのヘルプに、「PDFフォントファクトリ」を使えばmedium trustであっても、指定したフォントが印字されることが記載されていたので、試してみます。
PDFフォントファクトリの設定は「Web.config」で行います。
<?xml version="1.0" encoding="utf-8"?> <configuration> <configSections> <sectionGroup name="ActiveReports.PdfExport"> <section name="FontFactory" type="DataDynamics.ActiveReports.Web.FontFactorySectionHandler,ActiveReports.Web, Version=6.2.2659.1, Culture=neutral,PublicKeyToken=cc4967777c49a3ff" requirePermission="false"/> </sectionGroup> </configSections> <ActiveReports.PdfExport> <FontFactory Mode="File"> <AddFolder VirtualPath="~/Fonts" Recurse="true"/> <Substitute Font="Helv" To="Helvetica"/> <SetFallbackFont Font="Arial"/> <!-- font link nodes --> <AddFontLink Font="Arial" List="SimSun,gulim,PMingLiU"/> <AddFontLink Font="Tahoma" List="MS UI Gothic,SimSun,gulim,PMingLiU"/> <AddFontLink Font="MS UI Gothic" List="SimSun,gulim,PMingLiU,Microsoft Sans Serif" IsDefault="true"/> </FontFactory> </ActiveReports.PdfExport> <system.web> <trust level="Medium"></trust> <compilation debug="true" strict="false" explicit="true" targetFramework="4.0" /> </system.web> </configuration>
次に、プロジェクトに「Fonts」フォルダを作成して、そこに「C:\Windows\Fonts」フォルダから目的のフォントファイルをコピーしておきます(サンプルにはフォルダが含まれていますが、フォントファイルは含んでいません)。
PDFフォントファクトリを設定し、実際に動かしてみると、図5のように正しいPDFへの印字結果を得ることができました。プログラムコード側は1行の変更も必要ありませんでした。
なお、PDFフォントファクトリを使ったアプリケーションを配布するときは、今回のサンプルのように、「Fonts」フォルダにコピーするフォントファイルは、設置先の実環境からコピーするのがよいでしょう。
しかし、最も良いのは、medium trustでアプリケーションを実行する必要があるかどうかを検討することだと思います。
Windows Azureクラウドサービステンプレートを使う
Visual Studio 2010でWindows Azureアプリケーションを作成するためには、「Windows Azure Tools for Microsoft Visual Studio 2010 1.2」をインストールする必要があります。
Windows Azure Toolsを追加インストール済のVisual Studio 2010で新規プロジェクトの作成を行うと、テンプレートに[Cloud]-[Windows Azureクラウドサービス]が追加されており、このテンプレートからWindows Azureアプリケーションが作成できます。
ロールの選択
テンプレートを選択して[OK]をクリックすると、使用するロールの選択ダイアログが表示されます。ASP.NET WebアプリケーションをWindows Azure上で実現するためには、[ASP.NET Webロール]を選択します。
前述のとおり今回のサンプルでは、ASP.NET Webロールに加え、ActiveReportsでPDFを生成するコードの稼働ロールとしてワーカーロールも使います。これは、ワーカーロールはWindowsサービスのようなロールで、負荷の高い処理などはASP.NET Webロールから呼び出すワーカーロール側に設置するのが、Windows Azureの実用的なコード配置の1つの基準だからです。ただし、今回はActiveReportsを使う方法に主眼を置いているため、サンプルコードを少しでもシンプルにするべく非同期ではなく同期呼び出しとしています。
ロールを選択し、右側のリストボックスでロール名を変更したら、[OK]ボタンをクリックします(後から変更するよりも、この時点で名前をつけることをお勧めします)。これで、自動的にWindows Azureアプリケーション用のソリューションが作成されます。
ソリューションの初期状態からの変更
Visual Studio 2010のASP.NET WebアプリケーションテンプレートやASP.NET Webサイトテンプレートには空のテンプレートがあり、不要なaspxファイルが生成されません。しかし、今回のサンプルのようにWindows AzureクラウドサービステンプレートでASP.NET Webロールを生成すると、「Defalut.aspx」「About.aspx」「Login.aspx」ファイルが生成されます。そこで、ASP.NET Webロールから不要なaspxファイルやフォルダを削除して、必要最小限の構成にします。
Default.aspxページを作成
Default.aspxページに、PDFを表示するためのボタンを配置します。Windos Azureアプリケーションといっても、ASP.NET Webロールであれば、ページのデザインはASP.NET Webフォームと同じです。ツールボックスからButtonコントロールをドラッグ&ドロップして、プロパティを設定すれば完成です。
Windows Azureに発行
WebロールのGUIができたので、コンパイルエラーがなければ、ActiveReports関連のコードを組み込む前にWindows Azure上で動作するかどうかをクラウドサービスに発行して動きを確認してみます。
クラウドサービスに発行する前に、利用するためのアカウント設定が必要です。詳しくは筆者ブログのエントリー「Windows AzureをMSDN特別導入プランで無料利用するための設定方法」などを参考にしてください。
クラウドサービスに発行するには、ソリューションエクスプローラーからクラウドサービス定義のプロジェクトを右クリックして、[発行]を選択します。[クラウドサービスをWindows Azureに配置します]を選択し、資格情報などを選んで[OK]をクリックすれば、自動的にWindows Azureに転送が行われ、配置されます。
Windows Azureアプリケーションの実行
発行が完了したら、最後に表示されているWebサイトのURLをクリックすると、Windows Azureアプリケーションをブラウザから呼び出せます。
もちろん、この時点ではPDF作成部分は組み込んでいないので[PDF出力]ボタンをクリックしても何も起こらないのが正常な動作です。ここまで確認できたら、次はActiveReports関連のコードを組み込んでいきます。
ActiveReportsを動作させるワーカーロールコード
PDFはワーカーロール側で生成するため、ActiveReports関連のコードはすべてワーカーロール側に記述します。サンプルプログラム作成開始時点では、もちろんWindows Azure特有の問題やその対応方法も分からなかったので、ActiveReportsを使用するクラスライブラリの作成時と同じ手順でプログラミングしてみました。その時の作業を順番に追ってみます。
ActiveReportsの参照設定
まず、ソリューションエクスプローラーで「CZ1009CloudWorkerRoleワーカーロール」を右クリックして、「Graprcity ActiveReports for .NET」「Document」「PDF Export」「Viewer」のコンポーネントへの参照を追加します。
ActiveReports 6.0Jファイルの追加
参照の追加が完了したら、再びソリューションエクスプローラーで「CZ1009CloudWorkerRoleワーカーロール」を右クリックして、[追加]-[新しい項目]を選択し、ActiveReportsの帳票定義体ファイルを追加します。
図13を見れば分かるように、ActiveReportsの定義体には、2つのファイル形式があります。「ActiveReports 6.0Jファイル」を選択した場合には、クラスファイルとして定義体が追加され、WindowsフォームやWebフォームのようにビルド後はアセンブリの一部となります。もちろん、IDEで該当ファイルをダブルクリックすれば、ActiveReportsの帳票デザイナが開きます。
もう1つの「ActiveReports 6.0Jファイル(XMLベース)」を選択した場合は、rpxファイルとしてプロジェクトに追加され、ビルド後もアセンブリとは別のrpxファイルとして動作環境に配置されます。つまり、動作環境上のrpxファイルを編集することで、再ビルドなしに帳票定義体の修正ができるという利点があります。業務アプリケーションでは、帳票のちょっとした修正というのは意外と多いため、筆者はこのファイル形式をよく使っています。
しかし、この形式は実行時にrpxファイルの読み込みが必要なため、動作環境のどこにrpxファイルがあるかが分かっていなくてはなりません。ASP.NET Webロールの場合は、「My.Request.PhysicalApplicationPath」プロパティで位置が判明しますが、ワーカーロールの場合は、どのようにファイルパスを取ればよいか明確には分からなかったため、今回はアセンブリの一部となる「ActiveReports 6.0Jファイル」形式を採用しました。
ActiveReports 6.0J帳票定義体の定義
ActiveReportsの帳票定義体デザイナは、非常に使いやすい操作性になっています。基本的には、Windowsフォームデザイナと同じようにツールボックスからコントロールをドラッグ&ドロップし、配置するときにはスナップ線を頼りに最適な位置を決めることができます。Windowsフォームデザイナと異なるのは、コントロールとコントロールの間が0ピクセル、つまり開きがない状態が最適な位置と設定されている点です。
一覧表などを作る際、表のタイトルをPageHeaderセクションに配置し、データ部分をDetailセクションに配置するような場合でも、セクションをまたがってスナップ線が表示できるため、タイトル位置とデータ位置を容易に合わせることができます。
また、CrossSectionLineコントロールやCrossSectionBoxコントロールを使えば、セクション間をまたがった罫線も簡単に引くことができ、一覧表の罫線部分も簡単に定義できます。さらに、PageFooterセクションまで含めておけば、“データがなくてもページの下までいつも同じ大きさで枠線を引いておく”といった設定も簡単に実現できます。
ただし、CrossSectionLineコントロールとCrossSectionBoxコントロールのStartプロパティとEndプロパティのプロパティ設定値はcm(センチメートル)単位ではなく、インチ単位なので注意が必要です。プロパティで位置を決めるときは、cmからインチへの単位変換を行い、その値を小数点以下まで含めてプロパティに設定するようにしないと、Lineなど他のコントロールがcmでプロパティを設定しているので、微妙にずれることがあります。
HTTP内部エンドポイントの追加
Webロールからワーカーロールを呼び出すには、Windows Azure Storage Serviceのキューを使って非同期に連携する方法もありますが、非同期生成したPDFデータの扱いなどの考慮も必要になるため、今回はワーカーロールにHTTPで呼び出す内部インターフェース(内部エンドポイント)を定義して、それを同期的に呼び出すシンプルな方式にしました。
HTTPを使った内部エンドポイントを定義するには、ソリューションエクスプローラーからクラウドサービス定義(サンプルでは「CZ1001Cloud」)にあるワーカーロール定義のプロパティで、エンドポイントの追加を行います。今回定義するエンドポイントは、種類を「internal」としてAzure内部でのみ使用するように定義し、プロトコルに「http」を指定してWebロールからHTTPで呼び出せるようにします。
HTTP内部エンドポイントの設定
HTTP内部エンドポイントを設定したら、生成直後のワーカーロールにはRun
メソッド、OnStart
メソッド、RoleEnviromentChanging
メソッドのコードが既に記載されているので、Run
メソッドにエンドポイントが呼び出された時の処理を追加します。
Private Shared listener As New HttpListener Public Overrides Sub Run() Dim endpoint As IPEndPoint = RoleEnvironment.CurrentRoleInstance.InstanceEndpoints("HttpEndpoint").IPEndpoint listener.Prefixes.Add(String.Format("http://{0}:{1}/", endpoint.Address, endpoint.Port)) listener.Start() Do While (True) Dim context As HttpListenerContext = listener.GetContext() Using mes As System.IO.Stream = context.Response.OutputStream Try Dim byteMsg As Byte() = GetPdfDatas(False) mes.Write(byteMsg, 0, byteMsg.Length) Catch ex As Exception Dim msg As String = ex.Message Dim byteMsg As Byte() = System.Text.Encoding.UTF8.GetBytes(msg) mes.Write(byteMsg, 0, byteMsg.Length) End Try End Using Loop End Sub
エンドポイントの処理は、エンドポイントに対してlistener
を定義して呼び出しを待ち合わせし、呼び出されたら対応した処理を行うということになります。
listener
の定義は、エンドポイント名を「HttpEndPoint
」とした場合、InstanceEndpoints("HttpEndpoint")
でエンドポイントを取得して、HttpListener
に追加(Add
)します。後は、Start
メソッドでlistener
を開始してGetContext
メソッドで呼び出しを待ちます。
PDF生成コードの作成
GetContext
メソッドで待ち合わせていた呼び出しが行われたら、PDF作成用のGetPdfDatas
メソッドを呼び出します。GetPdfDatas
メソッドには、次のようなコードを記述します。
: (中略) : Imports DataDynamics.ActiveReports Imports DataDynamics.ActiveReports.Export.Pdf Public Class WorkerRole Inherits RoleEntryPoint : (中略) : Private block As Object = Nothing ''' <summary> ''' PDFデータを取得する ''' </summary> Public Function GetPdfDatas() As Byte() Dim memStream As System.IO.MemoryStream Dim ds As DataSet = GetRecords Using _rpt As New CZ1009Reports _rpt.Document.Printer.PrinterName = "" _rpt.PageSettings.PaperKind = System.Drawing.Printing.PaperKind.A4 _rpt.PageSettings.Orientation = Document.PageOrientation.Portrait _rpt.PageSettings.Margins.Top = ActiveReport.CmToInch(0.5F) _rpt.PageSettings.Margins.Bottom = ActiveReport.CmToInch(0.5F) 'データを割り当てる _rpt.DataSource = ds.Tables("Enviroment") ' レポートを作成します _rpt.Run(False) ' PDFエクスポートオブジェクトを生成します Using _pdf = New PdfExport ' PDFの出力用のメモリストリームを作成します memStream = New System.IO.MemoryStream ' メモリストリームにPDFエクスポートを行います _pdf.Security.Use128Bit = True _pdf.Security.OwnerPassword = "hatsune" _pdf.Security.Permissions = PdfPermissions.AllowPrint _pdf.Security.Encrypt = True _pdf.Export(_rpt.Document, memStream) End Using End Using Return memStream.ToArray() End Function : (中略) : End Class
メソッドの中のコードは、リスト1で紹介したASP.NET Webアプリケーションのコードとまったく同じです。
ActiveReportsを動作させるWebロールコード
ASP.NET Webロールからワーカーロールを呼び出す
今回のサンプルでは、Default.aspxの[PDF出力]ボタンをクリックすると「My.Response.Redirect("CZ1009Reports.aspx")
」を実行して、CZ1009Reports.aspxページを呼び出します。このCZ1009Reports.aspxページで、ワーカーロールを呼び出します。
ワーカーロールの呼び出し方は、動的に決定される内部エンドポイントのIPアドレスとポート番号を取得し、webClient.DownloadData
メソッドを使って呼び出します。
Public Class CZ1009Reports Inherits System.Web.UI.Page Private block As Object = Nothing Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) _ Handles Me.Load If Not Me.IsPostBack Then Call GetRecords(isEmbedFonts) End If End Sub Private Function GetRecords(ByVal isEmbedFonts As Boolean) As Boolean Using webClient As New System.Net.WebClient Dim userID As String = My.User.Name Dim workerEndPoint = Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment.Roles("CZ1009CloudWorkerRole").Instances(0).InstanceEndpoints("HttpEndpoint") Dim pdfStream() As Byte Dim pdf() As Byte = System.Text.Encoding.UTF8.GetBytes("%PDF") pdfStream = webClient.DownloadData("http://" & workerEndPoint.IPEndpoint.ToString) If pdfStream(0) = pdf(0) AndAlso pdfStream(1) = pdf(1) AndAlso pdfStream(2) = pdf(2) AndAlso pdfStream(3) = pdf(3) Then Response.ContentType = "application/pdf" Response.AddHeader("content-disposition", "inline; filename=AzureActiveReports.PDF") End If Response.BinaryWrite(pdfStream) Response.End() End Using Return True End Function End Class
ActiveReportsを動作させた実行例
Windows Azureでの実行
ビルドしてコンパイルエラーがなかったら、Windows Azureに発行して動作を確認してみましょう。
結果は、[PDF出力]ボタンをクリックするとCZ1009Reports.aspxページが呼び出されるところまではいいのですが、その後は残念ながら“ActiveReportsのDLLがWindows Azure上に存在しない”というエラーになってしまいます。
開発環境での実行
Windows AzureアプリケーションをVisual Studio 2010上で動かすと、Development FabricとDevelopment Storageが起動され、その環境を使って動作します。これは、ASP.NET WebアプリケーションをVisual Studio 2010上で動かしたときのASP.NET開発サーバーに相当するものです。
今回のサンプルを開発環境で動作させると、ActiveReportsが開発環境に入っているので、当然、Windows Azureで動作させたときのエラーは発生しません。しかし、そのかわりにリスト5の「_pdf.Export(_rpt.Document, memStream)」
行で「Failed To read TTF file, or no CMAP table found
」という実行時エラーが発生してしまいました。
Windows Azure環境の問題点切り分けと対処方法
ActiveReportsのDLLをWindows Azureに設置する
最初に、問題点をクリアにするために、“開発環境にActiveReportsがあってWindows Azure環境にはActiveReportsがない”という状態を解消し、開発環境とWindows Azure環境を同一環境にしたいと思います。
GAC(Global Assembly Cache)にあるDLLをコピーするためには、通常と同じように、参照設定にあるActiveReportsのDLLファイルの[ローカルコピー]プロパティを「True」に設定すれば、ビルドしたアセンブリと同じ実行時フォルダにコピーされるはずです。
この設定により、ActiveReportsのDLLファイルがWindows Azureの配布対象になるので、発行を行えば他のアセンブリと一緒にWindows Azureにコピーされ、環境を一緒にした訳ですから、Windows Azureでも“ActiveReportsのDLLがない”というエラーではなく、「Failed To read TTF file, or no CMAP table found
」という実行時エラーになるはずです。
そこで、実際に発行してみてWindows Azureでの動作を確認してみます。
Windows Azure上でActiveReportsからPDF出力
結果は、正常に動作し、PDFがブラウザに表示されました。
しかも、単に表示されただけではなく、先ほどASP.NET WebアプリケーションをASP.NET medium trustで動作させたときに確認したような、印字ずれも生じていないPDFになっています。
つまり、Windows Azureのワーカーロール(別途確認していますがWebロールも同様)の認証レベルは、ActiveReportsの動作環境的には、medium trustとは異なると言えると思います。
開発環境の問題点切り分けと対処方法
Windows Azureの実行環境では発生しない「Failed To read TTF file, or no CMAP table found
」のエラーですが、開発環境であるDevelopment Fabric上では発生してしまいます。やはり、実行環境と開発環境では多少の違いがあるのだと想定できます。
そこで、ASP.NET WebアプリケーションでActiveReportsがmedium trustでの制限を回避する方法として先ほど紹介した、PDFフォントファクトリ機能を使ってみたいと思います。
ワーカーロールでPDFフォントファクトリ機能を使う
ワーカーロール側でActiveReportsを使っていたので、「Fonts」フォルダを作成してフォントファイルをコピーし、ワーカーロールのApp.ConfigにPDFフォントファクトリの設定をしました。
App.ConfigにSystem.Web要素がないため、trust要素をどうやって記述しようかと悩みましたが、開発環境であるDevelopment FabricがASP.NET medium trustで動作していると推測できるため、trust要素を書かなくても認証レベルはmedium trustであるはずだと考えました。
さっそく動作させてみたのですが、「VirtualPath="~/Fonts"
」の指定が間違えているとのエラーになってしまいます。つまり、PDFフォントファクトリの設定は、ワーカーロールでは難しいということです。
Windows Azure環境では動作するので悔しいところですが、ワーカーロール側でActiveReportsを使うならば、“現時点では”開発環境でのテスト実行はあきらめないといけないようです。
Azure環境と開発環境で動くActiveReportsアプリ
Windows Azure環境では動作して開発環境では動作しないという状況は、業務アプリケーションを構築する上ではかなりのマイナス要因です。そこで、ASP.NET medium trustでは動作させられたことを踏まえ、ASP.NET Webアプリケーションを動作させるWebロールだけを使って、ActiveReportsを使ったPDF出力を実装して確認してみたいと思います。
WebロールでPDFフォントファクトリ機能を使う
最初に作成したASP.NET Webアプリケーションの構成と同様に、Webロール側でActiveReportsを使い、さらにPDFフォントファクトリ機能を使ってみました。すると、ワーカーロールの時と同じくWindows AzureでのActiveReportsのDLL問題は発生しましたが、同じ様にDLLをローカルコピーで配置する手法で解決できました。また、PDFフォントファクトリの設定をしなくても、Windows Azure上では動作します。
後は、開発環境のDevelopment Fabric上での動作がエラーになる点を解決するだけです。PDFフォントファクトリの設定は次のようになります。
- ソリューションエクスプローラーで、すべてのファイルを表示
- WebロールでDLLをコピーするときは、DLLを参照設定して「ローカルコピー」プロパティを「True」に
- PDFフォントファクトリではActiveReports.Web.DLLを使うので、ActiveReports.Web.DLLも参照の追加を行い[ローカルコピー]プロパティ値も「True」に
- 「Fonts」フォルダを作成し、フォントをコピー
- 「Fonts」フォルダをプロジェクトに追加
- フォントファイルのプロパティを「コンテンツ」に設定
上記のようにPDFフォントファクトリを設定したWindows Azureアプリケーションを、開発環境のDevelopment Fabric上で動かすと、正常に動作しました。そして、Windows Azure上に発行して動かしても、同じく正常に動作します。
さいごに
最終的にうまくいきましたが、Windows Azureに既にインストールされているフォントファイルを、Windows Azure環境にコピーして使うしかないという点には、納得ができませんでした。コンポーネント側で、開発環境ではPDFフォントファクトリが有効になり、Windows Azureへの発行時には無視されるような仕組みが用意される。もしくは、プラットフォーム側でWindows AzureとDevelopment Fablicの相違がなくなる、といった対応が行われれば、今後、利用できる可能性が広がりそうです。
Windows AzureでActiveReportsを使用する際のポイントは、配布先にActiveReportsが配布されていない、そして、インストールできないようなIIS環境で動作させたいときと類似しています。これは、ASP.NET 4が提供されているホスティングサービスを利用するときと同じです。
その他にも、何点か調整事項がありましたが、最終的には開発環境でもWindows Azure環境でも動作しました。しかし、最初に記述したとおり、この方法はグレープシティの動作保証対象外の使い方なので、業務アプリケーション構築に使うかどうかは決心がつきかねるでしょう。できれば、この記事を元にグレープシティで動作検証やヘルプの記述の拡充を行っていただき、Windows Azureでの動作も保証対象に加えていただけたら喜ばしい限りです。
Windows Azureのコンピューティング時間に対する課金はプログラムを発行した時点で発生します。また、00分を基準にした1時間単位で課金されるため、00分過ぎに発行して動作確認が終わったら、59分くらいまでに「Azure Services Developer Portal」から、アプリケーションの削除を行ってください。削除するには、「Hosted Service」で[Suspend]ボタンをクリックしてSTOP状態にしてから、[Delete]ボタンをクリックします。Suspendで、STOPの状態になっていても課金されるので注意してください。