Efcore: Tools: Support Xamarin Projects (currently requires separate .NET Standard class library)

Created on 29 Nov 2016  路  17Comments  路  Source: dotnet/efcore

Tooling is explicitly blocked at the moment, so we'd need to do some work there to enable migrations.

PM> Add-Migration Test
Startup project 'Phoneword' targets framework 'MonoAndroid'. The Entity Framework Core Package Manager Console Tools don't support this framework.
area-migrations area-tools area-xamarin type-enhancement

Most helpful comment

You can get around this issue by opening the generated migration file and replace the migrationBuilder.RenameColumn command with the following:
`
//Change table name so we can copy data to updated schema
migrationBuilder.RenameTable(name: "MyTableWithColumnNameToChange", newName: "MyTableWithColumnNameToChangeTempName");

//Recreate table with desired new column.
//I would recommend getting this from the migration script that created the table originally.
//Then just rename the offending column.
migrationBuilder.CreateTable(
name: "MyTableWithColumnNameToChange",
columns: table => new
{
Id = table.Column(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
CreationTime = table.Column(nullable: false),
CreatorUserId = table.Column(nullable: true),
LastModificationTime = table.Column(nullable: true),
MyTableColumn1 = table.Column(nullable: true),
MyTableColumn2 = table.Column(nullable: false),
MyTableColumn3 = table.Column(nullable: true),
MyTableColumn4 = table.Column(nullable: true),
MyTableColumn5 = table.Column(nullable: true),
ThisIsMyRenamedColumn = table.Column(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_MyTableWithColumnNameToChange", x => x.Id);
table.ForeignKey(
name: "FK_MyTableWithColumnNameToChange_SomeOtherTable_KeyedColumn",
column: x => x.KeyedColumn,
principalTable: "SomeOtherTable",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_MyTableWithColumnNameToChange_SomeOtherTable_KeyedColumn",
column: x => x.KeyedColumn,
principalTable: "SomeOtherTable",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
//Move data from old table we renamed above to new table with renamed column/
migrationBuilder.Sql("INSERT INTO MyTableWithColumnNameToChange SELECT Id, CreationTime, CreatorUserId, LastModificationTime, MyTableColumn1 , MyTableColumn2, MyTableColumn3, MyTableColumn4, MyTableColumn5, ThisIsMyOldColumnName FROM MyTableWithColumnNameToChangeTempName;");

//now drop the old table with the wrong name
migrationBuilder.DropTable(name: "MyTableWithColumnNameToChangeTempName");

`

Granted this is long and kinda annoying, but it's what you would have to do anyways if you were using a sqlite db running on a version prior to 3.25.0. Something to note is that foreign keys and indexes will also need to be taken into account when doing this process.

All 17 comments

Just an FYI: I've been able to get tooling to work by downgrading the version in NuGet to 1.1.0-msbuild3-final, and then immediately updating it back to 1.1.0-preview4-final. Then rerun Add-Migration and gens the migration within a Xamarin Forms netstandard project.

Anyone knows any alternative solution to this problem?

@raky291 You should be able to create a .NET Standard Class Library to put your EF Core code in and use the tools.

Hi @bricelam thank you

To use the EntityFrameworkCore.Tools with the .NET Standard, I needed to add a console app that references my library, and set it as the startup project.

screenshot

Correct. You can also just use the class library if you edit the *.csproj to cross-target .NET Core:

<TargetFrameworks>netcoreapp2.0;netstandard2.0</TargetFrameworks>

