2015-06-15 19 views
3

我想給的版本爲源碼(SQLite.NET分量)數據庫,而在Xamarin(MonoDroid的)創建它,而如何在表中的任何修改期間處理數據庫的版本並在遊戲存儲庫中更新。我如何獲得像Android本機SQLiteOpenHelper類的OnUpgrade功能。如何設置的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

你創建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方法來幫助你的類。

+0

Hi Murtaza,但我使用Xamarin方法創建數據庫。 http://developer.xamarin.com/guides/cross-platform/xamarin-forms/working-with/databases/。我沒有使用SQLiteOpenHelper類。謝謝 –

+0

顯示你的代碼.. –

+0

string folder = Environment.GetFolderPath(Environment.SpecialFolder.Personal); var conn = new SQLiteConnection(System.IO.Path.Combine(folder,「stocks.db」)); conn.CreateTable (); conn.CreateTable (); –

5

我不熟悉那個「SQLiteOpenHelper」,但我最近爲Android應用程序做了一個SQLite「遷移/升級」管理器。它將與IOS以及我使用SQLITE-NET PCL一起工作。我很樂意聽到你的想法。也許有更好的方法來做到這一點,但是這是我的方法:

  1. 我的數據庫有一些主表(inmutable數據)需要升級
  2. 其它表都只是用戶數據,與外鍵引用 主數據。這些表通常不會更新。
  3. 升級/遷移基本修改主數據,尊重 當前用戶數據

請允許我用實際案例解釋:

  1. 主數據將是「配料」
  2. 用戶數據表爲「庫存」
  3. 應用升級時可更新/升級成分表
  4. 用戶不容修改成分表,因爲它是主數據

我的代碼還不夠完善,我應該運行在一個事務中的每個遷移,但it's在我的待辦事項清單:)

「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」循環中,我們檢查當前的數據庫版本(保存在設備存儲中)。如果有更新的更新(遷移),我們運行它並更新持久化的「DatabaseVersion」鍵。

正如你所看到的,有在構造函數中提供了2個輔助類: ISQLite源碼ISettingsService設置使用MvvmCross(這不是強制性的)和ISQLite I'm在每個平臺上實現(IOS /機器人)。 I'll顯示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", 
      }; 
     } 
    } 

一世 希望這可以幫助。無論如何,如果有人知道更好的方式來做到這一點,請分享

相關問題