【问题标题】:How to set SQLite(SQLite.NET component) database Version in Xamarin Android如何在 Xamarin Android 中设置 SQLite(SQLite.NET 组件)数据库版本
【发布时间】:2015-08-30 15:02:39
【问题描述】:

我想在 Xamarin(Monodroid) 中创建时给出 sqlite(SQLite.NET 组件) 数据库的版本,以及如何处理数据库的版本在表中的任何修改和 Playstore 中的更新期间。如何获得 OnUpgrade 功能,例如 android 原生 SQLiteOpenHelper 类。

我正在使用以下方式

string folder = Environment.GetFolderPath   (Environment.SpecialFolder.Personal);
var conn = new SQLiteConnection (System.IO.Path.Combine (folder, "stocks.db"));
conn.CreateTable<Stock>();
conn.CreateTable<Valuation>();

【问题讨论】:

  • 我也遇到同样的问题,无法升级数据库版本
  • 显示你的 sqlitehelper 扩展类。

标签: c# android sqlite xamarin xamarin.android


【解决方案1】:

我不熟悉“SQLiteOpenHelper”,但我最近为我正在做的 Android 应用程序构建了一个 SQLite“迁移/升级”管理器。它将与 IOS 以及我正在使用 SQLITE-NET PCL 一起使用。我愿意听听您对此的看法。可能有更好的方法来做到这一点,但这是我的方法:

  1. 我的数据库有一些需要升级的主表(不可变数据)
  2. 其他表只是用户数据,外键引用 主要的数据。这些表格通常不会更新。
  3. 升级/迁移基本上修改了主数据,尊重 当前用户数据

请允许我用一个实际的案例来解释一下:

  1. 主数据将是“成分”
  2. 用户数据表为“Stock”
  3. 成分表可以在应用升级时更新/升级
  4. 用户不能修改成分表,因为它是主数据

我的代码仍然不完美,因为我应该在事务中运行每个迁移,但它在我的 TODO 列表中:)

应用启动时调用“DatabaseMigrationService.RunMigrations()”:

public interface IMigrationService
{
    Task RunMigrations();
}

public interface IMigration
{
    IMigration UseConnection(SQLiteAsyncConnection connection);
    Task<bool> Run();
}

数据库迁移服务

public sealed class DatabaseMigrationService : IMigrationService
    {
        private ISQLite sqlite;
        private ISettingsService settings;
        private List<IMigration> migrations;

        public DatabaseMigrationService(ISQLite sqlite, ISettingsService settings)
        {
            this.sqlite = sqlite;
            this.settings = settings;

            SetupMigrations();
        }

        private void SetupMigrations()
        {
            migrations = new List<IMigration> {
                new Migration1(),
                new Migration2(),
                new Migration3(),
                new Migration4(),
                new Migration5(),
                new Migration6()
            };
        }

        public async Task RunMigrations()
        {
            // TODO run migrations in a transaction, otherwise, if and error is found, the app could stay in a horrible state

            if (settings.DatabaseVersion < migrations.Count)
            {
                var connection = new SQLiteAsyncConnection(() => sqlite.GetConnectionWithLock());

                while (settings.DatabaseVersion < migrations.Count)
                {
                    var nextVersion = settings.DatabaseVersion + 1;
                    var success = await migrations[nextVersion - 1].UseConnection(connection).Run();

                    if (success)
                    {
                        settings.DatabaseVersion = nextVersion;
                    }
                    else
                    {
                        MvxTrace.Error("Migration process stopped after error found at {0}", migrations[nextVersion - 1].GetType().Name);
                        break;
                    }
                }
            }
        }
    }

逻辑很简单。在“while”循环中,我们检查当前数据库版本(保存在设备存储中)。如果有更新的更新(迁移),我们运行它并更新持久的“DatabaseVersion”键。

如您所见,构造函数中提供了 2 个辅助类: ISQLite sqliteISettingsService 设置 我正在使用 MvvmCross(这不是强制性的)并且 ISQLite 在每个平台(IOS/ANDROID)上都实现了。我将展示 Android 实现:

public class SqliteAndroid : ISQLite
    {
        private SQLiteConnectionWithLock persistentConnection;

        public SQLiteConnectionWithLock GetConnectionWithLock()
        {
            if (persistentConnection == null)
            {
                var dbFilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), Constants.DB_FILE_NAME);
                var platform = new SQLitePlatformAndroid();
                var connectionString = new SQLiteConnectionString(dbFilePath, true);
                persistentConnection = new SQLiteConnectionWithLock(platform, connectionString);
            }

            return persistentConnection;
        }
    }

