Xamarin Entity Framework Migration - Table 'name' already exists - c#

So, I have a Xamarin app with a local DB, and I'm using the command this.Database.Migrate() to apply any pending migration, it works fine at first, but the problem is, when I uninstall the app and install again, the app try to execute the same pending migration, and I got the error "Table 'name' already exists". Is there a way to ignore tables that already exists 'cause I don't want to delete the users local data every time they uninstall the app.
I'm using the command dotnet ef migrations add initial to create migrations.

To everyone facing this problem, I solved creating a method using SQLiteConnection. You need to add using SQLite; :
public static bool TableExists(string tableName)
{
var dbPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), databaseName);
using (var db = new SQLiteConnection(dbPath))
{
var info = db.GetTableInfo(tableName);
if (info.Any())
{
return true;
}
}
return false;
}

Related

How to read External Sqllite Database in xamirin android in Visual Studio C#

My Code :
var dbpath = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), "ot.db3");
Context myContext = null;
try
{
var dbcon = new SQLiteConnection(dbpath);
var db= dbcon.Query<records>("SELECT * FROM records WHERE sno = ? ", "1");
int count = db.Count;
}
catch (IOException ex)
{
var reason = string.Format("The database failed to create - reason {0}", ex.Message);
Toast.MakeText(myContext, reason, ToastLength.Long).Show();
}
I Create a Sqlite Database from my SQL Server Database.
Now I save it to my Phone on this path (Android/Data/Application/File)
Now using Sqlite-net-pcl nugget package for Sqlite Connection the connection works fine showing have no error.
When I try to read a table from database this Give any error that "No Such Table exist in database". And the table exists in the database and is populated with data.
What can I do?
Thanks in advance
Why are you writing the SQL query? You could make it more simple, something like, conn.Get(id);
You will only need to add [PrimaryKey] to your PK in the model.
Also, I suggest you to not use SQLite.SQLiteConnection and use SQLiteAsyncConnection instead, so you will be able to get data using "await conn.GetAsync(id)" and this way it won't block the main thread.
Create a Blank Database – A database reference can be created by passing the file path the SQLiteConnection class constructor. You do not need to check if the file already exists – it will automatically be created if required, otherwise the existing database file will be opened.
var db = new SQLiteConnection (dbPath);
Save Data – Once you have created a SQLiteConnection object, database commands are executed by calling its methods, such as CreateTable and Insert like this:
db.CreateTable<Stock> ();
db.Insert (newStock); // after creating the newStock object
Retrieve Data – To retrieve an object (or a list of objects) use the following syntax:
var stock = db.Get<Stock>(5); // primary key id of 5
var stockList = db.Table<Stock>();
For more info use following link:
https://developer.xamarin.com/guides/android/application_fundamentals/data/part_3_using_sqlite_orm/#Using_SQLite.NET
You need to check in your DB file if the table really exists.
Extract the database from your Android device/simulator with ADB
Example: adb pull //.db .
It will download the DB file to your computer.
Then, use a tool such as "DB Browser for SQLite" (http://sqlitebrowser.org/) to open your DB file and check if your table exists.
Other common mistakes when using SQLite with Android are:
the file is not found (the path in your connectionstring is not good)
the name is not the one your think (maybe in your case, it is "Record" and not "Records")
Hope it will help.

EntityFramework is deleting my records on dbcontext creation

In a .NET Core project I have the following model:
public class WrapperContext : DbContext
{
public DbSet<WrappedFunction> Functions { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder builder)
{
builder.UseSqlite("Data Source=functions.db");
}
}
public class WrappedFunction
{
[Key]
public int FunctionId { get; set; }
public String FunctionName { get; set; }
public String AssemblyPath { get; set; }
public String FactoryName { get; set; }
}
As you can see it is an Sqlite database.
I have added the initial migration and updated the database. Before running my test application I have populated the Functions table with a single record. I can open the DB with SqliteManager to see the record is there.
I then try to access the DB with the following function:
static MUinfo getInfoFor(String command)
{
UFInfo rv = null;
using (var ctx = new WrapperContext()) // <---- record disappears here
{
foreach (var fn in ctx.Functions)
{
if (fn.FunctionName.Equals(command))
{
rv = new UFInfo()
{
AssemblyPath = fn.AssemblyPath,
FactoryName = fn.FactoryName,
CommandName = command
};
}
}
}
if (rv != null) return rv;
return "No Info for " + command;
}
However, when stepping through the debugger I see that the foreach loop never executes as there are no records in the DB. When I stop before executing the foreach I can verify (using SqliteManager) that my single record has been deleted from the database.
So, it would appear that the DbContext object is somehow deleting the data from the database. Why would that be and what have I done wrong here? I am fairly new to EntityFramework so I may have done something obvious, but it isn't obvious to me.
One additional bit of info here. The project that does the db access is a library project. I have to copy the DB over to the build directory of the application before populating it and running the application. I don't know if that makes a difference or not.
Edit 6/19/17 (18:51):
OK. A bit more information (thanks to #StevePy). Nunit3 seems to conflict with EFCore (though I don't get any errors on restore) so I couldn't create a unit test for this. So I inserted a using (var ctx...) above the one in the listing above and inserted a couple of dummy records. Then I exited that using block and when I entered the one to traverse the records they were there.
However, I then commented out the dummy insertions and reran my test. And, once again, both records disappeared. So there is something very weird going on here with EFCore.
Edit 6/20/17 (15:30):
Well, I'm still not sure what is going on but there is an odd interaction between EFCore and Microsoft.Data.Sqlite.
I decided to remove all references to EFCore and simply use SQL queries on the DB. I did this without recreating the DB (so I was using the one already created by EFCore). I noticed the exact same behavior when I created a new SqliteConnection without using EFCore.
In desperation I deleted the DB and recreated it from scratch using Microsoft.Data.Sqlite. This DB didn't have any of the EFCore information in it, obviously. I then populated the DB with some test records and went to use it. No more disappearing records.
Apparently there was something very strange in the way EFCore set up the database in the first place that caused the issue. But I don't have any idea what it was.
What is likely happening is that you are using CodeFirst /w EF, but taking a DB first approach when it comes to testing your new code. EF tracks schema changes within the database and my guess is that by you creating the table ahead of time it does not know that the schema is vetted, so it drops and recreates it on context start-up.
The fix here should be to create a "stub" test that populates a test record one time using your EF model. From there you should have a table that EF recognizes against that context and accepts. From there you can create test records in whatever editor for testing purposes.
SQLite has several limitations when it comes to schema migration that you probably will want to consider as well. (see: https://learn.microsoft.com/en-us/ef/core/providers/sqlite/limitations)
You might be better off setting up DB first, telling EF how to map to an existing SQLite schema rather than trying to use Code-First as migration is pretty limited for that DB engine.

Entity Framework apply migrations at runtime

i'm creating a C# WinForms application which uses Entity Framework Code First and it is set to create the database if it doesn't exists.
Since the app is not distributed with a database, it creates it when it's needed, so i need to find a way to detect which migrations need to be applied for each case when i release a new version of the app.
How can i detect and apply needed migrations at runtime?
try this Initializer:System.Data.Entity.MigrateDatabaseToLatestVersion,it will update your database(no delete db,no delete data),just update entity changed.
Database.SetInitializer(new MigrateDatabaseToLatestVersion<T, DbMigrationsConfiguration<T>>());
try
{
using (var ctx = new T())
{
ctx.Database.Initialize(true);
}
}
catch (Exception e)
{
}

How to describe details of an automatic migration

In Entity Framework Code-First you can enable automatic migrations based on code changes in the model.
By calling Update-Database, these changes are:
Registered in a [timestamp]_AutomaticMigration
Generate SQL scripts and migrate database
Store the migration in the table __MigrationHistory
In the __MigrationHistory table, the migration is described in the Model column as a large hex value. eg:
0x1F8B0800000000000400CD57CD6EDB3810BE2FB0EF20F0B40512313F976D20B5C8CAC9C2D83A09AAB4775A1ADBC492944A5281FD6C3DF491FA0A1D...
In the Package Manager Console within Visual Studio, you can retrieve a list of automatic migrations with Get-Migrations.
However, there doesn't seem to be a way to retrieve the details of a particular automatic migration -- whether that's SQL script, or which model & fields are being affected.
Is there a way that I'm unaware of to retrieve details of a particular automatic migration?
Yes, there is.
This value in table __MigrationHistory, is a GZiped Xml, that contains the EDMX schema.
I made this following code to retrieve the EDMX model (this code uses C# 6)
You can access this code on my gist also, with this link: https://gist.github.com/AlbertoMonteiro/45198dc80641ce1896e6
In this gist there is a C# 5 version, that you can paste in a console application.
#r "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.Data.dll"
#r "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.Xml.Linq.dll"
using System.Xml.Linq;
using System.IO.Compression;
public void DecompressDatabaseMigration(string migrationName, string databaseName)
{
var connectionString = $#"Data source=.\sqlexpress;Initial catalog={databaseName};Integrated security=true";
var sqlToExecute = String.Format("select model from __MigrationHistory where migrationId like '%{0}'", migrationName);
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
var command = new SqlCommand(sqlToExecute, connection);
var reader = command.ExecuteReader();
if (!reader.HasRows)
{
throw new Exception("Now Rows to display. Probably migration name is incorrect");
}
while (reader.Read())
{
var model = (byte[])reader["model"];
var decompressed = Decompress(model);
File.WriteAllText(#"C:\temp\edmx.xml", decompressed.ToString());
}
}
}
public XDocument Decompress(byte[] bytes)
{
using (var memoryStream = new MemoryStream(bytes))
{
using (var gzipStream = new GZipStream(memoryStream, CompressionMode.Decompress))
{
return XDocument.Load(gzipStream);
}
}
}
DecompressDatabaseMigration(Args[1], Args[0]);
As you can see, there is some syntax that is allowed in the new C# REPL that arrived with Visual Studio Update 1.
So I save this file with .csx extention and then I execute, in my case DecompileMigration.csx
csi DecompileMigration.csx
Then the script will put in my C:\temp folder the file emdx.xml and then I see the model in this particular migration.
To use the REPL of VS 2015 update 1, you can
Load the customized CMD called Developer Command Prompt for VS2015
Press Windows and then search for this name Developer Command Prompt for VS2015
Option 1
Open this in C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Visual Studio 2015\Visual Studio Tools
Option 2
Open a cmd session, and then execute this following command
call "%vs140comntools%vsdevcmd.bat"
Option 3
Open it in Visual Studio 2015 U1 in the menu VIEW > Other Windows > C# Interactive
If you use this option, the Args member in the script will not work, so you must replace the Args[0] for your database name, and Args[0] for your database name.
This question came from a misunderstanding between automatic migrations vs. explicit migrations.
I considered an explicit migration would require you to write all up/down changes.
Add-Migration with migration changes that would otherwise be picked up by an automatic migration are placed automatically into the explicit migration file.
Calling Update-Database after the explicit migration will migrate the changes, but now with a code file detailing the changes.

Drop a database using entity framework

I'm trying to remove a database form my application using entity framework.
The code I use is the following:
using (var dbContext = container.Resolve<ApplicationDbContext>())
{
dbContext.Database.Delete();
}
According to msdn this should work but nothing happens.
the dbContext is registered using ContainerControlledLifetimeManager and should be the same instance used to create the DB.
Adding, updating and deleting instances of entity types needs dbContext.SaveChanges() to reflect changes.
However dbContext.Database.Delete() does not need dbContext.SaveChanges().
If you open connection for example from Sql Management Studio to your database and try to dbContext.Database.Delete() then you receive Cannot drop database "dbContext" because it is currently in use. If you restart you sql server, you drop those connections and then retry dbContext.Database.Delete() you successfully drop database.
Last thing is refresh database list in Sql Management Studio in order to see that database is not there any more.
Testing with this code snippet:
using (var dbContext = new dbContext())
{
dbContext.Database.Delete();
}
After #moguzalp and this (MSDN Database.Delete Function), I came with a solution for my case:
using System.Data.Entity;
Database.Delete("connectionStringOrName");
In my case I was trying to Recreate a mssqllocalDb database for test purposes. But whenever I used the same DbContext (or an immediately new one, disposing the first and opening another), it looked like the database was still up when I tried to create it.
Bad Example: (x)
public static void RecreateDatabase(string schemaScript)
{
using (DbContext context = new DbContext("connectionStringName"))
{
context.Database.Delete(); // Delete returns true, but...
Database database = context.Database;
database.Create(); // Database already exists!
database.ExecuteSqlCommand(schemaScript);
}
}
Working example: (/)
public static void RecreateDatabase(string schemaScript)
{
Database.Delete(); // Opens and disposes its own connection
using (DbContext context = new DbContext("connectionStringName")) // New connection
{
Database database = context.Database;
database.Create(); // Works!
database.ExecuteSqlCommand(schemaScript);
}
}
Context: I'm using this on an [AssemblyInitialize] function to test my ModelContexts
All of your changes occurred on local variable dbcontext.
That is final kick > add dbContext.SaveChanges(); at the end of your using block like so:
using (var dbContext = container.Resolve<ApplicationDbContext>())
{
dbContext.Database.Delete();
dbContext.SaveChanges();
}

Categories

Resources