2008-09-19 32 views
23

Đối với một dự án mã nguồn mở, tôi đang tìm kiếm một thực thi tốt, đơn giản của một từ điển được hỗ trợ bởi một tệp. Có nghĩa là, nếu một ứng dụng bị treo hoặc khởi động lại từ điển sẽ giữ trạng thái của nó. Tôi muốn nó cập nhật tệp cơ bản mỗi khi từ điển được chạm vào. (Thêm giá trị hoặc xóa giá trị). Một FileWatcher là không cần thiết nhưng nó có thể hữu ích.Tìm kiếm một triển khai từ điển liên tục độc lập đơn giản trong C#

class PersistentDictionary<T,V> : IDictionary<T,V> 
{ 
    public PersistentDictionary(string filename) 
    { 

    } 
} 

Yêu cầu:

  • mã nguồn mở, không có sự phụ thuộc vào mã nguồn gốc (không sqlite)
  • Lý tưởng nhất là một thực hiện rất ngắn và đơn giản
  • Khi cài đặt hoặc xóa một giá trị nó không nên ghi lại toàn bộ tệp cơ bản, thay vào đó nó sẽ tìm kiếm vị trí trong tệp và cập nhật giá trị.

câu hỏi tương tự

+0

s/persistant/dai dẳng/g – eleven81

+0

Checkout: [ 'PersistentDictionary'] (http://izlooite.blogspot.com/2011/04/persistent-dictionary.html) lớp –

+2

Bạn đang đùa tôi. Một câu hỏi được hỏi bởi Sam Saffron là đóng cửa như là chủ đề tắt? Bạn mọi người đang freaking điên. –

Trả lời

23
  • bplustreedotnet

    Gói bplusdotnet là một thư viện triển khai cấu trúc dữ liệu tương thích chéo trong C#, java, và Python mà rất hữu ích cho các ứng dụng mà cần phải lưu trữ và lấy thông tin liên tục. Cấu trúc dữ liệu bplusdotnet giúp dễ dàng để lưu các khóa chuỗi được liên kết với các giá trị vĩnh viễn.

  • ESENT Managed Interface

    Không phải 100% mã được quản lý nhưng nó có giá trị nhắc đến nó như thư viện không được quản lý tự nó đã là một phần của tất cả các cửa sổ XP/2003/Vista/7 hộp

    ESENT là một lưu trữ cơ sở dữ liệu nhúng engine (ISAM) là một phần của Windows. Nó cung cấp lưu trữ dữ liệu hiệu suất cao, giao dịch, đồng thời, hiệu suất cao với khóa cấp hàng, ghi nhật ký ghi trước và cách ly ảnh chụp nhanh. Đây là trình bao bọc được quản lý cho API ESENT Win32.

  • Akavache

    * Akavache là không đồng bộ, kiên trì bộ nhớ cache chính có giá trị tạo ra cho văn bản máy tính để bàn có nguồn gốc và các ứng dụng di động trong C#. Hãy suy nghĩ về nó như memcached cho các ứng dụng máy tính để bàn.

- The C5 Generic Collection Library

C5 cung cấp các cấu trúc chức năng và dữ liệu không được cung cấp bởi các tiêu chuẩn Net System.Collections.Generic namespace, chẳng hạn như dai dẳng cấu trúc dữ liệu cây, đống dựa hàng đợi ưu tiên, băm lập chỉ mục danh sách mảng và danh sách được liên kết và sự kiện về thay đổi bộ sưu tập.

+1

Cảm ơn lời khuyên, hãy thêm chúng vào danh sách thư viện hữu ích của tôi. –

+1

@lubos Từ tài liệu C5: * Bộ sưu tập được sắp xếp liên tục triển khai giao diện IPersistentSorted và là một bộ sưu tập được sắp xếp để người dùng có thể chụp ảnh chỉ đọc hoặc “copy” không bị ảnh hưởng bởi bản cập nhật cho bộ sưu tập gốc . Nó mô tả phương thức Snapshot() trả về một bộ sưu tập được sắp xếp với chính xác các mục giống như bộ sưu tập được sắp xếp liên tục. Các lớp sưu tập TreeSet và TreeBag là các bộ sưu tập được sắp xếp liên tục; các phương thức Snapshot() của chúng mất thời gian liên tục, nhưng sub2 cập nhật liên tiếp đến cây đã cho mất nhiều thời gian và không gian hơn * - không phải là yêu cầu –

+0

Cấu trúc dữ liệu C5 không liên tục theo cách đó. Xem http://en.wikipedia.org/wiki/Persistent_data_structure –

1

Âm thanh mát mẻ, nhưng làm thế nào bạn sẽ nhận được xung quanh thay đổi với giá trị được lưu trữ (nếu đó là một loại tham chiếu)? Nếu nó không thay đổi thì tất cả là tốt, nhưng nếu không phải bạn đang nhồi nhét :-)