设置只是一个类,它基于此插件读取/写入简单值到平台持久存储:https://github.com/jamesmontemagno/Xamarin.Plugins/tree/master/Settings

public interface ISettingsService
    {
        int DatabaseVersion { get; set; }
        [...]
    }

    public class SettingsService : ISettingsService
    {
        private string databaseVersionKey = "DatabaseVersion";
        public int DatabaseVersion
        {
            get { return CrossSettings.Current.GetValueOrDefault(databaseVersionKey, 0); } 
            set
            {
                CrossSettings.Current.AddOrUpdateValue(databaseVersionKey, value);
            }
        }
}

最后是迁移代码。 这是迁移的基类:

public abstract class BaseMigration : IMigration
    {
        protected SQLiteAsyncConnection connection;
        protected string migrationName;

        public IMigration UseConnection(SQLiteAsyncConnection connection)
        {
            this.connection = connection;
            migrationName = this.GetType().Name;
            return this;
        }

        public virtual async Task<bool> Run()
        {
            try
            {
                MvxTrace.Trace("Executing {0}", migrationName);
                int result = 0;
                var commands = GetCommands();
                foreach (var command in commands)
                {
                    MvxTrace.Trace("Executing command: '{0}'", command);
                    try
                    {
                        var commandResult = await connection.ExecuteAsync(command);
                        MvxTrace.Trace("Executed command {0}. Rows affected {1}", command, commandResult);
                        result = result + commandResult;
                    }
                    catch (Exception ex)
                    {
                        MvxTrace.Error("Command execution error: {0}", ex.Message);
                        throw ex;
                    }
                }

                MvxTrace.Trace("{0} completed. Rows affected {1}", migrationName, result);
                return result > 0;
            }
            catch (Exception ex)
            {
                MvxTrace.Error("{0} error: {1}", migrationName, ex.Message);
                return false;
            }
        }

        protected abstract List<string> GetCommands();
    }

迁移 1:

internal sealed class Migration1 : BaseMigration
    {
        override protected List<string> GetCommands()
        {
            return new List<string> {
                "DROP TABLE IF EXISTS \"Recipes\";\n",
                "DROP TABLE IF EXISTS \"RecipeIngredients\";\n",
                "DROP TABLE IF EXISTS \"Ingredients\";\n",
                "CREATE TABLE \"Ingredients\" (\n\t " +
                "\"Id\" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,\n\t " +
                "\"Name\" TEXT(35,0) NOT NULL COLLATE NOCASE,\n\t " +
                "\"Family\" INTEGER NOT NULL,\n\t " +
                "\"MeasureType\" INTEGER NOT NULL,\n\t " +
                "\"DaysToExpire\" INTEGER NOT NULL,\n\t " +
                "\"Picture\" TEXT(100,0) NOT NULL\n" +
                ");",
                "INSERT INTO \"Ingredients\" VALUES ('1', 'Aceite', '1', '2', '730', 'z_aceite_de_oliva.jpg');\n",
                "INSERT INTO \"Ingredients\" VALUES ('2', 'Sal', '1', '1', '9999', 'z_sal.jpg');\n",
                "INSERT INTO \"Ingredients\" VALUES ('3', 'Cebolla', '3', '1', '30', 'z_cebolla.jpg');\n",
                "INSERT INTO \"Ingredients\" VALUES ('4', 'Naranja', '4', '1', '21', 'z_naranja.jpg');\n",
                "INSERT INTO \"Ingredients\" VALUES ('5', 'Bacalao', '5', '1', '2', 'z_bacalao.jpg');\n",
                "INSERT INTO \"Ingredients\" VALUES ('6', 'Yogur', '6', '2', '21', 'z_yogur.jpg');\n",
                "INSERT INTO \"Ingredients\" VALUES ('7', 'Garbanzos', '7', '1', '185', 'z_garbanzos.jpg');\n",
                "INSERT INTO \"Ingredients\" VALUES ('8', 'Pimienta', '8', '1', '3', 'z_pimienta.jpg');\n",
                "INSERT INTO \"Ingredients\" VALUES ('9', 'Chocolate', '9', '1', '90', 'z_chocolate.jpg');\n",
                "INSERT INTO \"Ingredients\" VALUES ('10', 'Ketchup', '10', '2', '365', 'z_ketchup.jpg');\n",
                "INSERT INTO \"Ingredients\" VALUES ('11', 'Espinaca', '3', '1', '5', 'z_espinaca.jpg');\n",
                "INSERT INTO \"Ingredients\" VALUES ('12', 'Limón', '4', '3', '30', 'z_limon.jpg');\n",
                "INSERT INTO \"Ingredients\" VALUES ('13', 'Calamar', '5', '1', '2', 'z_calamares.jpg');\n",
                "INSERT INTO \"Ingredients\" VALUES ('14', 'Mantequilla', '6', '1', '21', 'z_mantequilla.jpg');\n",
                "INSERT INTO \"Ingredients\" VALUES ('15', 'Perejil', '8', '1', '7', 'z_perejil.jpg');\n",
                "INSERT INTO \"Ingredients\" VALUES ('16', 'Cacao', '9', '1', '365', 'z_cacao.jpg');\n",
                "INSERT INTO \"Ingredients\" VALUES ('17', 'Mayonesa', '10', '2', '7', 'z_mayonesa.jpg');\n",
                "INSERT INTO \"Ingredients\" VALUES ('18', 'Arroz', '7', '1', '999', 'z_arroz.jpg');\n",
                "INSERT INTO \"Ingredients\" VALUES ('19', 'Pepino', '3', '1', '15', 'z_pepino.jpg');\n",
                "INSERT INTO \"Ingredients\" VALUES ('20', 'Frambuesa', '4', '1', '3', 'z_frambuesa.jpg');"
            };
        }
    }

