Xamarin, Android, Entity Framework Core 2 and Migrations

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

Posts

  • 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?

  • Robert_VdVRobert_VdV USMember ✭✭

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

    Thanks!

  • StewartCunninghamStewartCunningham USMember
    edited September 2017

    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.

  • Robert_VdVRobert_VdV USMember ✭✭

    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

  • KrunalKrunal USMember ✭✭

    @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

  • MarkSmith.8123MarkSmith.8123 USXamarin Team, University, XamUProfessors Xamurai

    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.

  • KrunalKrunal USMember ✭✭

    @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?

  • MarkSmith.8123MarkSmith.8123 USXamarin Team, University, XamUProfessors Xamurai

    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

  • BrianHagerBrianHager USMember

    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...

  • BrianHagerBrianHager USMember

    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

  • NateGrumbineNateGrumbine Member ✭✭

    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!

  • NateGrumbineNateGrumbine Member ✭✭
    edited November 2018

    @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

  • NateGrumbineNateGrumbine Member ✭✭
    edited November 2018

    @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.

  • LucaPanellaLucaPanella USMember ✭✭

    It works! Thanks guys!!!

  • GreekTreatGreekTreat Member ✭✭

    @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!!!!

  • GreekTreatGreekTreat Member ✭✭

    Also thank you Rob for starting this in the first place

Sign In or Register to comment.