6

rdlc レポートで複数のサブレポートを処理するための手法を作成しましたが、汎用的で反復可能なものにしようとしたため、代わりにモデルを使用してケースごとに微調整する必要がありました。

たとえば、このような抽象インターフェイスを定義する場合、必要に応じて winform から winform にカット アンド ペーストします。

abstract class ISolutionStrategy
{
    public abstract void AlgorithmInterface(Int64 searchCriteria, SubreportProcessingEventArgs e);
}

まず、has-a オブジェクトを含めることで、これを各フォームに取り込めるようにしたいと考えています。また、デリゲートによるディスパッチ処理の動作をカプセル化し、処理メソッドも「汎用」にしたいと考えています。

したがって、設計要件は次のとおりです。

  • 複数のサブレポート処理を処理するために winform に含めることができるオブジェクトを作成します
  • winform でオブジェクトをインスタンス化して構成する
  • winform でディスパッチ テーブルまたは switch/case ステートメントを作成します。
  • その winform のレポート ビューアーの特定の要件を処理するすべてのメソッドを渡します。

目標は、スタンドアロンでテストして堅牢にすることができるオブジェクトを作成することです。また、ホイールをカット アンド ペーストして、新しい winform ごとに手動で微調整する必要がないようにすることです。

私が現在持っているものよりも優れたデザインを誰かが見つけたようです。

複数のサブレポート処理を処理するために winform に含めることができるオブジェクトを作成します

これまでのところ、ローカル フォーム ロード イベントにデリゲートがあります。

this.reportViewer1.LocalReport.SubreportProcessing += new SubreportProcessingEventHandler(LocalReport_SubreportProcessing);

*LocalReport_SubreportProcessing* メソッドの switch ステートメントによって処理されます。

メソッドの本体には、switch ステートメントが含まれています。

void LocalReport_SubreportProcessing(object sender, SubreportProcessingEventArgs e)

        {
            String commonSubreportKey = _commonSubreportKey;

            switch (e.ReportPath)
            {
                case "rptSubAlternateParts":
                    runSubAlternatePart(e, commonSubreportKey, new GetAlternateParts());
                    break;
                case "rptSubGetAssemblies":
                    runSubFailurePart(e, commonSubreportKey, new GetAssemblies());
                    break;
                case "rptSubGetAssemblies":
                    runSubGetGetEndItemLRMFailureInfo(e, commonSubreportKey, new GetEndItemLRMFailureInfo());
                    break;
                case "rptSubGetAssemblies":
                    runSubGetSubAssemblies(e, commonSubreportKey, new GetSubAssemblies());
                    break;
                default:
                    break;
            }
余談:

私の意見では、スイッチは、私が検討した代替案と比較して、ほとんど人間が判読できるものです。レポート名をキー、関数呼び出しデータを値としたハッシュの使用を検討しました。しかし、やり方がよくわからず、他人に理解してもらうのは難しいだろうと思っていました。


その後、switch ステートメントで関数呼び出しから渡された情報を再配置する関数が呼び出されます。

    private static void runSubAlternatePart(SubreportProcessingEventArgs e1, String  commonReportKey, GetAlternatePart myAP)
            {
                myAP.AlgorithmInterface(commonReportKey, e1);
            }

この再配置は間違いなくコードの吃音ですが、私が実装しようとしている戦略パターンの中間に必要なようです。

     abstract class IStrategy
        {
            public abstract void AlgorithmInterface(String searchParam, SubreportProcessingEventArgs e);

        }

レポートの 1 つの戦略の具体的な実装を次に示します。

class GetAlternatePart : IStrategy
{
private BLL.AlternatePartBLL ds = new BLL.AlternatePartBLL();


public override void AlgorithmInterface(String searchParam, SubreportProcessingEventArgs e)
              {

                    e.DataSources.Clear();
                    DataTable myDataTable = ds.GetAlternativePart(searchParam);
                    DataSet myDataSet = new DataSet();
                    myDataSet.Tables.Add(myDataTable);
                e.DataSources.Add(new ReportDataSource("BLL_AlternatePartBLL", myDataSet.Tables[0]));
            }

        }
    }

いずれにせよ、複数のサブレポートを持つ多くのレポートがあるため、レポート間で同じロジックを繰り返し手動で配線する必要はありません。

クラスを使用してスタッタリングが発生する中間部分を動的に作成するライブラリ品質の方法が必要です。また、サブレポートと対応するデータ ソースの詳細な接続を実際に実装する「匿名」機能を渡したいと考えています。

サブレポートを含む単一のレポート、またはいくつかの 1 回限りのレポートでさえ、私が行っていることは問題ありませんが、どうすれば手作業を減らし、より堅牢で、よりテストしやすくすることができるでしょうか?

私の環境は、.NET 3.5 をターゲットとする Visual Studio 2008 です。抽象クラスの宣言方法とコンパイル方法に違いがあるようです。

4

1 に答える 1

4

私が提案する解決策は、基本クラスへの非常に単純なリファクタリングであり、各WinFormに書き込む必要のあるコードを次の2つに減らします。1)そのフォームに使用されるレポートの設定。2)そのフォームのレポートデータを取得する方法の定義。

各WinFormがReportFormと呼ばれる基本クラスを継承すると仮定すると、各WinFormのコードは次のようになります。

public partial class Form1 : ReportForm
{
    public Form1()
    {
        // Wire up the report used by the Visual Studio-designed report viewer to the base class
        base.WinFormReport = reportViewer1.LocalReport;

        InitializeComponent();
    }

