locked
Xamarin, Android, Entity Framework Core 2 and Migrations RRS feed

  • Question

  • User303977 posted

    Hi,

    I've been trying to get EF Core and Migrations to work with Xamarin Android. I found this blog post by Jon Douglas.

    Fortunately EF Core and Visual Studio 2017 both have been upgraded in the mean time. But this makes it unfortunate that I can't replicate what has been written in the blog. (Different project templates, NuGet packages not compatible etc.)

    Is there anyone here who got EF Core 2.0 to work with Xamarin Android and Visual Studio 2017 version 15.3? If so, can you please tell us step by step how this can be done?

    Thanks a lot!

    Rob

    Thursday, August 24, 2017 6:07 PM

All replies

  • User17265 posted

    The whole NetCore/Standard build process is a gigantic mess and an even bigger waste of time. There are 2 different types of .NetStandard projects (project.json and 2017 csproj) and they aren't even compatible with each other. PCL cant reference the new 2017 projects, which means no EF Core 2.0, but it can reference the old project.json variant. In short, dont bother until Xamarin finally plays catch up and Visual Studio settles down from one buggy release to the next. Maybe next year?

    Thursday, August 31, 2017 2:37 PM
  • User303977 posted

    Hmmm, that doesn't sound to promising. But good to know I can keep focusing on the "old" stuff.

    Thanks!

    Friday, September 1, 2017 9:59 AM
  • User17265 posted

    They are now suggesting people use the betas and alphas of VS and Xamarin to get it to work (link), but given the problems we all have with the official releases, that is just asking for trouble. I've wasted enough time on it and will revisit in a few months.

    Monday, September 4, 2017 11:50 AM
  • User39542 posted

    Hi @Robert_VdV --

    Yes, it's tricky. The key is to have the correct references with the correct tags in the Migrations project. Here's what works:

    1. Create a new Migrations .NET Core Console app. You won't actually run this app, but it's a host project.
    2. Open the csproj file and add the following references. I'm including my entire csproj file here for clarity, but you need all the PackageReference items and in particular the DotNetCliToolReference which is where the ef command lives.

    ```xml

    Exe netcoreapp2.0

    ```

    1. Next, copy your DbContext class over to the Console app. Keep it in the namespace from your project, and make sure you have the DTO defined as well. You have to have the DbSet<T> defined in it. Finally, remove any code from OnConfiguring except for the UseSqlite extension method call. You can modify the call to compile (e.g. if you are using an abstraction to the get the filename .. just make it a string like "Data Source=blah.db").
    2. Open a CMD.exe prompt in the project folder where the csproj is for your Migrations project and execute the following lines:

    C:> dotnet restore ... C:> dotnet ef migrations add InitialCreate

    It should do some stuff and it will create a new Migrations folder with a few C# source files in it. These are added to your Migrations csproj automatically.

    1. Finally, switch back to VS and drag the Migrations folder into your data assembly where your DbContext class is. Open each source file and fixup the namespaces to be part of your project and then compile it.
    2. Add a call to Database.Migrate(); in your DbContext constructor. This will ensure it runs the migrations.

    Now you should be able to run your app and it will correctly create the DB and let you do EF.Core things to it.

    I hope that helps! Mark Smith Xamarin University

    P.S. We will have a free Lightning Lecture on Xamarin University that walks you through the whole EF Core scenario with Xamarin soon so watch for that!

    Friday, September 8, 2017 7:01 PM
  • User303977 posted

    Hi @MarkSmith.8123,

    Thanks a lot for your instructions. Hopefully I can find some time this weekend to have a play with it. And thank you for the p.s. about free Lightning Lecture. I'll keep an I on that.

    Rob

    Tuesday, September 12, 2017 1:01 PM
  • User117295 posted

    @"MarkSmith.8123"

    I followed the steps you have given but I'm getting following error in my command window when I try to add migration

    D:\>dotnet ef migrations add Initial -v error MSB4062: The "Xamarin.Forms.Build.Tasks.GetTasksAbi" task could not be loaded from the assembly C:\Users\.nuget\packages\xamarin.forms\2.5.0.280555\build\netstandard1.0\Xamarin.Forms.Build.Tasks.dll. Could not load file or assembly 'Microsoft.Build.Utilities.v4.0, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. The system cannot find the file specified. Confirm that the declaration is correct, that the assembly and all its dependencies are available, and that the task contains a public class that implements Microsoft.Build.Framework.ITask.

    Build FAILED.

    C:\Users\.nuget\packages\xamarin.forms\2.5.0.280555\build\netstandard1.0\Xamarin.Forms.targets(55,3): error MSB4062: The "Xamarin.Forms.Build.Tasks.GetTasksAbi" task could not be loaded from the assembly C:\Users\.nuget\packages\xamarin.forms\2.5.0.280555\build\netstandard1.0\Xamarin.Forms.Build.Tasks.dll. Could not load file or assembly 'Microsoft.Build.Utilities.v4.0, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. The system cannot find the file specified. Confirm that the declaration is correct, that the assembly and all its dependencies are available, and that the task contains a public class that implements Microsoft.Build.Framework.ITask. 0 Warning(s) 1 Error(s)

    Time Elapsed 00:00:01.04 Microsoft.EntityFrameworkCore.Tools.CommandException: Build failed. at Microsoft.EntityFrameworkCore.Tools.Project.Build() at Microsoft.EntityFrameworkCore.Tools.RootCommand.Execute() at Microsoft.DotNet.Cli.CommandLine.CommandLineApplication.Execute(String[] args) at Microsoft.EntityFrameworkCore.Tools.Program.Main(String[] args) Build failed.

    Any idea how I can fix this error? I'm using .net Standard 2.0 with Visual studio 2017 (15.5.6)

    Thanks

    Tuesday, March 27, 2018 6:30 PM
  • User39542 posted

    I had to create a Console app to generate the initial schema - I don't think you can use a reference, instead you have to copy the model classes into the other project manually. It's not ideal, but it worked for me.

    Tuesday, March 27, 2018 7:59 PM
  • User117295 posted

    @"MarkSmith.8123"

    So you are saying that I should create another project which will have all my model classes and I reference this project to my .net standard project? If so how subsequent migrations (subsequent changes in model classes as n when it happens) will work?

    Wednesday, March 28, 2018 4:17 AM
  • User39542 posted

    The other way around. The build task you are trying to run doesn't exist in the .NET Standard world yet. You need to run the Powershell command on a project which targets a real platform (ideally desktop .NET). Here's what I did that worked for me:

    1. Create a .NET Console app.
    2. Copy my model classes (yes, I copied them) into the app.
    3. Run the Powershell command on the .NET Console app.

    This generates the correct DB creation class for you. Then, copy that file back to your .NET Standard project and you can throw away the .NET Console app. It's only needed as a host for the Powershell command to be able to actually run the proper task. If you have to create migrations, you'll have to repeat the above steps.

    There might be a better way to get it to generate today - the above was done last September, but it's the only way I found that worked correctly. Very likely, at some point this will just be a supported scenario for mobile projects.

    Best, mark

    Wednesday, March 28, 2018 1:38 PM
  • User337047 posted

    Hi,

    I can not find any examples that will do a second migration that will, for example, create a new column then migrate data from some other columns to the new column. Here is how I attempted this. I create another migration after the initial create to add the column "NameAndFilePath". I wanted to test updating the column for existing data rows with data from the "Name" and "FilePath" columns by updating the Up method and adding some SQL:

        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.AddColumn<string>(
                name: "NameAndFilePath",
                table: "Documents",
                nullable: true);
    
            migrationBuilder.Sql(
                @"
                 UPDATE Documents
                 SET NameAndFilePath = Name + ' ' + FilePath;
                ");
        }
    

    I followed the same convention here by running the add migrations for the new migrations in the .NETCore Console app, then copied over the scripts to my Xamarin project then adding the update statement. The Column was added and I have a new entry in the __EFMigrationsHistory table in the SQLite database, but the update SQL did not seem to run and the column NameAndFilePath for the existing rows were populated with "0" instead of the data from the Name and FilePath columns. I came up with the idea from the Microsoft online EF documentation on how to do the data update.

    Obviously, running a "dotnet ef database update" will not work, because the sqlite file is not local and the project is a separate console app, and not the Xamarin project and it can't connect to the device (mac) and update the SQL. I am doing this using Windows Visual Studio 2017.

    Any feedback would be appreciated...

    Tuesday, April 10, 2018 8:30 PM
  • User337047 posted

    The problem ended up being my sql syntax for SQL lite.. The update statement should be:

             UPDATE Documents
             SET NameAndFilePath = Name || ' ' || FilePath
    

    After the corrections this works

    Wednesday, April 11, 2018 1:46 PM
  • User377322 posted

    I took your post and took it a few steps further and now have a working solution for having migrations work with Xamarin and EF Core 2.0. This is a bit of a hack, and you need to hard code a path to a local sqlite db for the migrations to apply to, but it works!

    Here is a link to my solution on GitHub /ngrumbine/EFDemo

    Quick and dirty explanation...

    I added the following Nuget Packages to my .NetStandard Dll for my Xamarin Forms app. * Microsoft.Data.Sqlite.Core * Microsoft.EntityFrameworkCore * Microsoft.EntityFrameworkCore.Design * Microsoft.EntityFrameworkCore.Sqlite * Microsoft.EntityFrameworkCore.Tools

    Like Mark said, I added a .Net Core Console Application... DbConfig

    Then to make it all work i added the following class to my EFDemo poject that allows the migrations to run!

    namespace EFDemo.Database
    {
        public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory<DatabaseContext>
        {
            public DatabaseContext CreateDbContext(string[] args)
            {
                Debug.WriteLine(Directory.GetCurrentDirectory() + @"\Config.db");
    
                return new DatabaseContext(Directory.GetCurrentDirectory() + @"\Config.db");
            }
        }
    }
    

    My DatabaseContext looks like:

    namespace EFDemo.Database
    {
        public class DatabaseContext : DbContext
        {
            private string _databasePath;
    
            public DatabaseContext(string databasePath)
            {
                _databasePath = databasePath;
                this.Database.Migrate();
            }
    
            protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
            {
                optionsBuilder.UseSqlite($"Filename={_databasePath}");
            }
    
            protected override void OnModelCreating(ModelBuilder modelBuilder)
            {
                base.OnModelCreating(modelBuilder);
    
            }
    
            public virtual DbSet<Models.Department> Departments { get; set; }
            public virtual DbSet<Models.Personnel> Personnel { get; set; }
        }
    }
    

    In the package manager console then you can run....

    ! add-migration "Initial" -Project EFDemo -StartupProject DbConfig

    and you can keep running add-migration and it will generate and auto migrate your db!

    The kicker is you create a config db in you console app, which i keep included in my solution and check in with my code so it can generate the migrations.

    Hope this helps. It took me all day yesterday to come up with this solution but its been working so far for me with out any issues!

    Thursday, November 8, 2018 5:36 PM
  • User377322 posted

    @"MarkSmith.8123" said: The other way around. The build task you are trying to run doesn't exist in the .NET Standard world yet. You need to run the Powershell command on a project which targets a real platform (ideally desktop .NET). Here's what I did that worked for me:

    1. Create a .NET Console app.
    2. Copy my model classes (yes, I copied them) into the app.
    3. Run the Powershell command on the .NET Console app.

    This generates the correct DB creation class for you. Then, copy that file back to your .NET Standard project and you can throw away the .NET Console app. It's only needed as a host for the Powershell command to be able to actually run the proper task. If you have to create migrations, you'll have to repeat the above steps.

    There might be a better way to get it to generate today - the above was done last September, but it's the only way I found that worked correctly. Very likely, at some point this will just be a supported scenario for mobile projects.

    Best, mark

    Using your ideaof the cosole app, i took it a little bit further and now have the migrations working with so hard work yesterday... my solution is on Github ... /ngrumbine/EFDemo

    Added comment below with info from my code

    Thursday, November 8, 2018 5:49 PM
  • User377322 posted

    @NateGrumbine said:

    @"MarkSmith.8123" said: The other way around. The build task you are trying to run doesn't exist in the .NET Standard world yet. You need to run the Powershell command on a project which targets a real platform (ideally desktop .NET). Here's what I did that worked for me:

    1. Create a .NET Console app.
    2. Copy my model classes (yes, I copied them) into the app.
    3. Run the Powershell command on the .NET Console app.

    This generates the correct DB creation class for you. Then, copy that file back to your .NET Standard project and you can throw away the .NET Console app. It's only needed as a host for the Powershell command to be able to actually run the proper task. If you have to create migrations, you'll have to repeat the above steps.

    There might be a better way to get it to generate today - the above was done last September, but it's the only way I found that worked correctly. Very likely, at some point this will just be a supported scenario for mobile projects.

    Best, mark

    Using your ideaof the cosole app, i took it a little bit further and now have the migrations working with so hard work yesterday... my solution is on Github ... /ngrumbine/EFDemo

    @MarkSmith.8123

    EFDemo Demo of how to integrate EF Core 2.0 into Xamarin Forms Cross Platform

    Quick and dirty explanation...

    I added the following Nuget Packages to my .NetStandard Dll for my Xamarin Forms app.

    Microsoft.Data.Sqlite.Core Microsoft.EntityFrameworkCore Microsoft.EntityFrameworkCore.Design Microsoft.EntityFrameworkCore.Sqlite Microsoft.EntityFrameworkCore.Tools Like Mark said, I added a .Net Core Console Application... DbConfig

    Then to make it all work i added the following class to my EFDemo poject that allows the migrations to run!

      namespace EFDemo.Database
      {
          public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory<DatabaseContext>
          {
              public DatabaseContext CreateDbContext(string[] args)
              {
                  Debug.WriteLine(Directory.GetCurrentDirectory() + @"\Config.db");
    
                  return new DatabaseContext(Directory.GetCurrentDirectory() + @"\Config.db");
              }
          }
      }
    

    My DatabaseContext looks like:

      namespace EFDemo.Database
      {
          public class DatabaseContext : DbContext
          {
              private string _databasePath;
    
              public DatabaseContext(string databasePath)
              {
                  _databasePath = databasePath;
                  this.Database.Migrate();
              }
    
              protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
              {
                  optionsBuilder.UseSqlite($"Filename={_databasePath}");
              }
    
              protected override void OnModelCreating(ModelBuilder modelBuilder)
              {
                  base.OnModelCreating(modelBuilder);
    
              }
    
              public virtual DbSet<Models.Department> Departments { get; set; }
              public virtual DbSet<Models.Personnel> Personnel { get; set; }
          }
      }
    

    In the package manager console then you can run....

    add-migration "Initial" -Project EFDemo -StartupProject DbConfig

    and you can keep running add-migration and it will generate and auto migrate your db!

    The kicker is you create a config db in you console app, which i keep included in my solution and check in with my code so it can generate the migrations.

    Thursday, November 8, 2018 6:50 PM
  • User195014 posted

    It works! Thanks guys!!!

    Thursday, February 7, 2019 4:28 PM
  • User384243 posted

    @NateGrumbine Great work. I was thinking there has to be a way to do this with copy back and forth. Thank you soo much for this!!!!

    Tuesday, April 23, 2019 2:16 PM
  • User384243 posted

    Also thank you Rob for starting this in the first place

    Tuesday, April 23, 2019 3:52 PM