@bricelam not true. if you set the multitargeting, the other projects (or at least the UWP, i don't have the others set to build currently) give an error
The project 'X' cannot be referenced. The referenced project is targeted to a different framework family (.NETCoreApp)

Correct. We stopped recommending this due to poor cross-targeting support in other project types like UWP.

Is this ever going to be worked on/fixed? the official workaround of having another project where you do migrations and then copy migrations to your project isn't very clean and i've faced issues because of it(admittedly due to my own mistakes in not copying migrations/models from .net console to xamarin app)

Guys, what is going on with this issue ?

@plamenkoyovchev This issue is in the Backlog milestone. This means that it is not going to happen for the 3.0 release. We will re-assess the backlog following the 3.0 release and consider this item at that time. However, keep in mind that there are many other high priority features with which it will be competing for resources.

Actually I was able to make it work after long research over the internet.
Currently, the easiest way to create migrations for Xamarin.Forms is:

  1. To open *.csproj file and edit聽
    <TargetFramework>netstandard2.0</TargetFramework>聽
    to聽
    <TargetFrameworks>netcoreapp2.0;netstandard2.0</TargetFrameworks>
    clean all projects and rebuild them separately聽
  2. Open Package Manager Console and navigate to the shared project folder then set startup project to be the shared project
    and run command: Add-Migration MigrationName
    聽 聽 - the migration files will be generated in Migrations folder
  3. In *.csproj file turn back the TargetFramework to reference only netstandard2.0 and don't forget to remove 's' from the tag: should be since it's referencing single framework

But now I have another problem for Xamarin.iOS the lastest SQLite version in iOS 12.2 is 3.24.0 which does not support Column Renaming. SQLite has a lot of limitaions and the miss of column renaming make it useless. It is supported in 3.26.0 but I am still not able to find how to force my Xamarin app to use dll of sqlite version 3.26.0

You can get around this issue by opening the generated migration file and replace the migrationBuilder.RenameColumn command with the following:
`
//Change table name so we can copy data to updated schema
migrationBuilder.RenameTable(name: "MyTableWithColumnNameToChange", newName: "MyTableWithColumnNameToChangeTempName");

//Recreate table with desired new column.
//I would recommend getting this from the migration script that created the table originally.
//Then just rename the offending column.
migrationBuilder.CreateTable(
name: "MyTableWithColumnNameToChange",
columns: table => new
{
Id = table.Column(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
CreationTime = table.Column(nullable: false),
CreatorUserId = table.Column(nullable: true),
LastModificationTime = table.Column(nullable: true),
MyTableColumn1 = table.Column(nullable: true),
MyTableColumn2 = table.Column(nullable: false),
MyTableColumn3 = table.Column(nullable: true),
MyTableColumn4 = table.Column(nullable: true),
MyTableColumn5 = table.Column(nullable: true),
ThisIsMyRenamedColumn = table.Column(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_MyTableWithColumnNameToChange", x => x.Id);
table.ForeignKey(
name: "FK_MyTableWithColumnNameToChange_SomeOtherTable_KeyedColumn",
column: x => x.KeyedColumn,
principalTable: "SomeOtherTable",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_MyTableWithColumnNameToChange_SomeOtherTable_KeyedColumn",
column: x => x.KeyedColumn,
principalTable: "SomeOtherTable",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
//Move data from old table we renamed above to new table with renamed column/
migrationBuilder.Sql("INSERT INTO MyTableWithColumnNameToChange SELECT Id, CreationTime, CreatorUserId, LastModificationTime, MyTableColumn1 , MyTableColumn2, MyTableColumn3, MyTableColumn4, MyTableColumn5, ThisIsMyOldColumnName FROM MyTableWithColumnNameToChangeTempName;");

//now drop the old table with the wrong name
migrationBuilder.DropTable(name: "MyTableWithColumnNameToChangeTempName");

`

Granted this is long and kinda annoying, but it's what you would have to do anyways if you were using a sqlite db running on a version prior to 3.25.0. Something to note is that foreign keys and indexes will also need to be taken into account when doing this process.

@andrewndavis Thanks for this. I've successfully forced iOS to not use its embedded SQLite by doing this: (https://forums.xamarin.com/discussion/153353/how-to-use-sqlite-3-26-0-instead-of-3-24-0-in-my-xamarin-forms-ios-project/)

Wondering if this will be addressed directly any time soon?

@mzhukovs This issue is in the Backlog milestone. This means that it is not going to happen for the 3.0 release. We will re-assess the backlog following the 3.0 release and consider this item at that time. However, keep in mind that there are many other high priority features with which it will be competing for resources.

So with a relatively new release of 3.1, I see this issue was going to be re-evaluated after the 3.0 release, is there hope that this will make it into the 5.0 release this November? From the general overview of the 5.0 release, there's a note under Migrations stating

The result is likely to be many small improvements in EF Core (for example, better Migrations on SQLite)

Can we take this to be a resolution to this issue? To be honest, current workarounds for Xamarin.Forms have proved to be stable and quite easy to implement, but official support is always warmly welcomed.

I jotted down some ideas for enabling this in https://github.com/dotnet/efcore/issues/18840

Was this page helpful?
0 / 5 - 0 ratings