2017-02-18 3 views
2

У меня есть класс Foo со свойством Id, как такКак я могу безопасно переименовать свойство в классе, представляющем объект SQLite в Xamarin.Forms?

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

Но я хотел бы переименовать Id быть FooId. Когда я делаю это и переустановить приложение Xamarin как обновление, я получаю следующее сообщение об ошибке при вставке:

table Foo has no column named FooId 

Очевидно, что определение таблицы меняется, и мне не хватает какой-то явную команду к БД SQLite, чтобы помочь этому процессу ,

Обратите внимание: он отлично работает, если я удалю приложение и заново установлю его.

Использование SQLite-net PCL версии 1.1.1, установленного из nuget.

ответ

2

Во-первых, я предложил бы использовать SQLite-net-pcl вместо SQLite.Net -PCL (обратите внимание на тире между SQLite и сетью) из-за issues на Android 6.0+ t шляпа никогда не была исправлена ​​(насколько я знаю, по крайней мере), что помешало бы вашему приложению работать на 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, потому что столбец, имя которого изменилось, имеет все значения null. Кажется, мне нужно каким-то образом получить значения из этой таблицы в список объектов (хотя я не мог заставить это работать) или тому подобное. Какие-либо предложения? –

+0

@GarrettDanielDeMeyer Правильно, поэтому, когда вы меняете имя свойства столбца/модели, вам нужно сохранить имя старой колонки/модели, потому что в нем есть старые значения. Затем вам нужно будет вытащить значения и переместить их в новое имя столбца. – hvaughan3

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