Nếu bạn không giao dịch với giá trị bất biến, tôi nghi ngờ cách tiếp cận tốt hơn là xử lý sự kiên trì ở mức giá trị và xây dựng lại từ điển khi cần thiết.

(được chỉnh sửa để thêm làm rõ)

+0

Id vui lòng chỉ hỗ trợ dữ liệu không thay đổi (tôi có thể thêm giao diện với OnChanged cho loại công cụ này hoặc có thể là đối tượng proxy có thể hơi xấu) –

0

Chỉ cần sử dụng tuần tự hóa. Nhìn vào lớp BinaryFormatter.

+0

Điều đó sẽ không cho tôi hiệu suất Im sau –

+0

Bạn nên chỉnh sửa câu hỏi để bao gồm các yêu cầu về hiệu suất của bạn. – spoulson

+2

Tôi đã làm "Khi đặt hoặc xóa giá trị, nó sẽ không ghi lại toàn bộ tệp cơ bản, thay vào đó nó sẽ tìm kiếm vị trí trong tệp và cập nhật giá trị." –

0

Tôi không biết gì để giải quyết vấn đề của bạn. Nó sẽ cần phải là một cấu trúc kích thước cố định, để bạn có thể đáp ứng các yêu cầu của việc có thể viết lại các bản ghi mà không cần viết lại toàn bộ tệp.

Điều này có nghĩa là các chuỗi bình thường không hoạt động.

+0

Có lẽ người ta có thể chỉ định độ dài tối đa trong ctor và nó có ném khi mọi người thêm các giá trị lớn hơn không? – Quibblesome

0

Giống như Douglas đã nói, bạn cần biết kích thước cố định của các loại (cả T và V). Ngoài ra, các cá thể có độ dài thay đổi trong lưới đối tượng được tham chiếu bởi bất kỳ trường hợp nào trong số đó đều bị loại bỏ.

Tuy nhiên, việc triển khai từ điển được hỗ trợ bởi một tệp khá đơn giản và bạn có thể sử dụng lớp BinaryWriter để ghi các loại vào đĩa, sau khi kế thừa hoặc đóng gói lớp Dictionary<TKey, TValue>.

+0

Toàn bộ điểm sẽ hỗ trợ các loại có kích thước động ... SQL thực hiện và sử dụng một tệp duy nhất để tôi không thấy lý do tại sao điều này không thể –

+0

Sử dụng cơ sở dữ liệu đằng sau nó thay vì tệp. Tôi sẽ không tái phát minh bất cứ điều gì đã được thực hiện tốt hơn trước đây. –

+0

SQL sử dụng bộ nhớ đệm và các tác vụ đa luồng để thực hiện theo cách đó. Để làm điều đó tất cả cho một đối tượng từ điển duy nhất là điên. Bạn đang yêu cầu không thực tế. –

6

một cách là sử dụng Extensible Storage Engine được tích hợp sẵn để lưu trữ nội dung của bạn. Đó là cơ sở dữ liệu giành chiến thắng gốc hỗ trợ lập chỉ mục, giao dịch, v.v.

+1

ESE là tốt cho vấn đề này ... aynde có một số wrappers cho nó. –

0

Xem xét tệp bộ nhớ được ánh xạ. Tôi không chắc chắn nếu có hỗ trợ trực tiếp trong. NET, nhưng bạn có thể pinvoke các cuộc gọi Win32.

0

tôi đã không thực sự sử dụng nó, nhưng dự án này dường như cung cấp một mmap() - như thực hiện trong C#

Mmap

-1

Tôi không nhiều của một lập trình viên, nhưng sẽ không tạo ra một thực sự định dạng XML đơn giản để lưu trữ dữ liệu của bạn thực hiện thủ thuật?

<dico> 
    <dicEntry index="x"> 
    <key>MyKey</key> 
    <val type="string">My val</val> 
    </dicEntry> 
    ... 
</dico> 

