Geo*_*rge 68 .net c# xml data-storage
我正在编写一个应用程序,它接收用户数据并将其存储在本地以供日后使用.应用程序将经常启动和停止,我想在应用程序启动/结束时保存/加载数据.
如果我使用平面文件,那将是相当简单的,因为数据并不真正需要保护(它只会存储在这台PC上).我相信的选择因此是:
平面文件需要更多的维护工作(没有类似于XML的内置类),但是之前我没有使用过XML,而且对于这个相对简单的任务来说,SQL似乎有些过分.
还有其他值得探索的途径吗?如果没有,哪一个是最好的解决方案?
编辑:要为问题添加更多数据,基本上我唯一想存储的是一个看起来像这样的字典
Dictionary<string, List<Account>>
Run Code Online (Sandbox Code Playgroud)
其中Account是另一种自定义类型.
我会将dict序列化为xmlroot,然后将Account类型作为属性吗?
更新2:
所以可以序列化字典.使它变得复杂的是这个dict的值是泛型本身,它是Account类型的复杂数据结构的列表.每个帐户都相当简单,它只是一堆属性.
我的理解是,这里的目标是尝试最终得到:
<Username1>
<Account1>
<Data1>data1</Data1>
<Data2>data2</Data2>
</Account1>
</Username1>
<Username2>
<Account1>
<Data1>data1</Data1>
<Data2>data2</Data2>
</Account1>
<Account2>
<Data1>data1</Data1>
<Data2>data2</Data2>
</Account2>
</Username2>
Run Code Online (Sandbox Code Playgroud)
你可以看到heirachy是
从a获取这个布局Dictionary<Username, List<Account>>是棘手的一点,也是这个问题的本质.
这里有很多关于序列化的"如何"回应,这是我的错,因为我没有在早期更清楚,但现在我正在寻找一个明确的解决方案.
Ada*_*son 23
这真的取决于你的存储.如果您正在谈论结构化数据,那么XML或非常轻量级的SQL RDBMS(如SQLite或SQL Server Compact Edition)都可以很好地为您服务.如果数据超出一个微不足道的大小,SQL解决方案将变得特别引人注目.
如果您要存储大量相对非结构化的数据(例如图像等二进制对象),那么显然数据库和XML解决方案都不合适,但考虑到您的问题,我猜测它更多的是前者而不是后者.
Che*_*eso 14
XML易于使用,通过序列化.使用隔离存储.
另请参阅如何确定每个用户状态的存储位置?注册?应用程序数据?隔离存储?
public class UserDB
{
// actual data to be preserved for each user
public int A;
public string Z;
// metadata
public DateTime LastSaved;
public int eon;
private string dbpath;
public static UserDB Load(string path)
{
UserDB udb;
try
{
System.Xml.Serialization.XmlSerializer s=new System.Xml.Serialization.XmlSerializer(typeof(UserDB));
using(System.IO.StreamReader reader= System.IO.File.OpenText(path))
{
udb= (UserDB) s.Deserialize(reader);
}
}
catch
{
udb= new UserDB();
}
udb.dbpath= path;
return udb;
}
public void Save()
{
LastSaved= System.DateTime.Now;
eon++;
var s= new System.Xml.Serialization.XmlSerializer(typeof(UserDB));
var ns= new System.Xml.Serialization.XmlSerializerNamespaces();
ns.Add( "", "");
System.IO.StreamWriter writer= System.IO.File.CreateText(dbpath);
s.Serialize(writer, this, ns);
writer.Close();
}
}
Run Code Online (Sandbox Code Playgroud)
Gal*_*llo 11
以上所有都是很好的答案,并且通常可以解决问题.
如果您需要一种简单,免费的方式来扩展到数百万条数据,请尝试使用CodePlex上的ESENT Managed Interface项目.
ESENT是一个可嵌入的数据库存储引擎(ISAM),它是Windows的一部分.它提供可靠的,事务性的,并发的,高性能的数据存储,具有行级锁定,预写日志记录和快照隔离.这是ESENT Win32 API的托管包装器.
它有一个很容易使用的PersistentDictionary对象.可以将它想象成一个Dictionary()对象,但它会自动加载并保存到磁盘而无需额外的代码.
例如:
/// <summary>
/// Ask the user for their first name and see if we remember
/// their last name.
/// </summary>
public static void Main()
{
PersistentDictionary<string, string> dictionary = new PersistentDictionary<string, string>("Names");
Console.WriteLine("What is your first name?");
string firstName = Console.ReadLine();
if (dictionary.ContainsKey(firstName))
{
Console.WriteLine("Welcome back {0} {1}", firstName, dictionary[firstName]);
}
else
{
Console.WriteLine("I don't know you, {0}. What is your last name?", firstName);
dictionary[firstName] = Console.ReadLine();
}
Run Code Online (Sandbox Code Playgroud)
回答乔治的问题:
支持的密钥类型
只有这些类型作为字典键支持:
Boolean Byte Int16 UInt16 Int32 UInt32 Int64 UInt64 Float Double Guid DateTime TimeSpan String
支持的值类型
字典值可以是任何密钥类型,密钥类型的可空版本,Uri,IPAddress或可序列化结构.如果结构符合所有这些标准,则该结构仅被视为可序列化:
•结构被标记为可序列化•结构的每个成员都是:1.基本数据类型(例如Int32)2.字符串,Uri或IPAddress 3.可序列化结构.
或者,换句话说,可序列化结构不能包含对类对象的任何引用.这样做是为了保持API一致性.将对象添加到PersistentDictionary会通过序列化创建对象的副本.修改原始对象不会修改副本,这会导致混淆行为.为了避免这些问题,PersistentDictionary只接受值类型作为值.
可序列化 [Serializable] struct Good {public DateTime?接受; 公共字符串名称; 公共十进制价格; 公共Uri Url; }
无法序列化 [Serializable] struct Bad {public byte [] Data; //不支持数组public Exception Error; //参考对象}
小智 8
我推荐文件的XML读/写类,因为它很容易序列化.
序列化(在python中称为pickling)是一种将对象转换为二进制表示的简单方法,然后可以将其写入磁盘或通过线路发送.
它非常有用,例如可以轻松地将设置保存到文件中.
如果使用
[Serializable]属性标记它们,则可以序列化自己的类.这序列化了一个类的所有成员,标记为的除外[NonSerialized].
以下是向您展示如何执行此操作的代码:
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
namespace ConfigTest
{ [ Serializable() ]
public class ConfigManager
{
private string windowTitle = "Corp";
private string printTitle = "Inventory";
public string WindowTitle
{
get
{
return windowTitle;
}
set
{
windowTitle = value;
}
}
public string PrintTitle
{
get
{
return printTitle;
}
set
{
printTitle = value;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后,您可能在ConfigForm中调用ConfigManager类并对其进行序列化!
public ConfigForm()
{
InitializeComponent();
cm = new ConfigManager();
ser = new XmlSerializer(typeof(ConfigManager));
LoadConfig();
}
private void LoadConfig()
{
try
{
if (File.Exists(filepath))
{
FileStream fs = new FileStream(filepath, FileMode.Open);
cm = (ConfigManager)ser.Deserialize(fs);
fs.Close();
}
else
{
MessageBox.Show("Could not find User Configuration File\n\nCreating new file...", "User Config Not Found");
FileStream fs = new FileStream(filepath, FileMode.CreateNew);
TextWriter tw = new StreamWriter(fs);
ser.Serialize(tw, cm);
tw.Close();
fs.Close();
}
setupControlsFromConfig();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Run Code Online (Sandbox Code Playgroud)
在序列化之后,您可以使用cm.WindowTitle等调用配置文件的参数.
你提到的第四个选项是二进制文件.虽然这听起来很晦涩难懂,但使用.NET中的序列化API却非常容易.
无论您选择二进制文件还是XML文件,都可以使用相同的序列化API,尽管您可以使用不同的序列化程序.
要对类进行二进制序列化,必须使用[Serializable]属性标记或实现ISerializable.
你可以用XML做类似的事情,虽然接口叫做IXmlSerializable,属性是[XmlRoot]和System.Xml.Serialization命名空间中的其他属性.
如果要使用关系数据库,SQL Server Compact Edition是免费的,非常轻量级且基于单个文件.
如果你的集合太大,我发现Xml序列化变得非常慢.序列化字典的另一个选项是使用BinaryReader和BinaryWriter"自己动手".
这里有一些示例代码,只是为了帮助您入门.你可以使这些通用的扩展方法来处理任何类型的字典,并且它工作得很好,但是在这里发布过于冗长.
class Account
{
public string AccountName { get; set; }
public int AccountNumber { get; set; }
internal void Serialize(BinaryWriter bw)
{
// Add logic to serialize everything you need here
// Keep in synch with Deserialize
bw.Write(AccountName);
bw.Write(AccountNumber);
}
internal void Deserialize(BinaryReader br)
{
// Add logic to deserialize everythin you need here,
// Keep in synch with Serialize
AccountName = br.ReadString();
AccountNumber = br.ReadInt32();
}
}
class Program
{
static void Serialize(string OutputFile)
{
// Write to disk
using (Stream stream = File.Open(OutputFile, FileMode.Create))
{
BinaryWriter bw = new BinaryWriter(stream);
// Save number of entries
bw.Write(accounts.Count);
foreach (KeyValuePair<string, List<Account>> accountKvp in accounts)
{
// Save each key/value pair
bw.Write(accountKvp.Key);
bw.Write(accountKvp.Value.Count);
foreach (Account account in accountKvp.Value)
{
account.Serialize(bw);
}
}
}
}
static void Deserialize(string InputFile)
{
accounts.Clear();
// Read from disk
using (Stream stream = File.Open(InputFile, FileMode.Open))
{
BinaryReader br = new BinaryReader(stream);
int entryCount = br.ReadInt32();
for (int entries = 0; entries < entryCount; entries++)
{
// Read in the key-value pairs
string key = br.ReadString();
int accountCount = br.ReadInt32();
List<Account> accountList = new List<Account>();
for (int i = 0; i < accountCount; i++)
{
Account account = new Account();
account.Deserialize(br);
accountList.Add(account);
}
accounts.Add(key, accountList);
}
}
}
static Dictionary<string, List<Account>> accounts = new Dictionary<string, List<Account>>();
static void Main(string[] args)
{
string accountName = "Bob";
List<Account> newAccounts = new List<Account>();
newAccounts.Add(AddAccount("A", 1));
newAccounts.Add(AddAccount("B", 2));
newAccounts.Add(AddAccount("C", 3));
accounts.Add(accountName, newAccounts);
accountName = "Tom";
newAccounts = new List<Account>();
newAccounts.Add(AddAccount("A1", 11));
newAccounts.Add(AddAccount("B1", 22));
newAccounts.Add(AddAccount("C1", 33));
accounts.Add(accountName, newAccounts);
string saveFile = @"C:\accounts.bin";
Serialize(saveFile);
// clear it out to prove it works
accounts.Clear();
Deserialize(saveFile);
}
static Account AddAccount(string AccountName, int AccountNumber)
{
Account account = new Account();
account.AccountName = AccountName;
account.AccountNumber = AccountNumber;
return account;
}
}
Run Code Online (Sandbox Code Playgroud)
小智 6
刚为我当前的项目完成了数据存储编码.这是我5美分.
我从二进制序列化开始.它很慢(加载100,000个对象大约30秒),它也在磁盘上创建了一个非常大的文件.但是,我需要几行代码来实现,并且我满足了所有存储需求.为了获得更好的性能,我开始进行自定义序列化.Tim Haynes在Code Project上找到了FastSerialization框架.实际上它的速度要快几倍(负载为12秒,保存为8秒,100K记录),占用的磁盘空间更少.该框架基于GalacticJello在之前的文章中概述的技术.
然后我转移到SQLite并且能够获得2个有时快3倍的性能 - 加载6秒,保存4秒,100K记录.它包括将ADO.NET表解析为应用程序类型.它还在磁盘上给了我更小的文件.本文介绍如何从ADO.NET中获得最佳性能:http://sqlite.phxsoftware.com/forums/t/134.aspx.生成INSERT语句是一个非常糟糕的主意.你可以猜到我是怎么知道的.:)确实,SQLite的实现花了我很多时间,加上仔细测量几乎所有代码行的时间.
我首先要看的是一个数据库。但是,序列化是一种选择。如果您进行二进制序列化,那么我会避免 BinaryFormatter- 如果您更改字段等,它有可能在版本之间生气。 Xml viaXmlSerialzier会很好,并且可以并排兼容(即具有相同的类定义)如果您想尝试基于合同的二进制序列化(无需任何努力即可为您提供平面文件序列化程序),请使用 protobuf-net。
| 归档时间: |
|
| 查看次数: |
144424 次 |
| 最近记录: |