2016-01-04 22 views
5

私は、ユーザーがKPI、チャート、集計関数、およびその他のパラメータのセットから選択できる、単純なレポートツールを作成しようとしています。その後、ボタンをクリックしてから、wcfサービスすべてのデータを含むカスタムモデルを返します。これは、MVC/WPFアプリケーションで表示できます(両方の可能性があります)。セミアドホックレポートの一般的なインターフェイス

ユーザーはいくつかの国にいる可能性があるため、データアノテーションを使用して、現在のユーザーが使用していた言語と数値の形式に適した方法ですべての数値とヘッダーを似ているようにしたい。

データの読み込みとそのすべての処理は問題なく動作しています。また、私はデータアノテーションを使用しているので、すべての言語/カルチャー固有の設定が気になります。問題は、ユーザーに表示したいモデルにすべてのデータを入れるときに始まります。

私がしようとしているのは、列のコレクションを含むReportクラスです。各列はint/double/...の値のリストです。さて、私はWCFを扱っているので、上記の説明はジェネリックの使用を(私が理解している範囲で)暗示しています。クラス/ wcf操作に[KnownType]または[ServiceKnownType]を使用できます。戻り値として基本型またはインタフェース。決して実際にこれを試したことはありませんが、私にはかなり論理的な説明がありましたので、私はこの部分に大きな問題はないと思います。

は今のところ、私のインターフェイスは、(私が持っている実際の問題に焦点を当てるために簡略化)などのようになります。

public interface IReport<T> where T: IConvertible { ICollection<IColumn<T>> Columns { get; set; } } 
public interface IColumn<T> where T: IConvertible { ICollection<IValue<T>> Values { get; set; } } 
public interface IValue<T> where T: IConvertible { T Value { get; set; } } 

各列の値が/ダブル/ ...、私は仮定int型かもしれないので、私は価値のために実際のクラスを持っている必要がありますよう、(私はコレクション型にデータ注釈属性を使用することができるとは思わない):もちろん

public class IntValue: IValue<int> 
{ 
    [DisplayFormat(DataFormatString = "{0:#,##0;-#,##0;'---'}", ApplyFormatInEditMode = true)] 
    public int Value { get; set; } 
} 

を、それは奇妙に見えますが、あなただけの可能性があるのでそれを一般的なクラスにしてくださいIValueを実装し、それを使って行う価値ですが、私がばかげたことをすれば私はDisplayFormat属性を使用することができ、それがユーザーに表示される方法について心配する必要はありません。常に適切です。

さて、IColumnとiReportはを実装するクラスのために、それは簡単です:

public class Report<T>: IReport<T> where T: IConvertible 
{ 
    public ICollection<IColumn<T>> Columns { get; set; } 
    public Report() { Columns=new List<IColumn<T>>(); } 
} 

public class Column<T>: IColumn<T> where T: IConvertible 
{ 
    public ICollection<IValue<T>> Values { get; set; } 
    public Column() { Values = new List<IValue<T>>(); } 
} 

インタフェースとクラスのリストから、あなたはすぐに、これはそれが不可能ないくつかの列のレポートを持ってできることがわかります他のタイプがあります。したがって、いくつかの列がintで、いくつかがdoubleであるレポートを作成することはできません... IReportのジェネリック制約により、型を指定するため、すべての列に対してその制約が適用されます。各列...それはまさに私が本当に望むものです。

私はどこにもいないと思うし、何かが本当にシンプルでないかもしれないと思うので、適切な方向に微妙な感覚を感じる。

TL; DR:ジェネリックコレクションを非ジェネリックタイプで取得するにはどうすればよいですか?

+0

「新しいIReport 」としてレポートを作成できませんか? – Baldrick

+0

私が今見ているところでは、IConvertibleであると期待しているので、int型またはdouble型の列を追加することはできません。IConvertible実装クラスではありません。 –

答えて

2

申し訳ありませんが、私は提案された解決策からインスピレーションを受け、以下のようなバリエーションを実装しました。ジェネリック医薬品を使いたくないと思っていますが、それでも私は迷惑になります。結局のところ、私はいくつかの型の列(または値)が必要です。それはジェネリックが何のためにあるのですか?また、フィールドの書式設定を提供するための仕組みを提供したかったのです。

私はIReportとIColumnインターフェイスをかなり単純にしましたが、IColumnインターフェイスのIValueインターフェイスは参照しません。代わりに、抽象クラスValueを使用して、書式設定とデータ取得のための基本フレームワークの一部を定義します(文字列形式です)。

実際のIntValue/DoubleValueとValueのベースクラスの間に、DataValueフィールドを提供する以外に何もしない汎用IValueインターフェイスを実装する汎用Valueクラスを追加しました.IntValue/DoubleValueクラスを使用して、AsFormattedStringメソッドを実装します。このメソッドは、Valueベースクラスコンストラクタで作成したFormatterを使用して通常のToStringメソッドを使用します。

実際の実装はIntValue/DoubleValueクラスで提供されており、既にハードコーディングされた標準フォーマットまたはクラスユーザーが提供するカスタムフォーマットを使用する可能性があります。

public interface IReport { ICollection<IColumn> Columns { get; set; } } 
public interface IColumn { ICollection<Value> Values { get; set; } } 