Từ đó, bạn tải các tập tin XML DOM và điền vào từ điển của bạn như bạn thích,

XmlDocument xdocDico = new XmlDocument(); 
string sXMLfile; 
public loadDico(string sXMLfile, [other args...]) 
{ 
    xdocDico.load(sXMLfile); 
    // Gather whatever you need and load it into your dico 
} 
public flushDicInXML(string sXMLfile, dictionary dicWhatever) 
{ 
    // Dump the dic in the XML doc & save 
} 
public updateXMLDOM(index, key, value) 
{ 
    // Update a specific value of the XML DOM based on index or key 
} 

Sau đó, bất cứ khi nào bạn muốn, bạn có thể cập nhật các DOM và lưu nó vào đĩa.

xdocDico.save(sXMLfile);

Nếu bạn có thể đủ khả năng để giữ cho DOM trong bộ nhớ hiệu suất-khôn ngoan, nó rất dễ dàng để giải quyết. Tùy thuộc vào yêu cầu của bạn, bạn có thể thậm chí không cần từ điển.

+3

Anh ấy không muốn viết lại toàn bộ tệp trên các bản cập nhật. – cjk

+0

Oups, có vẻ như tôi không chú ý đủ. –

14

Hãy để tôi phân tích này:

  1. Lấy thông tin bằng phím
  2. Persistant lưu trữ
  3. Không muốn viết lại toàn bộ tập tin khi 1 giá trị thay đổi
  4. nên tồn tai nạn

Tôi nghĩ bạn muốn có một cơ sở dữ liệu.

Chỉnh sửa: Tôi nghĩ bạn đang tìm kiếm điều sai. Tìm kiếm cơ sở dữ liệu phù hợp với yêu cầu của bạn. Và thay đổi một số yêu cầu của bạn, bởi vì tôi nghĩ sẽ rất khó để gặp tất cả chúng.

+2

Tôi hiểu những gì bạn đang nói, nhưng phần cơ sở dữ liệu chỉ là một chi tiết thực hiện khác, cho một giải pháp giảm phụ thuộc vào sqlite bạn cần có mã xử lý x64 vs x32, bạn cần một kho lưu trữ và một loạt các thứ khác. , có một db nhúng có thể là một giải pháp ... –

+6

... ma quỷ là chi tiết, và không có chi tiết hoặc mã trong câu trả lời này. Tôi đã yêu cầu một cách rõ ràng về việc triển khai IDictionary –

1

Tôi nghĩ vấn đề của bạn có thể sẽ là điểm cuối cùng:

Khi đặt hoặc thanh toán bù trừ một giá trị không nên ghi lại toàn bộ tệp cơ bản, thay vào đó nó sẽ tìm kiếm vị trí trong tệp và cập nhật giá trị.

Đây chính là điều mà DB làm - về cơ bản bạn mô tả cấu trúc bảng dựa trên tệp đơn giản.

Chúng tôi có thể minh họa sự cố bằng cách xem chuỗi.

Chuỗi trong bộ nhớ là những thứ linh hoạt - bạn không cần biết chiều dài của chuỗi trong C# khi bạn khai báo loại của nó.

Trong chuỗi lưu trữ dữ liệu và mọi thứ khác là kích thước cố định. Từ điển đã lưu của bạn trên đĩa chỉ là một tập hợp các byte theo thứ tự.

Nếu bạn thay thế một giá trị ở giữa, nó phải có cùng kích thước hoặc bạn sẽ phải ghi đè mỗi byte theo sau.

Đây là lý do tại sao hầu hết các cơ sở dữ liệu hạn chế các trường văn bản và blob thành các kích thước cố định. Các tính năng mới như varchar(max)/varbinary(max) trong Sql 2005+ thực sự là sự đơn giản thông minh đối với hàng chỉ thực sự lưu trữ một con trỏ tới dữ liệu thực.

Bạn không thể sử dụng các kích thước cố định với ví dụ của bạn vì nó chung chung - bạn không biết bạn sẽ lưu trữ loại nào để bạn không thể đệm các giá trị ở kích thước tối đa.

Bạn có thể làm:

class PersistantDictionary<T,V> : Dictionary<T,V> 
    where V:struct 

... như kiểu giá trị không thay đổi trong kích thước lưu trữ, mặc dù bạn sẽ phải cẩn thận với thực hiện của bạn để tiết kiệm đúng lượng lưu trữ cho từng loại.

