2017-02-18 7 views
2

我有一個類Foo財產ID如下如何安全地重命名錶示Xamarin.Forms中的SQLite實體的類的屬性?

public class Foo 
{ 
    [PrimaryKey] 
    public int Id {get; set;} 
} 

但我想重新命名編號是FooId。當我這樣做,重新安裝Xamarin的應用程序作爲一個更新,我得到插入以下錯誤:

table Foo has no column named FooId 

很顯然,該表的定義正在發生變化,我錯過了一些明確的命令將sqlite的數據庫,以幫助這一進程。

值得注意的是,如果我刪除應用程序並重新安裝它,它會正常工作。

使用從nuget安裝的SQLite-net PCL版本1.1.1。

回答

2

首先,我會建議使用SQLite-net-pcl代替SQLite.Net -PCL(注意SQLite和Net之間的破折號),因爲Android 6.0+上的issues帽子從來沒有被修復(據我所知至少)這會阻止你的應用在Android 6.0+上運行。這些API幾乎完全相同,但切換後您可以使用更多的API。

要修復列/模型屬性重命名的問題,以及在數據已經在表中之後添加列/模型屬性的問題,我嘗試檢測該異常。如果檢測到該代碼,則代碼將刪除該表,重新創建該表並重新插入所有數據。

我使用通用的存儲庫,所以它使它更容易,但下面的代碼應該給你一個想法。此外,SQLite.Net-PCL沒有DropTableAsync() API,所以你將不得不使用ExecuteAsync()如果你不改變庫:

public static async Task<string> UpdateTableSchemaAsync<T>() where T : class, new() { 
    IGenericRepository<T> genericRepo = new GenericRepository<T>(); 

    try { 
     List<T> savedObjects = await genericRepo.AllAsync(); //Collect all current records before dropping table 

     if(savedObjects != null && savedObjects.Count > 0) { 
      await genericRepo.DropTableAsync(); 
      await genericRepo.CreateTableAsync(); 

      // You could do some data transformation here if necessary, such as transferring all Foo.FooId values to the Foo.Id column 

      await genericRepo.InsertAllAsync(savedObjects); //Insert all objects back 
     } else { 
      await genericRepo.DropTableAsync(); 
      await genericRepo.CreateTableAsync(); 
     } 

     return null; 
    } catch(Exception ex) { 
     Debug.WriteLine("\nIn ModelHelpers.UpdateTableSchemaAsync() - Exception attempting to recreate " + typeof(T).Name + " data:\n" + ex.GetBaseException() + "\n"); 
     return "An error occurred while attempting to update the " + typeof(T).Name + " data."; 
    } 
} 

使用上述通過我作爲一個方法傳遞所有插入或更新操作Func這使該Func一個try/catch內檢測SQLiteException,並期待在Exception.Message(是的,我知道這是跛腳),如果被檢測到,我們運行UpdateTableSchemaAsync()方法和嘗試執行傳遞再次插入或更新Func

//Again this method is within a generic repository so T is defined through the generic repo instantiation 
public async Task<TResult> ExecuteSafeAsync<TResult>(Func<Task<TResult>> taskFunc, [CallerMemberName] string caller = "") { 
    try { 
     return await taskFunc.Invoke(); 
    } catch(SQLiteException sqlException) { 
     System.Diagnostics.Debug.WriteLine("\nIn GenericRepository.ExecuteSafeAsync() via " + caller + " - Exception attempting to run query on " + typeof(T).Name + "\n" + sqlException.GetBaseException() + "\n"); 

     if(sqlException.Message.ToLowerInvariant().Contains("column")) { //The error being searched for is "no such column: <column name>" 
      try { 
       await ModelHelpers.UpdateTableSchemaAsync<T>(); 
      } catch(Exception ex) { //If this fails for what ever reason, we do not want the ModelHelpers.UpdateTableSchemaAsync() method's exception being the one that gets detected upstream 
       System.Diagnostics.Debug.WriteLine("\nIn GenericRepository.ExecuteSafeAsync() via " + caller + " - Exception attempting to run rebuild " + typeof(T).Name + " table after detecting SQLite Exception\n" + ex.GetBaseException() + "\n"); 
      } 

      return await taskFunc.Invoke(); //Now that the table has been recreated lets try to run it again 
     } 

     throw; 
    } 
} 
+0

您推薦的圖書館就是我們正在使用的圖書館。但是,正如你所指出的那樣,我可以看到混亂。我嘗試了類似於你的解決方案的東西,但是在收集savedObjects時會丟失值,因爲名稱改變的列全部爲空值。看來我需要以某種方式將該表中的值轉換爲對象列表(儘管我無法使其工作)或諸如此類。有什麼建議麼? –

+0

@GarrettDanielDeMeyer更正,所以當您更改列/模型屬性名稱時,您需要保留舊的列/模型屬性名稱,因爲它具有舊值。然後,您需要提取這些值並將其移至新的列名稱。 – hvaughan3

相關問題