迁移 2:

internal sealed class Migration2 : BaseMigration
    {
        override protected List<string> GetCommands()
        {
            return new List<string> {
                "INSERT INTO \"Ingredients\" VALUES ('21', 'Otros (líquidos)', '0', '2', '365', 'z_otros_liquidos.png');\n",
                "INSERT INTO \"Ingredients\" VALUES ('22', 'Otros (sólidos)', '0', '1', '365', 'z_otros_solidos.png');\n",
                "INSERT INTO \"Ingredients\" VALUES ('23', 'Otros (unidades)', '0', '3', '365', 'z_otros_unidades.png');"
            };
        }
    }

使用更新命令进行迁移的示例:

internal sealed class Migration4 : BaseMigration
    {
        protected override List<string> GetCommands()
        {
            return new List<string> {
                "UPDATE Ingredients SET MeasureType = 3, Name = 'Ajo (diente)' WHERE Id = 106",
                "UPDATE Ingredients SET MeasureType = 3 WHERE Id = 116",
            };
        }
    }

我希望这会有所帮助。无论如何,如果有人知道更好的方法,请分享

【讨论】:

    【解决方案2】:

    您是否在 Android-Xamarin 中创建了 sqlite 助手?

    创建一个扩展 SqliteHelper 的类

    private class DatabaseHelper : SQLiteOpenHelper{
        internal DatabaseHelper(Context context)
            : base(context, dBName, null, databaseVersion){
        }
        public override void OnCreate(SQLiteDatabase db){
           db = SQLiteDatabase.OpenDatabase (destinationPath, null, 0);
        }
        public override void OnUpgrade(SQLiteDatabase db, int oldVersion, int newVersion){
            Log.Wtf(tag, "Upgrading database from version " + oldVersion + " to " + newVersion + ", which will destroy all old data");
            this.OnCreate(db);
        }
    }
    

    P.S 它有 onCreate 和 onUpgrade 方法可以帮助你。

    【讨论】:

    • 嗨 Murtaza,但我正在使用 Xamarin 方法创建数据库。 developer.xamarin.com/guides/cross-platform/xamarin-forms/…。我没有使用 SQLiteOpenHelper 类。谢谢
    • 字符串文件夹 = Environment.GetFolderPath (Environment.SpecialFolder.Personal); var conn = new SQLiteConnection (System.IO.Path.Combine (folder, "stocks.db")); conn.CreateTable(); conn.CreateTable();
    • 我认为它不支持升级版本号。我认为它以某种方式在内部处理。
    • 不使用插件,您可以使用默认方式创建帮助程序类
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-27
    • 2014-05-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多