Tuy nhiên mô hình của bạn sẽ không hoạt động hiệu quả - nếu bạn xem cách SQL Server và Oracle đối phó với các thay đổi bảng, chúng không thay đổi các giá trị như thế này. Thay vào đó, họ gắn cờ bản ghi cũ dưới dạng ma và thêm bản ghi mới với giá trị mới.Các bản ghi cũ đã bị làm mờ được dọn dẹp sau này khi DB ít bận.

Tôi nghĩ rằng bạn đang cố gắng phát minh lại bánh xe:

  • Nếu bạn đang làm việc với một lượng lớn dữ liệu sau đó bạn thực sự cần phải kiểm tra bằng cách sử dụng một DB toàn diện. MySql hoặc SqlLite đều tốt, nhưng bạn sẽ không tìm thấy một thực thi tốt, đơn giản, nguồn mở và lite.

  • Nếu bạn không xử lý nhiều lần tải dữ liệu thì tôi sẽ gửi toàn bộ chuỗi tệp, và đã có rất nhiều đề xuất tốt ở đây về cách thực hiện điều đó.

+1

MSSQL (và tôi giả định RDBMS khác) sử dụng bảng bù hàng để theo dõi bắt đầu các hàng từ một trang. –

1

Tôi muốn giới thiệu SQL Server Express hoặc cơ sở dữ liệu khác.

  • Hoàn toàn miễn phí.
  • Nó tích hợp rất tốt với C#, bao gồm LINQ.
  • Nó nhanh hơn giải pháp tự chế.
  • Đáng tin cậy hơn giải pháp tự chế.
  • Đó là cách mạnh mẽ hơn cấu trúc dữ liệu dựa trên đĩa đơn giản, vì vậy sẽ dễ dàng thực hiện nhiều hơn trong tương lai.
  • SQL là tiêu chuẩn của ngành, vì vậy các nhà phát triển khác sẽ hiểu chương trình của bạn dễ dàng hơn và bạn sẽ có kỹ năng hữu ích trong tương lai.
+4

SQL Express không phải là một giọt trong giải pháp, một trình cài đặt phức tạp lộn xộn của nó, sql ce có thể là một giải pháp ... –

+0

Một mối quan tâm hợp lý. –

1

Tôi đã tự mình thực hiện dựa trên một yêu cầu rất giống (tôi nghĩ giống hệt) mà tôi đã có đối với một dự án khác trước đây. Khi tôi đã làm điều đó, một điều tôi nhận ra là hầu hết thời gian bạn sẽ viết, bạn chỉ đọc khi chương trình bị treo hoặc khi nó bị đóng. Vì vậy, ý tưởng là làm cho việc viết càng nhanh càng tốt. Những gì tôi đã làm là tạo ra một lớp rất đơn giản mà sẽ chỉ viết một bản ghi của tất cả các hoạt động (bổ sung và xóa) vào từ điển khi mọi thứ xảy ra. Vì vậy, sau một thời gian bạn nhận được rất nhiều lặp đi lặp lại giữa các phím. Do đó, khi đối tượng phát hiện một số lượng nhất định của sự lặp lại, nó sẽ xóa nhật ký và ghi lại nó để mỗi khóa và giá trị của nó chỉ xuất hiện một lần.

Thật không may, bạn không thể phân lớp từ điển vì bạn không thể ghi đè bất kỳ thứ gì trong đó. Đây là thực hiện đơn giản của tôi, tôi đã không thử nghiệm nó mặc dù tôi xin lỗi, tôi nghĩ bạn có thể muốn ý tưởng mặc dù. Hãy sử dụng nó và thay đổi nó nhiều như bạn muốn.

class PersistentDictManager { 
    const int SaveAllThreshold = 1000; 

    PersistentDictManager(string logpath) { 
     this.LogPath = logpath; 
     this.mydictionary = new Dictionary<string, string>(); 
     this.LoadData(); 
    } 

    public string LogPath { get; private set; } 

    public string this[string key] { 
     get{ return this.mydictionary[key]; } 
     set{ 
      string existingvalue; 
      if(!this.mydictionary.TryGetValue(key, out existingvalue)) { existingvalue = null; } 
      if(string.Equals(value, existingvalue)) { return; } 
      this[key] = value; 

      // store in log 
      if(existingvalue != null) { // was an update (not a create) 
       if(this.IncrementSaveAll()) { return; } // because we're going to repeat a key the log 
      } 
      this.LogStore(key, value); 
     } 
    } 

