2015-06-15 3 views
3

Я хочу дать версию для SQLite (SQLite.NET компонента) базы данных при создании его в Xamarin (Monodroid), и как обрабатывать версию базы данных во время любых изменений в таблице и обновлять ее в игровом магазине. как я могу получить функциональность OnUpgrade, такую ​​как собственный класс SQLiteOpenHelper от android.Как установить SQLite (SQLite.NET компонент) базы данных версии в Xamarin Android

Я использую ниже способом

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

У меня также такая же проблема, не в состоянии обновить версию базы данных –

+0

показать свой расширяемый класс sqlitehelper. –

ответ

0

ли вы создать SQLITE помощник в Android-Xamarin?

Создать класс, который расширяет 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 методы, чтобы помочь вам.

+0

Привет, Murtaza, но я создаю базу данных, используя метод Xamarin. http://developer.xamarin.com/guides/cross-platform/xamarin-forms/working-with/databases/. Я не использую класс SQLiteOpenHelper. Спасибо –

+0

показать свой код .. –

+0

строка folder = Environment.GetFolderPath (Environment.SpecialFolder.Personal); var conn = новый SQLiteConnection (System.IO.Path.Combine (папка, «stocks.db»)); conn.CreateTable (); conn.CreateTable (); –

5

Я не знаком с этим «SQLiteOpenHelper», но недавно создал SQLite «миграция/обновление» для приложения Android, которое я делаю. Он будет работать с IOS, а также с использованием SQLITE-NET PCL. Я открыт, чтобы услышать ваши мысли об этом. Вероятно, есть лучшие способы сделать это, но это мой подход:

  1. Моей базы данных имеют несколько основных таблиц (inmutable данных), которая нуждается в обновлении
  2. Других таблиц являются только пользовательскими данными, с внешними ключами ссылки мастера данные. Эти таблицы обычно не обновляются.
  3. по обновлению/миграции в основном изменить основные данные, уважая текущие пользовательские данные

Позвольте мне объяснить это с фактическим случае:

  1. Основные данные были бы «ингредиенты»
  2. таблица данных пользователя будет «Сток»
  3. Состав таблица может быть обновлена ​​/ модернизирован, когда обновления приложения
  4. Пользователь не может изменить таблицу ингредиентов, как это основная данные

Моего код еще не совершен, так как я должен работать каждый миграцию в рамках сделки, но подмигнули в моем списке TODO :)

"DatabaseMigrationService.RunMigrations()" вызывается при запуске приложения:

public interface IMigrationService 
{ 
    Task RunMigrations(); 
} 

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

DatabaseMigrationService

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» loope мы проверяем текущую версию базы данных (сохраняется в хранилище устройств). Если есть новое обновление (миграция), мы запустим его и обновим сохраненный ключ «DatabaseVersion».

Как вы можете видеть, есть 2 вспомогательные классы, представленные в конструкторе: ISQLite SQLite и ISettingsService настройки I'm с помощью 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", 
      }; 
     } 
    } 

я надеюсь это поможет. В любом случае, если кто-то знает лучший способ сделать это, пожалуйста, поделитесь

Смежные вопросы