#c# #xamarin #xamarin.forms
#c# #xamarin #xamarin.forms
Вопрос:
Недавно я заметил пару статей, в которых упоминалось о создании соединений SQLite в общем коде. Это что-то новое, поскольку я всегда делал это таким образом с интерфейсом:
Есть ли способ, которым все это можно было бы выполнить в общем коде, а не в приведенной ниже реализации, для которой требуется общий код, iOS и Android?
Общий код:
namespace Memorise
{
public interface ISQLiteDB1
{
(SQLiteConnection, bool) GetConnection();
}
}
Код iOS:
[assembly: Dependency(typeof(ISQLiteDB_iOS))]
namespace Memorise.iOS
{
public class ISQLiteDB_iOS : ISQLiteDB
{
public (SQLite.SQLiteConnection, bool) GetConnection(string _dbName)
{
bool newDb = false ;
var sqliteFilename = _dbName;
string documentsPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
string libraryPath = Path.Combine(documentsPath, "..", "Library");
Debug.WriteLine(documentsPath);
Debug.WriteLine(libraryPath);
var path = Path.Combine(libraryPath, sqliteFilename);
switch (_dbName)
{
case CONST.DB1Name:
if (File.Exists(path))
{
newDb = false;
File.Delete(path);
}
else
{
newDb = true;
}
File.Copy(sqliteFilename, path);
break;
case CONST.DB2Name:
if (File.Exists(path))
{
newDb = false;
}
else
{
newDb = true;
File.Create(path);
}
break;
case CONST.DB3Name:
if (File.Exists(path))
{
newDb = false;
}
else
{
newDb = true;
File.Copy(sqliteFilename, path);
}
break;
}
return (new SQLite.SQLiteConnection(path), newDb);
}
}
}
Код Android:
[assembly: Dependency(typeof(SQLiteDB1_Android))]
namespace Memorise.Droid
{
public class SQLiteDB1_Android : ISQLiteDB1
{
public (SQLite.SQLiteConnection, bool) GetConnection()
{
bool newDb;
var sqliteFilename = "db1.db3";
string documentsPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal); // Documents folder
var path = Path.Combine(documentsPath, sqliteFilename);
if (File.Exists(path))
{
newDb = false;
File.Delete(path);
}
else
{
newDb = true;
}
FileStream writeStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write);
ReadWriteStream(Android.App.Application.Context.Assets.Open(sqliteFilename), writeStream);
return (new SQLite.SQLiteConnection(path), newDb);
}
void ReadWriteStream(Stream readStream, Stream writeStream)
{
int Length = 256;
Byte[] buffer = new Byte[Length];
int bytesRead = readStream.Read(buffer, 0, Length);
while (bytesRead > 0)
{
writeStream.Write(buffer, 0, bytesRead);
bytesRead = readStream.Read(buffer, 0, Length);
}
readStream.Close();
writeStream.Close();
}
}
}
Комментарии:
1. вы можете проверить это github.com/praeclarum/sqlite-net
2. вы можете легко создать соединение в общем коде, как демонстрируют документы. Однако вы ТАКЖЕ копируете существующий файл БД из пакета приложений в папку, доступную для записи — эта часть не может быть выполнена в общем коде.
3. @Jason — Есть ли какой-нибудь способ обойти это? Я только считываю (получаю данные) из файла БД в пакете приложений? Я заполняю другую базу данных данными, которые я прочитал.
4. вы можете включить данные в качестве встроенного ресурса в свою общую библиотеку и прочитать их из общего кода
5. Посмотрите на этот регистр и войдите в систему, используя SQLite в Xamarin. Формы
Ответ №1:
Мы можем использовать FileSystem
API в Xamarin.Пакет Essentials NuGet для поиска каталога данных приложения для конкретной платформы и создания нашего подключения к базе данных в общем коде.
var databasePath = Path.Combine(FileSystem.AppDataDirectory, "Database.db3");
var databaseConnection = new SQLiteConnection(databasePath);
Я углубляюсь в это сообщение в блоге о Xamarin SQLite: https://codetraveler.io/2019/11/26/efficiently-initializing-sqlite-database /
Ответ №2:
Как я вижу ваш код, у вас есть два файла базы данных, db1 и db2. Кроме того, я вижу, что db1 копируется из ресурсов проекта и вставляется в локальную папку. Итак, вам нужно поместить файл db1.db3 в папку Resources и пометить его как EmbeddedResource. Этот шаг важен.
Остальную часть вашего кода можно упростить и изменить на :-
public DataManager()
{
var db1Path = Path.Combine(BasePath, CONST.DB1Name);
newDb1 = !File.Exists(db1Path);
if (newDb1)
{
var assembly = IntrospectionExtensions.GetTypeInfo(typeof(DataManager)).Assembly;
Stream stream = assembly.GetManifestResourceStream("YourProjectName.Resources.db1.db3");
using (var db1SourceStream = new FileStream(db1Path, FileMode.Create, FileAccess.ReadWrite))
{
stream.CopyTo(db1SourceStream);
db1SourceStream.Close();
}
}
db1 = new SQLiteConnection(db1Path);
var db2Path = Path.Combine(BasePath, CONST.DB2Name);
newDb2 = !File.Exists(db2Path);
db2 = new SQLiteConnection(db2Path);
db2.TimeExecution = true;
}
Ответ №3:
Используя sqlite-net-pcl, мне удалось создать слой данных в стандартной библиотеке .NET и использовать его в Xamarin Forms (iOS), а также в WPF (но вы, вероятно, можете расширить его также, например, для Android и UWP.
Основная хитрость заключается в том, чтобы указать другой путь к локальному хранилищу приложения для файла базы данных, используя компиляцию DI или platform if / else.
Также один трюк для внешних ключей и каскад удаления («PRAGMA foreign_keys=ON;»), что для некоторых платформ было достаточно вызвать его только один раз, после создания объекта Connection, но только для iOS мне нужно было изменить его, чтобы он вызывался перед каждой командой, нуждающейся в каскадефункциональность внешних ключей.
protected readonly SQLiteAsyncConnection db;
protected abstract List<BaseMigration> Migrations { get; }
public YourDB(string dbPath)
{
db = new SQLiteAsyncConnection(dbPath);
db.ExecuteAsync("PRAGMA foreign_keys=ON;").Wait();
//ExecuteMigrations(db).Wait();
}