데이터를 로컬에 저장하는 방법.NET (C#)
나중에 사용할 수 있도록 사용자 데이터를 가져와서 로컬에 저장하는 어플리케이션을 작성합니다.응용 프로그램은 꽤 자주 시작되고 중지될 것이며, 응용 프로그램 시작/종료 시 데이터를 저장/로드할 수 있도록 하고 싶습니다.
데이터를 보호할 필요가 없기 때문에 플랫 파일을 사용하면 상당히 간단할 것입니다(이 PC에만 저장됩니다).제가 생각하는 옵션은 다음과 같습니다.
- 플랫파일
- XML
- SQL DB
플랫 파일은 유지 관리에 조금 더 많은 노력이 필요하지만(XML과 같이 클래스에 내장되어 있지 않음), XML을 사용해 본 적은 없으며 SQL은 이 비교적 쉬운 작업을 위해 오버킬한 것 같습니다.
탐험할 만한 다른 방법이 있습니까?그렇지 않다면 다음 중 어떤 것이 최선의 해결책입니까?
편집: 문제에 데이터를 조금 더 추가하기 위해 기본적으로 저장하고 싶은 것은 다음과 같은 사전뿐입니다.
Dictionary<string, List<Account>>
여기서 Account는 다른 사용자 지정 유형입니다.
dict를 xmlroot으로 serialize한 다음 Account type을 속성으로 serialize합니까?
업데이트 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>
보다시피 상속권은
- 사용자 이름 (dict 문자열) >
- 계정 (리스트의 각 계정) >
- 계정 데이터(즉, 클래스 속성).
이 레이아웃 가져오기Dictionary<Username, List<Account>>
까다로운 부분과 이 질문의 핵심입니다.
여기 직렬화에 대한 '방법' 응답이 많은데, 이는 초기에 명확하게 하지 않았기 때문에 제 잘못이지만, 지금은 확실한 해결책을 찾고 있습니다.
파일은 JSON으로 저장하겠습니다.이름/값 쌍 목록에 불과한 사전을 저장하고 있으므로 json의 목적은 이와 같습니다.
꽤 괜찮은, 무료인 것들이 있습니다.NET json 라이브러리 - 여기 하나 있지만 첫 번째 링크에서 전체 목록을 찾을 수 있습니다.
그건 정말로 당신이 무엇을 저장하느냐에 달려있습니다.구조화된 데이터를 말하는 경우 XML 또는 SQLite 또는 SQL Server Compact Edition과 같은 매우 가벼운 SQL RDBMS가 적합합니다.데이터가 사소한 크기 이상으로 이동할 경우 SQL 솔루션이 특히 중요해집니다.
비교적 구조화되지 않은 많은 데이터(예: 이미지와 같은 이진 개체)를 저장하는 경우 데이터베이스나 XML 솔루션 모두 적절치 않지만, 질문하신 내용을 고려해보면 후자보다는 전자에 가깝다고 생각합니다.
위의 내용은 모두 좋은 답변이며, 일반적으로 문제를 해결합니다.
수백만 개의 데이터로 쉽고 무료로 확장할 수 있는 방법이 필요하다면 GitHub 또는 NuGet에서 ESENT Managed Interface 프로젝트를 시도해 보십시오.
ESENT는 Windows(윈도우)의 일부인 내장형 데이터베이스 스토리지 엔진(ISAM)입니다.행 레벨 잠금, 쓰기 선행 로깅 및 스냅샷 격리 기능을 갖춘 안정적이고 트랜잭션 처리된 동시 고성능 데이터 스토리지를 제공합니다.이는 ESENT Win32 API에 대한 관리 래퍼입니다.
Persistent Dictionary 객체가 있어 사용하기가 매우 쉽습니다.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();
}
조지의 질문에 대답하기 위해서:
지원되는 키 유형
다음 유형만 사전 키로 지원됩니다.
부울 바이트 Int16 UInt16 Int32 UInt32 Int64 UInt64 플로트 더블 가이드 날짜 TimeSpan 문자열
지원되는 가치 유형
사전 값은 키 유형, Nullable 버전의 키 유형, URI, IPAddress 또는 serializable 구조 중 하나일 수 있습니다.구조물은 다음 기준을 모두 충족하는 경우에만 직렬화 가능한 것으로 간주됩니다.
• 구조물은 직렬화 가능한 것으로 표시됩니다. • 구조물의 모든 구성원은 다음 중 하나입니다. 1. 기본 데이터 유형(예: Int32) 2.문자열, URI 또는 IP 주소 3.직렬화 가능한 구조.
즉, 직렬화 가능한 구조는 클래스 개체에 대한 참조를 포함할 수 없습니다.API의 일관성을 유지하기 위해서입니다.Persistent Dictionary에 개체를 추가하면 직렬화를 통해 개체의 복사본이 생성됩니다.원래 개체를 수정해도 복사본이 수정되지 않으므로 혼란스러운 동작이 발생할 수 있습니다.이러한 문제를 방지하기 위해 Persistent Dictionary에서는 값 유형만 값으로 허용합니다.
직렬화 가능 [직렬화 가능] 구조 {public DateTime}?수신; 공개 문자열 이름; 공개 10진수 가격; 공개 Url; }
직렬화될 수 없음 [직렬화 가능] 구조 잘못된 {public byte[] 데이터, // 배열은 지원되지 않는 public Exception Error, // 참조 개체 }
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();
}
}
파일용 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;
}
}
}
}
그런 다음 ConfigForm에서 ConfigManager 클래스를 호출하여 Serialize하면 됩니다.
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);
}
}
직렬화된 후 cm를 사용하여 구성 파일의 매개 변수를 호출할 수 있습니다.창 제목 등
컬렉션이 너무 커지면 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;
}
}
네 번째 옵션은 이진 파일입니다.난해하고 어려울 것 같지만 의 직렬화 API를 사용하면 정말 쉽습니다.그물.
이진 파일을 선택하든 XML 파일을 선택하든 동일한 serialization API를 사용할 수 있지만 서로 다른 serializer를 사용할 수도 있습니다.
클래스를 이진 직렬화하려면 [Serializable] 특성으로 표시하거나 ISerializable을 구현해야 합니다.
인터페이스가 [XmlSerializable]이고 속성이 [XmlRoot] 및 시스템의 다른 속성이지만 XML로 비슷한 작업을 수행할 수 있습니다.Xml.직렬화 네임스페이스입니다.
관계형 데이터베이스를 사용하려는 경우 SQL Server Compact Edition은 무료이며 매우 가볍고 단일 파일을 기반으로 합니다.
제가 현재 진행 중인 프로젝트의 데이터 저장소 코딩을(를)여기 제 5센트입니다.
저는 바이너리 시리얼라이제이션으로 시작했습니다.속도가 느렸고(100,000개의 객체를 로드하는 데 약 30초), 디스크에도 꽤 큰 파일을 만들었습니다.하지만 구현하는 데 몇 줄의 코드가 필요했고 스토리지 요구사항을 모두 해결했습니다.더 나은 성능을 얻기 위해 사용자 정의 시리얼라이제이션으로 이동했습니다.코드 프로젝트 팀 헤인즈의 Fast Serialization 프레임워크를 찾았습니다.실제로 이는 몇 배 더 빠르며(로드 시 12초, 저장 시 8초, 기록 100K) 디스크 공간도 적게 소요됩니다.이 프레임워크는 이전 게시물에서 갤럭틱 젤로가 개략적으로 설명한 기술을 기반으로 구축되었습니다.
그 후 SQLite로 전환하여 로드 시 6초, 저장 시 4초, 100K 레코드의 경우 3배 빠른 성능을 2배 향상할 수 있었습니다.ADO 파싱이 포함되어 있습니다.응용프로그램 유형에 대한 NET 테이블.또한 디스크의 파일 크기도 훨씬 작았습니다.이 기사에서는 ADO에서 최상의 성능을 발휘하는 방법을 설명합니다.NET: http://sqlite.phxsoftware.com/forums/t/134.aspx .INSERT 문을 생성하는 것은 매우 나쁜 생각입니다.제가 그걸 어떻게 알게 되었는지 짐작하실 수 있을 겁니다.:) 실제로 SQLite 구현에는 상당한 시간이 걸렸고 코드의 거의 모든 라인에서 시간이 걸리는 것을 세심하게 측정했습니다.
제가 가장 먼저 보는 것은 데이터베이스입니다.그러나 직렬화는 선택 사항입니다.만약 당신이 바이너리 시리얼라이제이션을 한다면, 저는 피해갈 것입니다. BinaryFormatter
- 필드 등을 변경하면 버전 간에 화를 내는 경향이 있습니다.Xml 경유XmlSerialzier
계약 기반 바이너리 직렬화(아무 노력 없이 플랫 파일 직렬화기를 제공)를 시도하려면 프로토오브넷과 나란히 호환(즉, 클래스 정의가 동일함)될 수 있습니다.
데이터가 복잡하거나 양이 많거나 로컬에서 쿼리해야 하는 경우 개체 데이터베이스가 유효한 옵션일 수 있습니다.저는 Db4o나 카르보나이트를 보는 것을 추천합니다.
이 스레드의 많은 답변은 솔루션을 과도하게 설계하려고 시도합니다.제 말이 맞다면 사용자 설정을 저장하고 싶을 뿐입니다.
.ini 파일 또는 App을 사용합니다.이에 대한 구성 파일.
만약 내가 틀렸고 당신이 설정 이상의 데이터를 저장하고 있다면 csv 형식의 평문 파일을 사용하세요.이것들은 XML의 오버헤드 없이 빠르고 쉽습니다. 사람들은 이것들이 우아하지 않고, 잘 확장되지 않으며, 이력서에 잘 맞지 않기 때문에 똥을 싸는 것을 좋아하지만, 필요한 것에 따라 이것이 당신에게 가장 좋은 해결책이 될 수도 있습니다.
데이터의 복잡성, 크기 등을 파악하지 못한 상태에서...XML은 유지보수가 쉽고 쉽게 접근할 수 있습니다.액세스 데이터베이스를 사용하지 않을 것이며 플랫 파일은 특히 파일에서 둘 이상의 데이터 필드/요소를 처리하는 경우 장기적으로 유지하기가 더 어렵습니다.
저는 대용량 플랫 파일 데이터 피드를 매일 처리하는데, 극단적인 예를 들자면 플랫 파일 데이터는 제가 처리하는 XML 데이터 피드보다 유지하기가 훨씬 어렵습니다.
C#을 사용하여 XML 데이터를 데이터셋에 로드하는 간단한 예:
DataSet reportData = new DataSet();
reportData.ReadXml(fi.FullName);
XML 데이터를 쿼리하기 위한 옵션으로 LINQ to XML을 확인할 수도 있습니다.
HTH...
저는 로컬 데이터 저장소가 있는 "독립형" 앱을 여러 번 해봤습니다.가장 좋은 것은 SQL Server Compact Edition(이전 이름은 SQLAnywhere)일 것입니다.
가볍고 자유롭습니다.또한 다른 프로젝트에서 재사용 가능한 데이터 액세스 계층을 작성할 수 있으며, 애플리케이션이 전체 SQL 서버와 같이 더 큰 규모로 확장해야 하는 경우 연결 문자열만 변경하면 됩니다.
계정 개체의 복잡성에 따라 XML 또는 플랫 파일을 추천합니다.
각 계정에 저장할 값이 몇 개뿐인 경우 다음과 같이 속성 파일에 저장할 수 있습니다.
account.1.somekey=Some value
account.1.someotherkey=Some other value
account.1.somedate=2009-12-21
account.2.somekey=Some value 2
account.2.someotherkey=Some other value 2
... 등등속성 파일은 문자열 사전에 직접 매핑되므로 읽기가 쉬워야 합니다.
이 파일을 저장할 위치는 프로그램의 하위 폴더 안에 있는 AppData 폴더에 저장하는 것이 가장 좋습니다.이곳은 현재 사용자들이 항상 글을 쓸 수 있는 곳으로, OS 자체가 다른 사용자들로부터 안전하게 지켜줍니다.
저의 첫번째 성향은 접속 데이터베이스입니다..mdb 파일은 로컬에 저장되며 필요한 경우 암호화할 수 있습니다.XML이나 JSON도 많은 시나리오에 적합합니다.플랫 파일은 읽기 전용, 비검색(순방향 읽기 전용) 정보에만 사용합니다.저는 너비 설정보다는 csv 형식을 선호하는 편입니다.
저장하려는 데이터의 양에 따라 달라집니다.사실 플랫 파일과 XML 사이에는 차이가 없습니다. XML은 문서에 구조를 제공하기 때문에 아마도 더 바람직할 것입니다.실제로는.
마지막 옵션이자 현재 많은 애플리케이션이 사용하는 것은 Windows 레지스트리입니다.개인적으로 추천하지는 않지만(Registry Bloat, Corruption, 기타 잠재적인 문제), 선택 사항입니다.
이진 직렬화 경로로 이동할 경우 특정 구성원이 액세스해야 하는 속도를 고려합니다.작은 컬렉션일 경우 전체 파일을 로드하는 것이 타당하지만, 크기가 클 경우 인덱스 파일도 고려할 수 있습니다.
파일 내의 특정 주소에 있는 계정 속성/필드를 추적하면 특히 키 사용량에 따라 인덱스 파일을 최적화할 경우 액세스 시간을 단축할 수 있습니다.(디스크에 쓰는 경우에도 possibly 가능)
간단히 하세요. 말씀하신 것처럼 플랫 파일로도 충분합니다.플랫 파일을 사용합니다.
이것은 당신이 당신의 요구사항을 정확하게 분석했다고 가정하는 것입니다.XML로 직렬화하는 것은 생략하고, 간단한 사전에 대해서는 오버킬합니다.데이터베이스도 마찬가지입니다.
대부분의 경우 파일에 JSON만 있으면 충분합니다(대부분 배열이나 개체 또는 단일 숫자나 문자열만 저장해야 함).SQLite가 거의 필요하지 않습니다. SQLite를 설정하고 사용하는 데 시간이 더 필요합니다. 대부분의 경우 과도하게 사용됩니다.
언급URL : https://stackoverflow.com/questions/1941928/how-to-store-data-locally-in-net-c
'programing' 카테고리의 다른 글
jQuery: 옵션의 텍스트/값이 지정된 드롭다운 목록에서 옵션을 제거합니다. (0) | 2023.10.17 |
---|---|
maria에서 인덱스 키의 최대 문자 제한 증가DB (0) | 2023.10.17 |
jQuery에서 아이디 추가? (0) | 2023.10.17 |
Android에서 스타일리시한 속성 선언 (0) | 2023.10.17 |
MariaDb slow_query_log 환경 변수(도커 포함) (0) | 2023.10.17 |