Xamarin.Forms 로컬 데이터베이스
SQLite 데이터베이스 엔진을 사용하면 애플리케이션에서 Xamarin.Forms 데이터 개체를 로드하고 공유 코드에 저장할 수 있습니다. 샘플 애플리케이션은 SQLite 데이터베이스 테이블을 사용하여 할 일 항목을 저장합니다. 이 문서에서는 공유 코드의 SQLite.Net 사용하여 로컬 데이터베이스에 정보를 저장하고 검색하는 방법을 설명합니다.
다음 단계에 따라 모바일 앱에 SQLite.NET 통합합니다.
SQLite NuGet 패키지 설치
NuGet 패키지 관리자를 사용하여 sqlite-net-pcl을 검색하고 공유 코드 프로젝트에 최신 버전을 추가합니다.
이름이 유사한 NuGet 패키지가 여러 개 있습니다. 올바른 패키지에는 이러한 특성이 있습니다.
- ID: sqlite-net-pcl
- 작성자: SQLite-net
- 소유자: praeclarum
- NuGet 링크: sqlite-net-pcl
패키지 이름에도 불구하고 .NET 표준 프로젝트에서도 sqlite-net-pcl NuGet 패키지를 사용합니다.
Important
SQLite.NET praeclarum/sqlite-net 리포지토리에서 지원되는 타사 라이브러리입니다.
앱 상수 구성
샘플 프로젝트에는 일반적인 구성 데이터를 제공하는 Constants.cs 파일이 포함되어 있습니다.
public static class Constants
{
public const string DatabaseFilename = "TodoSQLite.db3";
public const SQLite.SQLiteOpenFlags Flags =
// open the database in read/write mode
SQLite.SQLiteOpenFlags.ReadWrite |
// create the database if it doesn't exist
SQLite.SQLiteOpenFlags.Create |
// enable multi-threaded database access
SQLite.SQLiteOpenFlags.SharedCache;
public static string DatabasePath
{
get
{
var basePath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
return Path.Combine(basePath, DatabaseFilename);
}
}
}
상수 파일은 데이터베이스 연결을 초기화하는 데 사용되는 기본 SQLiteOpenFlag
열거형 값을 지정합니다. 열거형은 SQLiteOpenFlag
다음 값을 지원합니다.
Create
: 연결이 없는 경우 자동으로 데이터베이스 파일을 만듭니다.FullMutex
: 연결이 serialize된 스레딩 모드로 열립니다.NoMutex
: 연결이 다중 스레딩 모드로 열립니다.PrivateCache
: 연결이 설정된 경우에도 공유 캐시에 참여하지 않습니다.ReadWrite
: 연결에서 데이터를 읽고 쓸 수 있습니다.SharedCache
: 연결이 활성화된 경우 공유 캐시에 참여합니다.ProtectionComplete
: 디바이스가 잠겨 있는 동안 파일이 암호화되고 액세스할 수 없습니다.ProtectionCompleteUnlessOpen
: 파일이 열릴 때까지 암호화되지만 사용자가 디바이스를 잠그더라도 액세스할 수 있습니다.ProtectionCompleteUntilFirstUserAuthentication
: 사용자가 디바이스를 부팅하고 잠금을 해제할 때까지 파일이 암호화됩니다.ProtectionNone
: 데이터베이스 파일이 암호화되지 않았습니다.
데이터베이스 사용 방법에 따라 다른 플래그를 지정해야 할 수 있습니다. 자세한 SQLiteOpenFlags
내용은 sqlite.org 새 데이터베이스 연결 열기를 참조하세요.
데이터베이스 액세스 클래스 만들기
데이터베이스 래퍼 클래스는 앱의 나머지 부분의 데이터 액세스 계층을 추상화합니다. 이 클래스는 쿼리 논리를 중앙 집중화하고 데이터베이스 초기화 관리를 간소화하므로 앱이 증가함에 따라 데이터 작업을 보다 쉽게 리팩터링하거나 확장할 수 있습니다. Todo 앱은 이 용도로 클래스를 TodoItemDatabase
정의합니다.
지연 초기화
사용자 TodoItemDatabase
지정 AsyncLazy<T>
클래스가 나타내는 비동기 지연 초기화를 사용하여 데이터베이스가 처음 액세스할 때까지 초기화를 지연합니다.
public class TodoItemDatabase
{
static SQLiteAsyncConnection Database;
public static readonly AsyncLazy<TodoItemDatabase> Instance = new AsyncLazy<TodoItemDatabase>(async () =>
{
var instance = new TodoItemDatabase();
CreateTableResult result = await Database.CreateTableAsync<TodoItem>();
return instance;
});
public TodoItemDatabase()
{
Database = new SQLiteAsyncConnection(Constants.DatabasePath, Constants.Flags);
}
//...
}
이 Instance
필드는 개체가 아직 없는 경우 개체에 TodoItem
대한 데이터베이스 테이블을 만들고 싱글톤으로 반환 TodoItemDatabase
하는 데 사용됩니다. Instance
형식 AsyncLazy<TodoItemDatabase>
의 필드는 처음 대기될 때 생성됩니다. 여러 스레드가 동시에 필드에 액세스하려고 하면 모두 단일 생성을 사용합니다. 그런 다음, 생성이 완료되면 모든 await
작업이 완료됩니다. 또한 생성이 완료된 후의 모든 await
작업은 값을 사용할 수 있기 때문에 즉시 계속됩니다.
참고 항목
데이터베이스 연결은 단일 데이터베이스 연결이 앱의 수명 동안 사용되도록 하는 정적 필드입니다. 영구 정적 연결을 사용하면 단일 앱 세션 동안 연결을 여러 번 열고 닫는 것보다 더 나은 성능을 제공합니다.
비동기 지연 초기화
데이터베이스 초기화를 시작하고, 실행을 차단하지 않으며, 예외를 catch할 기회를 갖기 위해 샘플 애플리케이션은 클래스가 나타내는 AsyncLazy<T>
비동기 지연 초기화를 사용합니다.
public class AsyncLazy<T>
{
readonly Lazy<Task<T>> instance;
public AsyncLazy(Func<T> factory)
{
instance = new Lazy<Task<T>>(() => Task.Run(factory));
}
public AsyncLazy(Func<Task<T>> factory)
{
instance = new Lazy<Task<T>>(() => Task.Run(factory));
}
public TaskAwaiter<T> GetAwaiter()
{
return instance.Value.GetAwaiter();
}
}
클래스는 AsyncLazy
및 Task<T>
형식을 Lazy<T>
결합하여 리소스 초기화를 나타내는 지연 초기화된 작업을 만듭니다. 생성자에 전달되는 팩터리 대리자는 동기 또는 비동기일 수 있습니다. 팩터리 대리자는 스레드 풀 스레드에서 실행되며 여러 스레드가 동시에 시작하려고 하는 경우에도 두 번 이상 실행되지 않습니다. 팩터리 대리자가 완료되면 지연 초기화된 값을 사용할 수 있으며 인스턴스를 기다리는 모든 메서드가 AsyncLazy<T>
이 값을 받습니다. 자세한 내용은 AsyncLazy를 참조하세요.
데이터 조작 메서드
클래스에는 TodoItemDatabase
만들기, 읽기, 편집 및 삭제의 네 가지 데이터 조작 형식에 대한 메서드가 포함됩니다. SQLite.NET 라이브러리는 SQL 문을 작성하지 않고 개체를 저장하고 검색할 수 있는 간단한 ORM(개체 관계형 맵)을 제공합니다.
public class TodoItemDatabase
{
// ...
public Task<List<TodoItem>> GetItemsAsync()
{
return Database.Table<TodoItem>().ToListAsync();
}
public Task<List<TodoItem>> GetItemsNotDoneAsync()
{
// SQL queries are also possible
return Database.QueryAsync<TodoItem>("SELECT * FROM [TodoItem] WHERE [Done] = 0");
}
public Task<TodoItem> GetItemAsync(int id)
{
return Database.Table<TodoItem>().Where(i => i.ID == id).FirstOrDefaultAsync();
}
public Task<int> SaveItemAsync(TodoItem item)
{
if (item.ID != 0)
{
return Database.UpdateAsync(item);
}
else
{
return Database.InsertAsync(item);
}
}
public Task<int> DeleteItemAsync(TodoItem item)
{
return Database.DeleteAsync(item);
}
}
에서 데이터 액세스 Xamarin.Forms
클래스는 TodoItemDatabase
클래스의 Instance
데이터 액세스 작업을 호출할 수 있는 TodoItemDatabase
필드를 노출합니다.
async void OnSaveClicked(object sender, EventArgs e)
{
var todoItem = (TodoItem)BindingContext;
TodoItemDatabase database = await TodoItemDatabase.Instance;
await database.SaveItemAsync(todoItem);
// Navigate backwards
await Navigation.PopAsync();
}
고급 구성
SQLite는 이 문서 및 샘플 앱에서 다루는 것보다 더 많은 기능을 갖춘 강력한 API를 제공합니다. 다음 섹션에서는 확장성에 중요한 기능을 다룹니다.
자세한 내용은 sqlite.org 대한 SQLite 설명서를 참조하세요.
미리 쓰기 로깅
기본적으로 SQLite는 기존 롤백 저널을 사용합니다. 변경되지 않은 데이터베이스 콘텐츠의 복사본은 별도의 롤백 파일에 기록된 다음 변경 내용이 데이터베이스 파일에 직접 기록됩니다. COMMIT은 롤백 저널이 삭제될 때 발생합니다.
WAL(Write-Ahead Logging)은 먼저 변경 내용을 별도의 WAL 파일에 씁니다. WAL 모드에서 COMMIT은 WAL 파일에 추가되는 특수 레코드로, 단일 WAL 파일에서 여러 트랜잭션이 발생할 수 있습니다. WAL 파일은 검사점이라는 특수 작업에서 데이터베이스 파일로 다시 병합됩니다.
읽기 권한자와 작성기가 서로를 차단하지 않으므로 읽기 및 쓰기 작업을 동시에 수행할 수 있으므로 로컬 데이터베이스의 경우 WAL이 더 빠를 수 있습니다. 그러나 WAL 모드는 페이지 크기를 변경할 수 없으며 데이터베이스에 파일 연결을 추가하고 추가 검사점 작업을 추가합니다.
SQLite.NET WAL을 사용하도록 설정하려면 인스턴스에서 EnableWriteAheadLoggingAsync
메서드를 SQLiteAsyncConnection
호출합니다.
await Database.EnableWriteAheadLoggingAsync();
자세한 내용은 sqlite.org SQLite 미리 쓰기 로깅 을 참조하세요.
데이터베이스 복사
SQLite 데이터베이스를 복사해야 하는 몇 가지 경우가 있습니다.
- 데이터베이스가 애플리케이션과 함께 제공되었지만 모바일 디바이스의 쓰기 가능한 스토리지로 복사하거나 이동해야 합니다.
- 데이터베이스의 백업 또는 복사본을 만들어야 합니다.
- 데이터베이스 파일의 버전 관리, 이동 또는 이름을 바꿔야 합니다.
일반적으로 데이터베이스 파일 이동, 이름 바꾸기 또는 복사는 몇 가지 추가 고려 사항이 있는 다른 파일 형식과 동일한 프로세스입니다.
- 데이터베이스 파일을 이동하기 전에 모든 데이터베이스 연결 닫아야 합니다.
- 미리 쓰기 로깅을 사용하는 경우 SQLite는 공유 메모리 액세스(.shm) 파일 및 (미리 쓰기 로그)(.wal) 파일을 만듭니다. 이러한 파일에도 변경 내용을 적용해야 합니다.
자세한 내용은 .에서 Xamarin.Forms파일 처리를 참조하세요.