Verilerin kodda saklanması


17

Geçmişimde birkaç kez verileri kodda saklamak istedim. Bu, nadiren değişen ve bir veritabanına erişimin mümkün olmadığı, pratik veya istenildiği yerlerde kullanılan veriler olacaktır. Küçük bir örnek, ülkelerin bir listesini saklamak olabilir. Bunun için şöyle bir şey yapabilirsiniz:

public class Country
{
    public string Code { get; set; }
    public string EnglishName {get;set;}
}

public static class CountryHelper
{
    public static List<Country> Countries = new List<Country>
        {
            new Country {Code = "AU", EnglishName = "Australia"},
            ...
            new Country {Code = "SE", EnglishName = "Sweden"},
            ...
        };

    public static Country GetByCode(string code)
    {
        return Countries.Single(c => c.Code == code);
    }
}

Bunu geçmişte yapmaktan kaçtım çünkü veri setleri nispeten küçüktü ve nesneler oldukça basitti. Şimdi daha karmaşık nesneler (her biri 5-10 özellik, bazı özellikler sözlükler) ve toplam yaklaşık 200 nesne olacak bir şey ile çalışıyorum.

Verilerin kendisi çok nadiren değişir ve değiştiğinde o kadar da önemli değildir. Yani bir sonraki sürümüne yuvarlamak gayet iyi.

Veri kaynağımı montajda statik olarak depolanan bir şeye dönüştürmek için T4 veya ERB veya başka bir geçici çözüm kullanmayı planlıyorum.

Görünüşe göre seçeneklerim

  1. Verileri XML'de saklayın. XML dosyasını bir montaj kaynağı olarak derleyin. Gerektiği gibi veri yükleyin, tekrarlanan performans için yüklenen verileri sözlüğe kaydedin.
  2. Başlangıçta başlatılan bir tür statik nesne veya nesne oluşturun.

Seçenek 1'in performans sonuçlarını anladığımdan eminim. En azından önsezim, yıldız performansının olmayacağı.

Seçenek 2'ye gelince, ne yapacağımı bilmiyorum. Aslında bu veri C # kodu depolamak için en iyi yolu ve başlatmak için en iyi yolu bilmek için .NET framework iç hakkında bilmiyorum. Ben System.Globalization.CultureInfo.GetCulture(name)aslında ne istediğime çok benzer bir iş akışı olduğundan, nasıl çalıştığını görmek için .NET reflektör kullanarak etrafında dürttü . Ne yazık ki bu iz bir sona erdi extern, bu yüzden orada hiçbir ipucu. Benim örneğimde olduğu gibi, tüm verilerle statik bir özellik başlatılıyor mu? Yoksa talep üzerine nesneleri oluşturmak ve daha sonra önbelleklemek daha iyi olur mu?

    private static readonly Dictionary<string, Country> Cache = new Dictionary<string,Country>(); 

    public static Country GetByCode(string code)
    {
        if (!Cache.ContainsKey(code))
            return Cache[code];

        return (Cache[code] = CreateCountry(code));
    }

    internal static Country CreateCountry(string code)
    {
        if (code == "AU")
            return new Country {Code = "AU", EnglishName = "Australia"};
        ...
        if (code == "SE")
            return new Country {Code = "SE", EnglishName = "Sweden"};
        ...
        throw new CountryNotFoundException();
    }

Bunları bir kerede statik bir üyede oluşturmanın avantajı, tüm nesnelere bakmak ve bunları sorgulamak için LINQ veya başka bir şey kullanabilmenizdir. Buna rağmen şüpheli bir başlangıç ​​performans cezası var. Birisinin bu konuda tecrübesi olduğunu ve fikirlerini paylaşabileceğini umuyorum!


5
200 nesne? Performans için endişelenmeniz gerektiğini düşünmüyorum, özellikle de bir kerelik bir maliyetle.
svick

1
Nesneleri kodda saklamak, sonra da normal olarak bağımlılık enjeksiyonuna bir referans iletmek için başka bir seçeneğiniz var. Bu şekilde, nasıl yapılandırıldıklarını değiştirmek isterseniz, doğrudan her yerden onlara işaret eden statik referanslarınız olmaz.
Amy Blankenship

@svick: Bu doğru. Muhtemelen performans konusunda aşırı temkinli davranıyorum.
Nisan'ta mroach

