Usando ADO.NET com o Xamarin.iOS
O Xamarin tem suporte interno para o banco de dados SQLite que está disponível no iOS, exposto usando sintaxe familiar semelhante a ADO.NET. O uso dessas APIs exige que você escreva instruções SQL processadas pelo SQLite, como CREATE TABLE
instruções e INSERT
SELECT
.
Referências de Assembly
Para usar o sqlite de acesso por meio de ADO.NET você deve adicionar System.Data
referências e Mono.Data.Sqlite
ao seu projeto iOS, conforme mostrado aqui (para exemplos em Visual Studio para Mac e Visual Studio):
Clique com o botão direito do mouse em Referências > Editar Referências... e clique para selecionar os assemblies necessários.
Sobre Mono.Data.Sqlite
Usaremos a Mono.Data.Sqlite.SqliteConnection
classe para criar um arquivo de banco de dados em branco e, em seguida, criar uma instância de SqliteCommand
objetos que podemos usar para executar instruções SQL no banco de dados.
Criando um banco de dados em branco – chame o
CreateFile
método com um caminho de arquivo válido (ou seja, gravável). Você deve marcar se o arquivo já existe antes de chamar esse método, caso contrário, um novo banco de dados (em branco) será criado na parte superior do antigo e os dados no arquivo antigo serão perdidos:Mono.Data.Sqlite.SqliteConnection.CreateFile (dbPath);
Observação
A
dbPath
variável deve ser determinada de acordo com as regras discutidas anteriormente neste documento.Criando uma conexão de banco de dados – depois que o arquivo de banco de dados SQLite tiver sido criado, você poderá criar um objeto de conexão para acessar os dados. A conexão é construída com uma cadeia de conexão que assume a forma de
Data Source=file_path
, conforme mostrado aqui:var connection = new SqliteConnection ("Data Source=" + dbPath); connection.Open(); // do stuff connection.Close();
Conforme mencionado anteriormente, uma conexão nunca deve ser reutilmente usada em threads diferentes. Em caso de dúvida, crie a conexão conforme necessário e feche-a quando terminar; mas lembre-se de fazer isso com mais frequência do que o necessário também.
Criando e executando um comando de banco de dados – depois de termos uma conexão, podemos executar comandos SQL arbitrários nele. O código a seguir mostra uma instrução CREATE TABLE sendo executada.
using (var command = connection.CreateCommand ()) { command.CommandText = "CREATE TABLE [Items] ([_id] int, [Symbol] ntext, [Name] ntext);"; var rowcount = command.ExecuteNonQuery (); }
Ao executar o SQL diretamente no banco de dados, você deve tomar as precauções normais para não fazer solicitações inválidas, como tentar criar uma tabela que já existe. Acompanhe a estrutura do banco de dados para que você não cause uma SqliteException, como "A tabela de erros do SQLite [Itens] já existe".
Acesso básico a dados
O código de exemplo DataAccess_Basic deste documento tem esta aparência ao ser executado no iOS:
O código a seguir ilustra como executar operações SQLite simples e mostra os resultados como texto na janela main do aplicativo.
Você precisará incluir estes namespaces:
using System;
using System.IO;
using Mono.Data.Sqlite;
O exemplo de código a seguir mostra uma interação de banco de dados inteira:
- Criando o arquivo de banco de dados
- Inserindo alguns dados
- Consulta dos dados
Essas operações normalmente apareceriam em vários locais em todo o código, por exemplo, você pode criar o arquivo de banco de dados e as tabelas quando seu aplicativo é iniciado pela primeira vez e executar leituras e gravações de dados em telas individuais em seu aplicativo. No exemplo abaixo, foram agrupados em um único método para este exemplo:
public static SqliteConnection connection;
public static string DoSomeDataAccess ()
{
// determine the path for the database file
string dbPath = Path.Combine (
Environment.GetFolderPath (Environment.SpecialFolder.Personal),
"adodemo.db3");
bool exists = File.Exists (dbPath);
if (!exists) {
Console.WriteLine("Creating database");
// Need to create the database before seeding it with some data
Mono.Data.Sqlite.SqliteConnection.CreateFile (dbPath);
connection = new SqliteConnection ("Data Source=" + dbPath);
var commands = new[] {
"CREATE TABLE [Items] (_id ntext, Symbol ntext);",
"INSERT INTO [Items] ([_id], [Symbol]) VALUES ('1', 'AAPL')",
"INSERT INTO [Items] ([_id], [Symbol]) VALUES ('2', 'GOOG')",
"INSERT INTO [Items] ([_id], [Symbol]) VALUES ('3', 'MSFT')"
};
// Open the database connection and create table with data
connection.Open ();
foreach (var command in commands) {
using (var c = connection.CreateCommand ()) {
c.CommandText = command;
var rowcount = c.ExecuteNonQuery ();
Console.WriteLine("\tExecuted " + command);
}
}
} else {
Console.WriteLine("Database already exists");
// Open connection to existing database file
connection = new SqliteConnection ("Data Source=" + dbPath);
connection.Open ();
}
// query the database to prove data was inserted!
using (var contents = connection.CreateCommand ()) {
contents.CommandText = "SELECT [_id], [Symbol] from [Items]";
var r = contents.ExecuteReader ();
Console.WriteLine("Reading data");
while (r.Read ())
Console.WriteLine("\tKey={0}; Value={1}",
r ["_id"].ToString (),
r ["Symbol"].ToString ());
}
connection.Close ();
}
Consultas mais complexas
Como o SQLite permite que comandos SQL arbitrários sejam executados nos dados, você pode executar qualquer instrução CREATE, INSERT, UPDATE, DELETE ou SELECT que desejar. Você pode ler sobre os comandos SQL compatíveis com o SQLite no site do Sqlite. As instruções SQL são executadas usando um dos três métodos em um objeto SqliteCommand:
- ExecuteNonQuery – normalmente usado para criação de tabela ou inserção de dados. O valor retornado para algumas operações é o número de linhas afetadas, caso contrário, é -1.
- ExecuteReader – usado quando uma coleção de linhas deve ser retornada como um
SqlDataReader
. - ExecuteScalar – recupera um único valor (por exemplo, uma agregação).
EXECUTENONQUERY
As instruções INSERT, UPDATE e DELETE retornarão o número de linhas afetadas. Todas as outras instruções SQL retornarão -1.
using (var c = connection.CreateCommand ()) {
c.CommandText = "INSERT INTO [Items] ([_id], [Symbol]) VALUES ('1', 'APPL')";
var rowcount = c.ExecuteNonQuery (); // rowcount will be 1
}
EXECUTEREADER
O método a seguir mostra uma cláusula WHERE na instrução SELECT. Como o código está criando uma instrução SQL completa, ele deve ter cuidado para escapar de caracteres reservados, como a aspa (') em torno de cadeias de caracteres.
public static string MoreComplexQuery ()
{
var output = "";
output += "\nComplex query example: ";
string dbPath = Path.Combine (
Environment.GetFolderPath (Environment.SpecialFolder.Personal), "ormdemo.db3");
connection = new SqliteConnection ("Data Source=" + dbPath);
connection.Open ();
using (var contents = connection.CreateCommand ()) {
contents.CommandText = "SELECT * FROM [Items] WHERE Symbol = 'MSFT'";
var r = contents.ExecuteReader ();
output += "\nReading data";
while (r.Read ())
output += String.Format ("\n\tKey={0}; Value={1}",
r ["_id"].ToString (),
r ["Symbol"].ToString ());
}
connection.Close ();
return output;
}
O método ExecuteReader retorna um objeto SqliteDataReader. Além do método Read mostrado no exemplo, outras propriedades úteis incluem:
- RowsAffected – contagem das linhas afetadas pela consulta.
- HasRows – se alguma linha foi retornada.
EXECUTESCALAR
Use isso para instruções SELECT que retornam um único valor (como uma agregação).
using (var contents = connection.CreateCommand ()) {
contents.CommandText = "SELECT COUNT(*) FROM [Items] WHERE Symbol <> 'MSFT'";
var i = contents.ExecuteScalar ();
}
O ExecuteScalar
tipo de retorno do método é object
– você deve converter o resultado dependendo da consulta de banco de dados. O resultado pode ser um inteiro de uma consulta COUNT ou uma cadeia de caracteres de uma única consulta SELECT de coluna. Observe que isso é diferente de outros métodos Execute que retornam um objeto leitor ou uma contagem do número de linhas afetadas.
Microsoft.Data.Sqlite
Há outra biblioteca Microsoft.Data.Sqlite
, que pode ser instalada do NuGet, que é funcionalmente equivalente a Mono.Data.Sqlite
e permite os mesmos tipos de consultas.
Há uma comparação entre as duas bibliotecas e alguns detalhes específicos do Xamarin. Mais importante para aplicativos Xamarin.iOS, você deve incluir uma chamada de inicialização:
// required for Xamarin.iOS
SQLitePCL.Batteries_V2.Init();