Lưu trữ dữ liệu trong mã


17

Một vài lần trong quá khứ tôi đã muốn lưu trữ dữ liệu bằng mã. Đây sẽ là dữ liệu hiếm khi thay đổi và được sử dụng ở những nơi không thể truy cập vào cơ sở dữ liệu, thực tế hoặc mong muốn. Một ví dụ nhỏ sẽ lưu trữ một danh sách các quốc gia. Cho rằng bạn có thể làm một cái gì đó như:

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

Trước đây tôi đã từng làm điều này vì các bộ dữ liệu tương đối nhỏ và các đối tượng khá đơn giản. Bây giờ tôi đang làm việc với một cái gì đó sẽ có các đối tượng phức tạp hơn (mỗi thuộc tính 5 - 10, một số thuộc tính là từ điển) và tổng số khoảng 200 đối tượng.

Dữ liệu tự thay đổi rất hiếm khi và khi nó thay đổi, nó thực sự không quan trọng lắm. Vì vậy, đưa nó vào phiên bản phát hành tiếp theo là hoàn toàn tốt.

Tôi dự định sử dụng T4 hoặc ERB hoặc một số giải pháp tạo khuôn mẫu khác để biến nguồn dữ liệu của tôi thành thứ gì đó được lưu trữ tĩnh trong cụm.

Có vẻ như lựa chọn của tôi là

  1. Lưu trữ dữ liệu trong XML. Biên dịch tệp XML dưới dạng tài nguyên lắp ráp. Tải dữ liệu khi cần, lưu trữ dữ liệu đã tải vào Từ điển để sử dụng lại hiệu suất.
  2. Tạo một số loại đối tượng tĩnh hoặc các đối tượng được khởi tạo khi khởi động.

Tôi khá chắc chắn rằng tôi hiểu ý nghĩa hiệu suất của tùy chọn 1. Ít nhất, linh cảm của tôi là nó sẽ không có hiệu suất sao.

Đối với lựa chọn 2, tôi không biết phải làm gì. Tôi không biết đủ về các phần bên trong của .NET framework để biết cách tốt nhất để thực sự lưu trữ dữ liệu này trong mã C # và các cách tốt nhất để khởi tạo nó. Tôi đã sử dụng .NET Reflector để xem cách thức System.Globalization.CultureInfo.GetCulture(name)hoạt động, vì đây thực sự là một quy trình công việc rất giống với những gì tôi muốn. Thật không may, con đường đó đã kết thúc tại một extern, vì vậy không có gợi ý ở đó. Là khởi tạo một thuộc tính tĩnh với tất cả dữ liệu, như trong ví dụ của tôi, cách để đi? Hoặc sẽ tốt hơn để tạo các đối tượng theo yêu cầu và sau đó lưu trữ chúng, như thế này?

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

Lợi thế để tạo chúng cùng một lúc trong một thành viên tĩnh là bạn có thể sử dụng LINQ hoặc bất cứ thứ gì khác để xem tất cả các đối tượng và truy vấn chúng nếu bạn muốn. Mặc dù tôi nghi ngờ làm điều này có một hình phạt hiệu suất khởi động. Tôi hy vọng ai đó có kinh nghiệm với điều này và có thể chia sẻ ý kiến ​​của họ!


5
200 đối tượng? Tôi không nghĩ bạn phải lo lắng về hiệu suất, đặc biệt nếu đó là chi phí một lần.
Svick

1
Bạn có một tùy chọn khác, đó là lưu trữ các đối tượng trong mã, sau đó chuyển tham chiếu đến phép tiêm phụ thuộc thông thường. Theo cách đó, nếu bạn muốn thay đổi cách chúng được xây dựng, bạn không có các tham chiếu tĩnh trỏ trực tiếp đến chúng từ mọi nơi.
Amy Blankenship

@svick: Đó là sự thật. Có lẽ tôi đang quá thận trọng về hiệu suất.
mroach

@AmyBlankenship Tôi luôn luôn sử dụng các phương thức trợ giúp, nhưng DI là một ý kiến ​​hay. Tôi đã không nghĩ về nó. Tôi sẽ thử và xem tôi có thích mẫu đó không. Cảm ơn!
mroach

" Dữ liệu tự thay đổi rất hiếm khi và khi nó thay đổi, nó thực sự không quan trọng lắm. " Hãy nói điều đó với người Bosnia, Serbs, Croats, Ukranians, Nam Sudanis, Nam Yemen cũ, Liên Xô cũ, et al. Nói với mọi lập trình viên, những người phải đối phó với việc chuyển đổi sang tiền Euro, hoặc với các lập trình viên, những người có thể phải đối phó với lối thoát tiềm năng của Hy Lạp từ nó. Dữ liệu thuộc về cấu trúc dữ liệu. Các tệp XML hoạt động độc đáo và có thể dễ dàng thay đổi. Cơ sở dữ liệu SQL Ditto.
Ross Patterson

Câu trả lời:


11

Tôi sẽ đi với tùy chọn một. Thật đơn giản và dễ đọc. Một người khác nhìn vào mã của bạn sẽ hiểu nó ngay lập tức. Việc cập nhật dữ liệu XML của bạn cũng sẽ dễ dàng hơn nếu cần. (Ngoài ra, bạn có thể cho phép người dùng cập nhật chúng nếu bạn có một giao diện đẹp và lưu trữ các tệp riêng biệt)

Chỉ tối ưu hóa nó nếu bạn cần - tối ưu hóa sớm là xấu :)


3
Nếu bạn đang viết C # thì bạn đang chạy trên một nền tảng có thể phân tích cú pháp XML nhỏ một cách nhanh chóng. Vì vậy, nó không đáng bận tâm về các vấn đề hiệu suất.
James Anderson