@AmyBlankenship Her zaman yardımcı yöntemler kullanırdım, ama DI iyi bir fikir. Bunu düşünmemiştim. Bunu bir deneyeceğim ve modeli beğenip beğenmediğimi göreceğim. Teşekkürler!
Nisan'ta mroach

" Verilerin kendisi çok nadiren değişiyor ve değiştiğinde gerçekten o kadar da önemli değil. " Bunu Bosnalılara, Sırplara, Hırvatlara, Ukraynalılara, Güney Sudanilere, eski Güney Yemenlere, eski Sovyetlere ve ark. Bunu Euro para birimine geçişle uğraşmak zorunda olan her programcıya veya Yunanistan'ın ondan potansiyel çıkışıyla uğraşmak zorunda kalabilecek programcılara söyleyin. Veriler veri yapılarına aittir. XML dosyaları iyi çalışır ve kolayca değiştirilebilir. Ditto SQL veritabanları.
Ross Patterson

Yanıtlar:


11

Birinci seçenekle giderdim. Basit ve okunması kolay. Kodunuza bakan bir başkası kodu hemen anlayacaktır. Gerekirse XML verilerinizi güncellemek de daha kolay olacaktır. (Ayrıca, güzel bir kullanıcı arabiriminiz varsa ve dosyaları ayrı olarak depoladıysanız kullanıcının bunları güncellemesine izin verebilirsiniz)

Sadece ihtiyacınız olduğunda optimize edin - erken optimizasyon kötüdür :)


3
C # yazıyorsanız, ufacık XML'leri hızlı bir şekilde ayırabilen bir platformda çalışıyorsunuz. Bu yüzden performans sorunları hakkında rahatsız edici değil.
James Anderson

7

Bunların temelde anahtar / değer çiftleri olduğu göz önüne alındığında, bu dizeleri derleme zamanında derlemenize katıştırılmış bir kaynak dosyası olarak saklamanızı öneririm . Daha sonra bunları a ResourceManger. Kodunuz şöyle görünecektir:

private static class CountryHelper
{
    private static ResourceManager rm;

    static CountryHelper()
    {
        rm = new ResourceManager("Countries",  typeof(CountryHelper).Assembly);
    }

    public static Country GetByCode(string code)
    {
        string countryName = rm.GetString(code + ".EnglishName");
        return new Country { Code = code, EnglishName = countryName };
    }
}

Biraz daha verimli hale getirmek için hepsini bir kerede yükleyebilirsiniz, şöyle:

private static class CountryHelper
{
    private static Dictionary<string, Country> countries;

    static CountryHelper()
    {
        ResourceManager rm = new ResourceManager("Countries",  typeof(CountryHelper).Assembly);
        string[] codes = rm.GetString("AllCodes").Split("|"); // AU|SE|... 
        countries = countryCodes.ToDictionary(c => c, c => CreateCountry(rm, c));
    }

    public static Country GetByCode(string code)
    {
        return countries[code];
    }

    private static Country CreateCountry(ResourceManager rm, string code)
    {
        string countryName = rm.GetString(code + ".EnglishName");
        return new Country { Code = "SE", EnglishName = countryName };
    }
}

Uygulamanızı birden çok dili desteklemeniz gerektiğinde bu size çok yardımcı olacaktır .

Bu bir WPF uygulamasıysa, kaynak sözlüğü çok daha belirgin bir çözümdür.


2

Karar gerçekten: Yalnızca geliştiricinin (veya bir derleme sistemine erişimi olan herhangi birisinin), değiştirilmesi gerektiğinde verileri değiştirmesini mi yoksa son kullanıcının veya muhtemelen son kullanıcının kuruluşundan birinin verileri değiştirebilmesini mi istiyorsunuz? veri?

İkinci durumda, kullanıcı tarafından okunabilir ve kullanıcı tarafından düzenlenebilir biçimde bir dosyanın kullanıcının erişebileceği bir yerde saklanmasını öneririm. Açıkçası verileri okurken dikkatli olmanız gerekir; bulduğunuz her şeye geçerli veri olduğu konusunda güvenilemez. Muhtemelen JSON XML tercih ederim; Anlamayı ve doğru olmayı daha kolay buluyorum. Veri biçimi için bir düzenleyici olup olmadığını kontrol edebilirsiniz; örneğin, MacOS X'te bir plist kullanıyorsanız, o zaman verilerin yakınına gitmesi gereken her kullanıcının bilgisayarında iyi bir düzenleyici olacaktır.

