2016-01-04 16 views
5

Kullanıcının bir dizi KPI, çizelge, toplama fonksiyonu ve diğer parametreler arasından seçim yapabileceği basit bir raporlama aracı oluşturmaya çalışıyorum; Daha sonra tüm verilerle özel bir model döndüren çağrılır. Bu daha sonra bir MVC/WPF uygulamasında görüntülenebilir (her ikisi de olabilir).Yarı reklam raporu için genel arayüzler

Kullanıcılar çeşitli ülkelerden olabileceğinden, mevcut numaraların kullandığı dile ve sayı biçimlerine uygun bir şekilde tüm sayı ve üstbilgileri portre yapmak için veri ek açıklamalarını kullanmak istiyorum.

Verilerin yüklenmesi ve tüm bu şeyler gayet iyi çalışıyor, sorun değil. Ayrıca, veri ek açıklamaları kullanıyorum, bu yüzden tüm dil/kültüre özgü ayarlar dikkate alınır. Sorun, tüm verileri kullanıcıya göstermek istediğim modele koymaya başladığımda başlar.

Yapmaya çalıştığım bir sütun koleksiyonu içeren bir Rapor sınıfına sahip olmak. Her sütun, int/double/... değerlerinin bir listesi olabilir. Şimdi, WCF ile uğraştığım ve yukarıdaki açıklamanın (bildiğim kadarıyla) jeneriklerin kullanımı anlamına geldiğinden, sınıfları/wcf işlemleri için [KnownType] veya [ServiceKnownType] yöntemlerini kullanabileceğimi düşünüyorum. baz tipi veya dönüş değeri olarak arabirim. Bunu gerçekten denemedim, ama bana oldukça mantıklı görünen bazı iyi açıklamalar buldum, bu yüzden bu kısım için büyük bir sorunum olmayacağını düşünüyorum (en azından umarım).

Şu anda, benim arayüzleri gibidir böyle (Ben gerçek bir sorun odaklanmak basitleştirilmiş):

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; } } 

her sütundaki değer bir int/çift/... sanırım olabilir beri garip görünüyor

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

, ne zamandan beri tekini: Ben gibi (ben bir koleksiyon türüne veri açıklama özelliğini kullanabilirsiniz sanmıyorum) sadece değer için gerçek bir sınıf olması gerekir IValue'yi uygulayan ve onunla yapılabilecek genel bir sınıf değeri yapın, ama eğer aptalca bir şey yaparsam ve Her olası tip için bir sınıf hazırlayın (şimdi yazdığımda, kulağa gerçekten kötü geliyor, biliyorum), DisplayFormat özniteliğini kullanabilirim ve kullanıcıya sunacağı şekilde endişelenmenize gerek yok. Her zaman uygun olurum. Şimdi

, IColumn ve İReport uygulamak sınıflar için, bu basit:

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>>(); } 
} 

arayüzleri ve sınıfların listesinden, hemen bu imkansız bir rapor bulundurmak yapar göreceksiniz nerede bazı sütunlar başka türler var. Bu nedenle, bazı sütunların int, bazılarının iki katı olduğu bir rapor oluşturmak mümkün değildir. ... IReport'taki genel kısıtlama bir tür belirttiğiniz için, tüm sütunlar için buna yapışmış olursunuz. Her sütun ... Ve gerçekten istediğim bu.

Hiç bir yere gitmediğimi ve muhtemelen gerçekten basit bir şeyi kaçırdığımı hissediyorum, bu yüzden doğru yönde bir dürtme takdir edilecektir.

TL; DR: Genel olmayan bir koleksiyonda genel bir koleksiyon nasıl alabilirim?

+0

Raporunuzu "yeni bir IReport " olarak oluşturamaz mısınız? – Baldrick

+0

Şimdi görebildiğim kadarıyla, bu, IConvertible uygulama sınıfını değil, IConvertible olmasını beklediğinden, int veya double sütun eklememe izin vermiyor. –

cevap

2