public interface IValue<T> where T: IConvertible { T Data { get; set; } } 

public abstract class Value 
{ 
    #region Formatting 

    protected IFormatProvider Formatter { get; set; } 
    protected abstract IFormatProvider GetFormatter(); 
    protected abstract string AsFormattedString(); 
    public override string ToString() { return AsFormattedString(); } 

    #endregion 

    public Value() { Formatter = GetFormatter(); } 
} 

public abstract class Value<T>: Value, IValue<T> where T: IConvertible 
{ 
    #region IValue members 

    public T Data { get; set; } 

    #endregion 

    #region Formatting 

    protected override string AsFormattedString() { return Data.ToString(Formatter); } 

    #endregion 
} 

public class IntValue: Value<int> 
{ 
    public IntValue() { } 
    public IntValue(string formatstring, int data) { Formatter = new IntFormatter(formatstring); Data = data; } 

    #region Formatting 

    protected override IFormatProvider GetFormatter() { return new IntFormatter(); } 

    internal class IntFormatter: CustomFormatter 
    { 
     public IntFormatter() : this("{0:#,##0;-#,##0;'---'}") { } 
     public IntFormatter(string formatstring) : base(formatstring) { } 
    } 

    #endregion 
} 

public class DoubleValue: Value<double> 
{ 
    public DoubleValue() { } 
    public DoubleValue(string formatstring, double data) { Formatter = new DoubleFormatter(formatstring); Data = data; } 

    #region Formatting 

    protected override IFormatProvider GetFormatter() { return new DoubleFormatter(); } 

    internal class DoubleFormatter: CustomFormatter 
    { 
     public DoubleFormatter() : this("{0:0.#0;-0.#0;'---'}") { } 
     public DoubleFormatter(string formatstring) : base(formatstring) { } 
    } 

    #endregion 
} 

public class ReportView: IReport 
{ 
    public ICollection<IColumn> Columns { get; set; } 
    public ReportView() { Columns = new List<IColumn>(); } 
} 

public class ReportColumn: IColumn 
{ 
    public ICollection<Value> Values { get; set; } 
    public ReportColumn() { Values = new List<Value>(); } 
} 

はそれはのような使われています:これについて訂正、追加する何か/あり

// Creating a report 
    IReport report = new ReportView(); 

    // Adding columns 
    IColumn mycolumn = new ReportColumn(); 
    mycolumn.Values.Add(new IntValue() { Data = 1 }); 
    mycolumn.Values.Add(new DoubleValue() { Data = 2.7 }); 
    mycolumn.Values.Add(new IntValue("{0:#,##0;-#,##0;'---'}", 15)); 
    mycolumn.Values.Add(new DoubleValue("{0:0.#0;-0.#0;'---'}", 2.9)); 
    report.Columns.Add(mycolumn); 

    // Looping through each column, and get each value in the formatted form 
    foreach(var column in report.Columns) 
    { 
     foreach(var value in column.Values) { value.ToString(); } 
    } 

場合は、私が聞いて喜んでいると思います。上記のようにBinary WorrierによってヒントされたVisitorパターンをチェックし、セットアップ全体をテストします。私が愚かな、あるいは貧しいデザインの選択肢を作るかどうか教えてください!私はおそらく、それを各値に提供することなく列全体のための単一のフォーマットを提供するために少し左右に変更する必要がありますが、基本フレームワークはそこにあります。

1

ジェネリック型を使用すると、あなたは狂ってしまうでしょう。ジェネリック医薬品の使用が間違っているかどうかを正確に評価するのに多くの時間を費やしていません。 。 。私はジェネリック医薬品が一切必要ではないと信じているからです。

レポートはちょうど列がちょうど値のリストを必要とし、実際には、一度の値は自分自身の世話をすることができ、それは列の型

interface IReport 
{ 
    IEnumerable<IColumn> Columns {get;} 
} 

気にしない、列のリストを必要とします値の型は気にしません。値がちょうど(おそらく単なる文字列として、またはおそらく与えられた長方形などで自己を「描く」ために)自己を表現できるようにする必要があり

interface IColumn 
{ 
    IEnumerable IValue Values {get;} 
} 

interface IValue 
{ 
    string AsString(); 
} 

あなたが持つことができますさまざまなタイプ(IntValueDoubleValueなど)の型付きの値の実装、そしてあなたが笑っているIValueインターフェイスを実装すると、

これは意味がありますか?

+0

ありがとうございます。私はそれを本当に素早く試みます。私は、データ注釈部分がIValueの実際の実装に入ると仮定します。 しかし、クラスのユーザーが実際に値型を提供するようにしたいのですが?今は確かに動作する文字列のセットです。しかし、計算でこのデータを使用し始めたいのであれば...? –

+0

私は[Visitor pattern](https:// en。wikipedia.org/wiki/Visitor_pattern)を使用して、さまざまな集計と計算をデータに実装します。値が既知のインタフェースを実装すると、訪問者は、インタフェース上の呼び出しの結果を気に入ることができます。例えばあなたはIValuesを通り抜けてその値を合計しようとする "DoubleSum"訪問者を持っています。二重に値を強制することができない場合は、それを投げたり、そのセルをゼロとして扱うことができます。 –

関連する問題