İlk durumda, veriler yapınızın bir parçasıysa, gerçekten bir fark yaratmaz. Sizin için en uygun olanı yapın. C ++ 'da, verileri minimum ek yük ile yazmak, makrolara izin verilen birkaç yerden biridir. Verilerin bakımı üçüncü bir tarafça yapılırsa, onlara kaynak dosyaları gönderebilir, düzenlemelerine izin verebilirsiniz ve daha sonra bunları kontrol etmek ve projenizde kullanmaktan sorumlusunuz.


1

Bunun en iyi örneği muhtemelen WPF'dir ... Formlarınız temelde XML olan XAML'de yazılmıştır, ancak .NET'in bir nesne temsiline çok hızlı bir şekilde rehidrate edebilmesi dışında. XAML dosyası bir BAML dosyasına derlenir ve ".resources" dosyasına sıkıştırılır, bu dosya daha sonra derleme içine gömülür (.NET reflektörün en son sürümü size bu dosyaları gösterebilir).

Bunu destekleyecek büyük bir belge olmamasına rağmen, MSBuild aslında bir XAML dosyası alabilir, BAML'ye dönüştürebilir ve sizin için gömebilir (formlar için doğru mu?).

Yani, yaklaşımım şöyle olacaktır: Verilerinizi XAML'de saklayın (sadece bir nesne grafiğinin XML temsilidir), XAML'yi bir kaynak dosyasına doldurun ve derlemeye gömün. Çalışma zamanında XAML'yi alın ve rehidrate etmek için XamlServices kullanın. Ardından, statik bir üyeye sarın veya kodunuzun geri kalanından tüketmek için daha test edilebilir olmasını istiyorsanız, Bağımlılık Enjeksiyonunu kullanın.

Yararlı referans malzemesi olan birkaç bağlantı:

Bir yan notta, verilerin daha kolay değiştirilmesini istiyorsanız, gerçekten karmaşık olan nesneleri ayarlar mekanizması aracılığıyla yüklemek için app.config veya web.config dosyalarını kullanabilirsiniz. Ayrıca XAML dosyasını dosya sisteminden de yükleyebilirsiniz.


1

Sanırım System.Globalization.CultureInfo.GetCulture (name), bir sistem dosyasından veri almak için Windows API'lerini çağırır.

Koddaki verileri yüklemek için, tıpkı @svick'in söylediği gibi, 200 nesneyi yüklemek üzereyseniz, kodunuz bazı düşük bellek aygıtlarında çalışmadığı sürece çoğu durumda sorun olmayacaktır.

Verileriniz asla değişmezse, deneyimim basitçe şöyle bir liste / sözlük kullanıyor:

private static readonly Dictionary<string, Country> _countries = new Dictionary<string,Country>();

Ancak sorun, sözlüğünüzü başlatmak için bir yol bulmanız gerektiğidir. Sanırım bu acı noktası.

Böylece sorun "Verileriniz nasıl oluşturulur?" Olarak değiştirilir. Ancak bu, ihtiyacınız olanla ilgilidir.

Ülkeler için veri oluşturmak istiyorsanız,

System.Globalization.CultureInfo.GetCultures()

CultrueInfo dizisini edinin ve sözlüğünüzü ihtiyaçlarınıza göre başlatabilirsiniz.

Ve elbette kodu statik bir sınıfa koyabilir ve sözlüğü statik yapıcıda aşağıdaki gibi başlatabilirsiniz:

public static class CountryHelper
{
    private static readonly Dictionary<string, Country> _countries;
    static CountryHelper()
    {
        _countries = new Dictionary<string,Country>();
        // initialization code for your dictionary
        System.Globalization.CultureInfo[] cts = System.Globalization.CultureInfo.GetCultures(System.Globalization.CultureTypes.AllCultures);
        for(int i=0; i < cts.Length; i++)
        {
            _countries.Add(cts[i].Name, new Country(cts[i]));
        }
    }

    public static Country GetCountry(string code)
    {
        Country ct = null;
        if(this._countries.TryGet(code, out ct))
        {
            return ct;
        } else
        {
            Log.WriteDebug("Cannot find country with code '{0}' in the list.", code);
            return null;
        }
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.