Tamam, önerilen çözümlerden ilham aldım ve aşağıdaki gibi bir varyasyon uyguladım. Jenerikleri çok fazla kullanmak istemediğimi anlıyorum ama yine de beni rahatsız ediyor. Sonuçta, birkaç türden sütun (veya değer) istiyorum. Jenerikler bunun için var. Ayrıca, alanların biçimlendirmesini sağlamak için yerleşik bir mekanizma sağlamak istedim.

IReport ve IColumn arabirimlerini oldukça basit bıraktım, ancak IColumn arabirimindeki bir IValue arabirimine başvurmuyorum. Bunun yerine, biçimlendirme ve veri alma için bazı temel çerçeveyi tanımladığım soyut bir sınıf Değeri kullanıyorum (dize biçiminde).

Gerçek IntValue/DoubleValue ve Value baseclass arasında, Genel alan arabirimi sağlayan bir genel Value sınıfını ekledim. Bu, Veri alanını sağladığından başka bir şey yapmadan IntValue/DoubleValue sınıfları ve Değer temel sınıf kurgusunda oluşturduğum Biçemleyici'yi kullanarak normal ToString yöntemini kullanan AsFormattedString yöntemini uygulayın.

Bu biçimlendiricinin gerçek uygulaması, IntValue/DoubleValue sınıflarında sağlanmıştır ve zaten sabit kodlanmış veya sınıf kullanıcısı tarafından sağlanan özel bir standart biçimi kullanma olanağı sunar.

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>(); } 
} 

Böyle olarak kullanılır: Bu konuda düzeltilmiş eklenecek bir şey/var

// 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(); } 
    } 

, ben duymak memnun olurum. Binary Worrier tarafından yukarıda ima edilen Ziyaretçi şablonunu kontrol edip tüm kurulumu test edeceğim. Eğer aptal ya da kötü tasarım seçimleri pls yaparsam haberim olsun! Her bir değere sağlamaya gerek kalmadan sütunun tamamı için tek bir biçim sağlamak için sola ve sağa biraz değiştirmem gerekecek, ama bence temel çerçeve var.

1

Ben türler için jenerik kullanmanın seni delirteceğini düşünüyorum. Jenerik kullanımıyla ilgili yanlış olanı tam olarak değerlendirmek için fazla zaman harcamadım. . . çünkü jeneriklere ihtiyacınız olduğuna inanmıyorum.

rapor yalnızca değerleri kendileri sonra bakabilirsiniz kez, gerçekten sütun sadece değerler listesi ihtiyacı

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

sütun türleri ile ilgili bakım ve gelmez, sütunların listesini ihtiyacı

Değerlerin türlerini umursamıyor.

interface IColumn 
{ 
    IEnumerable IValue Values {get;} 
} 

değeri sadece

interface IValue 
{ 
    string AsString(); 
} 

Bir olabilir (sadece bir dize ya da muhtemelen belirli bir dikdörtgenin vb yılında kendini "çizmek" olarak muhtemelen) kendi kendini işlemek edebilmek gerekiyor Farklı türler için (IntValue, DoubleValue vb.) Değer uygulaması yazılır ve bir kez güldüğünüz IValue arayüzünü uygular.

Bu mantıklı mı?

+0

Yapar, teşekkür ederim. Bunu çok çabuk deneyeceğim. Veri açıklama kısmı varsayardım, o zaman sadece IValue'un gerçek uygulamasına girer. Bununla birlikte, eğer sınıfın kullanıcılarını gerçekten değer türleri sağlamak için zorlamak istersem? Şu anda kesinlikle işe yarayan bir dizi ip var. Ama eğer bu verileri hesaplamalarda kullanmaya başlamak istersem ...? –

+0

Satır aşağı [Ziyaretçi kalıbı] (https: // en.wikipedia.org/wiki/Visitor_pattern) veriler üzerinde farklı toplama ve hesaplamalar yapmak. Değerler, bilinen bir arayüzü uyguladıktan sonra, ziyaretçi arayüzdeki çağrıların sonuçlarıyla istediğini yapabilir. Örneğin. IValues'den geçen ve değerlerini toplamaya çalışan bir "DoubleSum" ziyaretçisisiniz. Eğer herhangi bir değeri bir çifte zorlayamazsa, atmak ya da o hücreyi sıfır olarak ya da neye benzediğini tedavi edebilir. –