    public void Remove(string key) { 
     if(!this.mydictionary.Remove(key)) { return; } 
     if(this.IncrementSaveAll()) { return; } // because we're going to repeat a key in the log 
     this.LogDelete(key); 
    } 

    private void CreateWriter() { 
     if(this.writer == null) { 
      this.writer = new BinaryWriter(File.Open(this.LogPath, FileMode.Open)); 
     } 
    } 

    private bool IncrementSaveAll() { 
     ++this.saveallcount; 
     if(this.saveallcount >= PersistentDictManager.SaveAllThreshold) { 
      this.SaveAllData(); 
      return true; 
     } 
     else { return false; } 
    } 

    private void LoadData() { 
     try{ 
      using(BinaryReader reader = new BinaryReader(File.Open(LogPath, FileMode.Open))) { 
       while(reader.PeekChar() != -1) { 
        string key = reader.ReadString(); 
        bool isdeleted = reader.ReadBoolean(); 
        if(isdeleted) { this.mydictionary.Remove(key); } 
        else { 
         string value = reader.ReadString(); 
         this.mydictionary[key] = value; 
        } 
       } 
      } 
     } 
     catch(FileNotFoundException) { } 
    } 

    private void LogDelete(string key) { 
     this.CreateWriter(); 
     this.writer.Write(key); 
     this.writer.Write(true); // yes, key was deleted 
    } 

    private void LogStore(string key, string value) { 
     this.CreateWriter(); 
     this.writer.Write(key); 
     this.writer.Write(false); // no, key was not deleted 
     this.writer.Write(value); 
    } 

    private void SaveAllData() { 
     if(this.writer != null) { 
      this.writer.Close(); 
      this.writer = null; 
     } 
     using(BinaryWriter writer = new BinaryWriter(File.Open(this.LogPath, FileMode.Create))) { 
      foreach(KeyValuePair<string, string> kv in this.mydictionary) { 
       writer.Write(kv.Key); 
       writer.Write(false); // is not deleted flag 
       writer.Write(kv.Value); 
      } 
     } 
    } 

    private readonly Dictionary<string, string> mydictionary; 
    private int saveallcount = 0; 
    private BinaryWriter writer = null; 
} 
+0

Nếu bạn muốn cải thiện nó, bạn có thể làm cho nó phát hiện thời gian nhàn rỗi và chỉ tuôn ra các tập tin đầy đủ sau đó.Tôi biết bạn muốn có một hệ thống chỉ cập nhật các phần liên quan của tập tin, nhưng không chỉ có thể viết ra khá khó khăn, tôi chắc rằng điều này sẽ là quá đủ cho nhu cầu của bạn! –

2

Tôi đang làm việc để chuyển EHCache sang .NET. Hãy nhìn vào các dự án

http://sourceforge.net/projects/thecache/

dai dẳng bộ nhớ đệm là chức năng cốt lõi mà đã được thực hiện. Tất cả các bài kiểm tra đơn vị chính đều được thông qua. Tôi có một chút bị mắc kẹt trên bộ nhớ đệm được phân phối, nhưng bạn không cần phần đó.

+0

Về mặt lý thuyết, điều này sẽ hoạt động đúng cho vấn đề tôi có. Cảm ơn –

8

Tôi đã triển khai loại PersistedDictionary mà bạn đang tìm kiếm. Lưu trữ cơ bản là công cụ cơ sở dữ liệu ESENT được tích hợp trong các cửa sổ.Mã này có sẵn ở đây:

http://managedesent.codeplex.com/

+0

Tôi bây giờ đang cố gắng ra PersistentDictionary, nhưng tôi tiếp tục nhận được ngoại lệ sau khi tôi nhanh chóng từ điển: Lỗi TempPathInUse (JET_errTempPathInUse, Temp đường dẫn đã được sử dụng bởi một cá thể cơ sở dữ liệu khác). Tài liệu không nói rằng tôi cần sử dụng tài liệu đó làm singelton, cũng như không nói rằng tôi phải vứt bỏ nó đúng cách. Vì vậy, điều gì đó rõ ràng là sai. –

+0

Xem thảo luận trên trang web codeplex ManagedEsent. –

+1

Để tham khảo, cuộc thảo luận có vẻ là chủ đề này https://managedesent.codeplex.com/discussions/217114 –

Các vấn đề liên quan