7

Vì đây thực chất là các cặp khóa-giá trị, tôi khuyên bạn nên lưu trữ các chuỗi này dưới dạng tệp tài nguyên được nhúng trong tổ hợp của bạn tại thời điểm biên dịch. Sau đó bạn có thể đọc chúng ra bằng cách sử dụng a ResourceManger. Mã của bạn sẽ trông giống như thế này:

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

Để làm cho nó hiệu quả hơn một chút, bạn có thể tải tất cả chúng cùng một lúc, như thế này:

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

Điều này sẽ giúp bạn rất nhiều nếu bạn cần làm cho ứng dụng của mình hỗ trợ nhiều ngôn ngữ.

Nếu điều này xảy ra là một ứng dụng WPF, một từ điển tài nguyên là một giải pháp rõ ràng hơn nhiều.


2

Quyết định thực sự là: Bạn có muốn chỉ nhà phát triển (hoặc bất kỳ ai có quyền truy cập vào hệ thống xây dựng) sửa đổi dữ liệu khi cần sửa đổi hoặc người dùng cuối hoặc có thể ai đó trong tổ chức của người dùng cuối có thể sửa đổi dữ liệu?

Trong trường hợp sau, tôi đề nghị rằng một tệp ở định dạng người dùng có thể đọc và người dùng có thể chỉnh sửa sẽ được lưu trữ ở nơi người dùng có thể truy cập. Rõ ràng khi bạn đọc dữ liệu bạn cần phải cẩn thận; bất cứ điều gì bạn tìm thấy không thể được tin cậy là dữ liệu hợp lệ. Tôi có lẽ thích JSON hơn XML; Tôi thấy dễ hiểu hơn và hiểu đúng. Bạn có thể kiểm tra nếu có một trình soạn thảo có sẵn cho định dạng dữ liệu; ví dụ: nếu bạn sử dụng một số nguyên trên MacOS X thì mọi người dùng nên đến gần dữ liệu sẽ có một trình chỉnh sửa phù hợp trên máy tính của anh ta.

Trong trường hợp đầu tiên, nếu dữ liệu là một phần của bản dựng của bạn, nó không thực sự tạo ra sự khác biệt. Làm những gì thuận tiện nhất cho bạn. Trong C ++, ghi dữ liệu với chi phí tối thiểu là một trong số ít nơi cho phép macro. Nếu công việc duy trì dữ liệu được thực hiện bởi bên thứ ba, bạn có thể gửi cho họ các tệp nguồn, để họ chỉnh sửa chúng và sau đó bạn có trách nhiệm kiểm tra chúng và sử dụng chúng trong dự án của bạn.


1

Ví dụ tốt nhất về điều này tồn tại có lẽ là WPF ... Các biểu mẫu của bạn được viết bằng XAML, về cơ bản là XML, ngoại trừ .NET có thể bù nước lại thành biểu diễn đối tượng rất nhanh. Tệp XAML được biên dịch thành tệp BAML và được nén vào tệp ".resource", sau đó được nhúng trong cụm (phiên bản mới nhất của .NET Reflector có thể hiển thị cho bạn các tệp này).

Mặc dù không có tài liệu tuyệt vời nào để hỗ trợ nó, nhưng trên thực tế MSBuild có thể lấy tệp XAML, chuyển đổi nó thành BAML và nhúng nó cho bạn (nó có phải là biểu mẫu không?).

Vì vậy, cách tiếp cận của tôi sẽ là: Lưu trữ dữ liệu của bạn trong XAML (nó chỉ là biểu diễn XML của biểu đồ đối tượng), nhét XAML vào tệp tài nguyên và nhúng nó vào trong cụm. Trong thời gian chạy, lấy XAML và sử dụng XamlService để bù nước. Sau đó, bọc nó trong một thành viên tĩnh hoặc sử dụng Dependency Injection nếu bạn muốn nó dễ kiểm tra hơn, để tiêu thụ nó từ phần còn lại của mã của bạn.

Một vài liên kết là tài liệu tham khảo hữu ích:

Bên cạnh đó, bạn thực sự có thể sử dụng các tệp app.config hoặc web.config để tải lên các đối tượng phức tạp hợp lý thông qua cơ chế cài đặt nếu bạn muốn dữ liệu được thay đổi dễ dàng hơn. Và bạn cũng có thể tải XAML từ hệ thống tệp.


1

Tôi đoán System.Globalization.CARMInfo.GetCARM (tên) sẽ gọi các API Windows để lấy dữ liệu từ một tệp hệ thống.

Để tải dữ liệu theo mã, giống như @svick đã nói, nếu bạn sắp tải 200 đối tượng, hầu hết các trường hợp sẽ không thành vấn đề, trừ khi mã của bạn chạy trên một số thiết bị bộ nhớ thấp.

Nếu dữ liệu của bạn không bao giờ thay đổi, trải nghiệm của tôi chỉ đơn giản là sử dụng danh sách / từ điển như:

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

Nhưng vấn đề là bạn cần tìm cách khởi tạo từ điển của mình. Tôi đoán đây là điểm đau.

Vì vậy, vấn đề được thay đổi thành "Làm thế nào để tạo dữ liệu của bạn?". Nhưng điều này liên quan đến những gì bạn cần.

Nếu bạn muốn tạo dữ liệu cho các quốc gia, bạn có thể sử dụng

System.Globalization.CultureInfo.GetCultures()

lấy mảng CultrueInfo và bạn có thể khởi tạo từ điển với nhu cầu của mình.

Và tất nhiên bạn có thể đặt mã vào một lớp tĩnh và khởi tạo từ điển trong hàm tạo tĩnh như:

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;
        }
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.