    // The search parameters will be different for every winform, and will presumably
    //  come from some winform UI elements on that form, e.g., parentPartTextBox.Text
    protected override DataResult GetReportData(SubreportProcessingEventArgs e)
    {
        // Return the data result, which contains a data table and a label which will be
        //  passed to the report data source
        // You could use DataSet in DataResult instead of DataTable if needed
        switch (e.ReportPath)
        {
            case "rptSubAlternateParts":
                return new DataResult(
                    new BLL.AlternatePartBLL().GetAlternativePart(parentPartTextBox.Text)
                    , "BLL_AlternatePartBLL"
                );

            case "rptSubGetAssemblies":
                return new DataResult(
                    new BLL.SubAssemblyBLL().GetSubAssemblies(someOtherTextBox.Text)
                    , "BLL_SubAssemblyBLL"
                );

            default:
                throw new NotImplementedException(string.Format("Subreport {0} is not implemented", e.ReportPath));

        }
    }
                                .
                                .
                                .

上記のコードはこれらのことを行います:

1)フォームで使用されたレポートを基本クラス(ReportForm)に通知します。必要に応じて、レポートをReportFormにリファクタリングすることもできますが、私のアプローチでは、VisualStudioでReportViewerとそのレポートを作成および操作できます。ただし、デザイナではなくプログラムでレポートを渡す場合は、派生したWinFormクラスから基本クラスにレポートを送信することをお勧めします。

2)レポートがすべてのサブレポートのデータを取得する方法を定義します。そのためには、DataTableとラベルを返す必要があります。これは、レポートデータソースで最終的に必要になるのはそれだけだからです。DataTableとラベルをRDLCデータソースにバインドするコードは、基本クラス(ReportForm)に属します。これは、そのバインドコードがすべてのWinFormに共通であるためです。

これで、ReportFormコードは次のようになります。

/// <summary>
/// Don't cut & paste into any Windows Forms, inherit the behavior you want from a base class
/// </summary>
public abstract class ReportForm : System.Windows.Forms.Form
{
    // I'm not sure exactly what this is used for, but I put it in base class in case there is some use for it here
    protected string _commonSubreportKey = "12345";

    // This will be the one line of code needed in each WinForm--providing the base class a reference
    //  to the report, so it has access to the SubreportProcessing event
    protected Report WinFormReport { get; set; }

    // Making this abstract requires each derived WinForm to implement GetReportData--foolproof!
    protected abstract DataResult GetReportData(SubreportProcessingEventArgs e);

    // Wire up the subreport_processing handler when any WinForm loads
    // You could override this in derived WinForms classes if you need different behavior for some WinForms,
    //  but I would bet this default behavior will serve well in most or all cases
    protected virtual void Form1_Load(object sender, EventArgs e)
    {
        Report.SubreportProcessing += new SubreportProcessingEventHandler(LocalReport_SubreportProcessing);
    }

    // When the Subreport processing event fires, handle it here
    // You could also override this method in a derived class if need be
    protected virtual void LocalReport_SubreportProcessing(object sender, SubreportProcessingEventArgs e)
    {
        // Get the data needed for the subreport
        DataResult dataResult = this.GetReportData(e);

        e.DataSources.Clear();
        e.DataSources.Add(new ReportDataSource(dataResult.Label, dataResult.Table));
    }
}

ReportForm基本クラスはFormを継承し、すべてのWinFormはReportFormを継承することに注意してください。これがデザイン全体の鍵となります。このReportForm基本クラスの仕組みは次のとおりです。

1)WinFormがインスタンス化されると、基本プロパティWinFormReportが設定されるため、基本オブジェクトはどのレポートが使用されているかを認識します。

2)WinFormが読み込まれると、派生クラスで定義されていないため、基本クラスでFormLoadイベントが呼び出されます。フォームの読み込み時に、レポートのSubreport_Processingイベントが接続されます。

3)ユーザーがパラメーターを入力し、何かをクリックしてレポートビューアーでレポートを作成すると、最終的にサブレポートがRDLCによってインスタンス化され、Subreport_Processingイベントがサブレポートごとに1回ずつ複数回発生します。

4)イベントが発生すると、基本クラスのイベントハンドラーがGetReportData(e)を呼び出します。これにより、WinFormで定義されたGetReportDataメソッドが呼び出されます。このメソッドは基本クラスで抽象的であるため、基本クラスで定義することはできませんが、派生クラスで定義する必要があることに注意してください。

5)WinFormのGetReportData(e)メソッドは、最初に指定したディスパッチャーロジックを使用して、DataTable(必要に応じてDataSetにすることもできます)とテキスト文字列をベースハンドラーに返します。

6)ベースハンドラーはDataTable / DataSetとテキスト文字列を取得し、それらをレポートデータソースとしてレポートにフィードします。また、レポートを表示するために必要な他の処理も実行できます。

よく考えた結果、一般的な動作を基本クラスにかなり簡単にリファクタリングすることをお勧めします。これは、要件に応じて機能すると思い、さらに複雑なものが必要になる場所がわからなかったためです。このアプローチは非常に読みやすく、新しいWinFormごとに必要なものを完全に最小限に抑え、何よりも非常に拡張可能であることがわかると思います。つまり、システムの開発を続けると、常に「この新しいコードは、各WinFormで繰り返す必要があるものですか、それとも基本クラスに入るのが一般的ですか?」と尋ねられます。

このアプローチについて質問や懸念がある場合は、コメントを追加してください。幸運を祈ります。それがまさにあなたが探しているものであることを願っています!

于 2013-02-14T00:27:21.170 に答える