对于一个开源项目,我正在寻找一个由文件支持的字典的简单实现。意思是,如果应用程序崩溃或重新启动字典将保持其状态。我想每次触摸字典时更新底层文件。 (添加值或删除值)。 FileWatcher不是必需的,但它可能很有用。
class PersistentDictionary<T,V> : IDictionary<T,V>
{
public PersistentDictionary(string filename)
{
}
}
要求:
类似问题
答案 0 :(得分:23)
bplusdotnet包是C#,java和Python中交叉兼容的数据结构实现库,对于需要存储和检索持久性信息的应用程序非常有用。 bplusdotnet数据结构可以轻松存储与值永久关联的字符串键。
不是100%托管代码,但值得一提的是,非托管库本身已经是每个Windows XP / 2003 / Vista / 7框的一部分
ESENT是一个可嵌入的数据库存储引擎(ISAM),它是Windows的一部分。它提供可靠的,事务性的,并发的,高性能的数据存储,具有行级锁定,预写日志记录和快照隔离。这是ESENT Win32 API的托管包装。
* Akavache是一个异步,持久的键值缓存,用于在C#中编写本机桌面和移动应用程序。可以把它想象成桌面应用程序的memcached。
<击> - The C5 Generic Collection Library
C5提供标准.Net System.Collections.Generic
命名空间未提供的功能和数据结构,例如持久树数据结构,基于堆的优先级队列,哈希索引数组列表和链接列表,以及有关集合更改的事件。
击>
答案 1 :(得分:13)
让我分析一下:
我想你想要一个数据库。
编辑:我认为你正在寻找错误的东西。搜索符合您要求的数据库。并改变你的一些要求,因为我认为很难满足所有要求。
答案 2 :(得分:8)
我已经实现了您正在寻找的那种PersistedDictionary。底层存储是内置于Windows中的ESENT数据库引擎。代码可在此处获取:
答案 3 :(得分:6)
一种方法是使用内置在窗口中的Extensible Storage Engine来存储你的东西。它是一个原生的win数据库,支持索引,事务等......
答案 4 :(得分:2)
我正在努力将EHCache移植到.NET。看看项目
http://sourceforge.net/projects/thecache/
持久性缓存是已经实现的核心功能。所有主要单元测试都在通过。我对分布式缓存有点困惑,但你不需要那部分。
答案 5 :(得分:1)
听起来很酷,但您如何绕过存储值(如果它是引用类型)本身的变化?如果它是不可变的那么一切都很好,但如果不是你有点填充: - )
如果你不处理不可变值,我会怀疑更好的方法是处理值级别的持久性,并根据需要重建字典。
(编辑以添加澄清)
答案 6 :(得分:1)
查看此博客:
http://ayende.com/Blog/archive/2009/01/17/rhino.dht-ndash-persistent-amp-distributed-storage.aspx
看起来正是您正在寻找的。
答案 7 :(得分:1)
我自己写了一个实现,基于我前一段时间对另一个项目非常相似(我认为相同)的要求。当我这样做时,我意识到的一件事是,你大部分时间都在做写作,你只会在程序崩溃或关闭时很少进行读取。因此,我们的想法是尽可能快地进行写入。我所做的是创建一个非常简单的类,它只会在发生事情时将所有操作(添加和删除)的日志写入字典。所以过了一会儿你会在键之间重复一遍。因此,一旦对象检测到一定量的重复,它将清除日志并重写它,以便每个键及其值仅出现一次。
不幸的是,您不能将Dictionary子类化,因为您无法覆盖其中的任何内容。这是我的简单实现,虽然我很抱歉,但我还没有测试过,我想你可能想要这个想法。随意使用它并根据需要进行更改。
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;
}
答案 8 :(得分:1)
我发现这个链接听起来像你在找什么。它是用Java编程的,但它不应该很难将它移植到C#:
http://www.javaworld.com/javaworld/jw-01-1999/jw-01-step.html
答案 9 :(得分:1)
我认为你的问题很可能是最后一点:
设置或清除值时,不应重写整个基础文件,而应搜索文件中的位置并更新值。
这正是数据库的作用 - 您基本上是在描述一个基于文件的简单表结构。
我们可以通过查看字符串来说明问题。
内存中的字符串是灵活的东西 - 当你声明它的类型时,你不需要知道C#中字符串的长度。
在数据存储字符串中,其他所有内容都是固定大小。您在磁盘上保存的字典只是按顺序的字节集合。
如果你在中间替换一个值,它必须是完全相同的大小,或者你必须重写它后面的每个字节。
这就是为什么大多数数据库将text和blob字段限制为固定大小的原因。 Sql 2005+中的varchar(max)
/ varbinary(max)
等新功能实际上是对行的巧妙简化,只实际存储了指向实际数据的指针。
您不能在示例中使用固定大小,因为它是通用的 - 您不知道将要存储的类型,因此您无法将值填充到最大大小。
你可以这样做:
class PersistantDictionary<T,V> : Dictionary<T,V>
where V:struct
...因为值类型的存储大小不同,但您必须小心实施,以便为每种类型保存适当的存储空间。
然而,您的模型不会非常高效 - 如果您了解SQL服务器和Oracle如何处理表更改,他们不会更改这样的值。相反,他们将旧记录标记为鬼,并使用新值添加新记录。当数据库不太忙时,旧的幻影记录会被清除。
我认为你正试图重新发明轮子:
如果您正在处理大量数据,那么您真的需要使用完整的数据库进行检查。 MySql或SqlLite都很好,但你不会找到一个好的,简单的,开源和精简的实现。
如果您没有处理大量数据,那么我会选择整个文件序列化,并且已经有很多关于如何做到这一点的好建议。
答案 10 :(得分:0)
只需使用序列化。查看BinaryFormatter类。
答案 11 :(得分:0)
我推荐SQL Server Express或其他数据库。
答案 12 :(得分:0)
我实际上并没有使用它,但这个项目显然提供了一个mmap() - 就像在C#中实现一样
答案 13 :(得分:0)
考虑内存映射文件。我不确定.NET中是否有直接支持,但您可以调用Win32调用。
答案 14 :(得分:0)
像道格拉斯所说,你需要知道你的类型的固定大小(T和V)。此外,任何这些实例引用的对象网格中的可变长度实例都已用完。
尽管如此,实现一个由文件支持的字典非常简单,在继承或封装BinaryWriter
类之后,您可以使用Dictionary<TKey, TValue>
类将类型写入磁盘。
答案 15 :(得分:0)
我不知道有什么可以解决你的问题。它需要是一个固定大小的结构,这样你才能满足能够重写记录而不需要重写整个文件的要求。
这意味着普通字符串已经用完了。
答案 16 :(得分:-1)
我不是一个程序员,但是不会创建一个非常简单的XML格式来存储你的数据吗?
<dico>
<dicEntry index="x">
<key>MyKey</key>
<val type="string">My val</val>
</dicEntry>
...
</dico>
从那里,您可以加载XML文件DOM并根据需要填写字典,
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
}
然后,您可以随时更新DOM并将其保存在磁盘上。
xdocDico.save(sXMLfile);
如果你有能力将DOM保持在内存性能方面,那么它很容易处理。根据您的要求,您可能根本不需要字典。