EntityFramework.Migrations, using DbMigrator in a C# class - c#

I just installed the new EntityFramework.Migrations package. I scaffoled my migrations following this tutorial: http://blogs.msdn.com/b/adonet/archive/2011/09/21/code-first-migrations-alpha-3-no-magic-walkthrough.aspx
Using the Powershell window, everything works fine.
But we need to create a class that will rollback all the migrations for our automated tests.
So I made a simple class that looks like this:
public class CustomMigrator
{
public void DropDatabase()
{
new DbMigrator(new Settings()).Update("0");
}
public void RegenerateDatabase()
{
new DbMigrator(new Settings()).Update();
}
}
Settings is my DbMigrationContext implementation that looks like this:
public class Settings : DbMigrationContext<MyDb>
{
public Settings()
{
AutomaticMigrationsEnabled = false;
SetCodeGenerator<CSharpMigrationCodeGenerator>();
AddSqlGenerator<SqlConnection, SqlServerMigrationSqlGenerator>();
}
}
When I call this:
new CustomMigrator().DropDatabase();
I get a weird exception:
The given assembly name or codebase was invalid. (Exception from HRESULT: 0x80131047)
I know that Migrations are still in alpha, but I was wondering if anyone have been able to run the migrations using DbMigrator?
Thanks.

I just found my problem, it is because I was using EntityFrameworkProfiler and there is a bug with the latest EF release that breaks the profiler.
http://blogs.hibernatingrhinos.com/5121/entity-framework-june-2011-ctp-v4-2-is-now-supported-in-entity-framework-profiler
For the moment I did not need the profiler, so I just removed the line of code that was initializing the profiler and now it works.

Related

Adding Akavache Static Linker class or Initializer error

I'm currently using Visual Studio Mac 2019 for to build my iOs Xamarin Forms Application.
My Application Akavache to store persistent data specifically credentials which I utilizes its BlobCache.Secure storage, but sadly the data doesn't persist.
I found that I should add either of the following:
1. Linker Class
using System;
using Akavache.Sqlite3;
namespace NameSpace.iOS
{
[Preserve]
public static class LinkerPreserve
{
static LinkerPreserve()
{
var persistentName = typeof(SQLitePersistentBlobCache).FullName;
var encryptedName = typeof(SQLiteEncryptedBlobCache).FullName;
}
}
public class PreserveAttribute : Attribute
{
}
}
or
2. Initializer
Akavache.Registrations.Start("FollowTheDrop");
Akavache: saved value not available after iOS app restart
but every time I add the solution above the following error below occurs during the build
MTOUCH : error MT2101: Can't resolve the reference 'System.Int32
SQLitePCL.raw::sqlite3_bind_blob(SQLitePCL.sqlite3_stmt,System.Int32,System.Byte[])',
referenced from the method 'System.Void
Akavache.Sqlite3.BulkInsertSqliteOperation/<>c__DisplayClass7_0::b__0()'
in 'SQLitePCLRaw.core, Version=1.1.13.388, Culture=neutral,
PublicKeyToken=1488e028ca7ab535'.
Am I missing something that causes this error?
It was solved by updating the following Nuget Packages below which has dependencies on each other:
Akavache 9.0.1
Splat 14.3.1
fusillade 2.4.47
Lastly when adding the linker static class consider adding a Preserve Attribute as shown below:
[Preserve]
public static class LinkerPreserve
{
static LinkerPreserve()
{
var persistentName = typeof(SQLitePersistentBlobCache).FullName;
var encryptedName = typeof(SQLiteEncryptedBlobCache).FullName;
}
}
public class PreserveAttribute : Attribute
{
}

grab UserSecrets from a NuGet package

I've written a C# class library for my company to use internally, and it uses DotNet UserSecrets to allow each developer to have their own credentials set without needing to worry about accidentally committing them. It worked fine during testing, but after installing it as a NuGet package as opposed to a project dependency, it no longer seems to be able to read from the secrets.json file. I'm wondering if this is a security thing that C# prevents, or if I need to do something else to enable that functionality in an external package.
The package code looks like this:
using Microsoft.Extensions.Configuration;
using TechTalk.Specflow;
namespace Testing.Utilities
{
[Binding]
public class Context
{
private static IConfigurationRoot configuration { get; set; }
private static FeatureContext feature_context;
// SpecFlow attribute runs this before anything else executes
[BeforeFeature(Order = 1)]
private static void SetFeatureContext(FeatureContext context)
{
try
{
configuration = new ConfigurationBuilder()
.AddUserSecrets<Context>()
.Build();
}
catch { }
feature_context = context;
test_context = context.FeatureContainer.Resolve<TestContext>();
}
public static string GetSecretVariable(string name)
{
object v = null;
// if the user secrets were found
if (configuration != null)
{
v = configuration[name];
}
if (v == null)
{
Logger.Warning($"secret variable '{name}' not found");
return null;
}
return v.ToString();
}
}
}
And in the calling code which always gets Null from the getter method:
using Testing.Utilities; // via NuGet package
namespace Testing
{
public static void Main()
{
System.Console.WriteLine($"found {Context.GetSecretVariable("super_secret")}");
}
}
Update:
It works as expected when I drag my locally built .nupkg file into my NuGet package cache and replace the one pulled from the repo. I updated the version number and pushed the change so I know they are on the same version, and it still only worked when I manually inserted my build. Now I'm more confused...
I ported the project from .NET Framework 4.6.1 to .NET 6 and it seemed to fix it. Kinda drastic change, but easy enough refactor and 461 is EOL anyways.

How do I call SQLitePCL.Batteries.Init().?

I am attempting to create an SQLite database for my application and have come across this error.
System.Exception: 'You need to call SQLitePCL.raw.SetProvider(). If
you are using a bundle package, this is done by calling
SQLitePCL.Batteries.Init().'
I created a simple console app the run the exact same code for creation, with no issues. The code looks like this!
using (var dataContext = new SampleDBContext())
{
dataContext.Accounts.Add(new Account() { AccountName = name, AccountBalance = balance });
}
public class SampleDBContext : DbContext
{
private static bool _created = false;
public SampleDBContext()
{
if (!_created)
{
_created = true;
Database.EnsureDeleted();
Database.EnsureCreated();
}
}
protected override void OnConfiguring(DbContextOptionsBuilder optionbuilder)
{
optionbuilder.UseSqlite(#"Data Source="Source folder"\Database.db");
}
public DbSet<Account> Accounts { get; set; }
}
Can anyone shed any light on the issue? I installed the same Nuget Packages on both projects, the only difference between the two is the Data Source and the POCO classes I used for the database.
Thanks.
Edit
My program currently consists of a Console application that references a .Net Framework Class Library. The Console application simple has a constructor that looks like this:
public Program()
{
using (var db = new FinancialContext())
{
db.Accounts.Add(new Account() { AccountName = "RBS", AccountBalance=20 });
}
}
The Class Library has a FinancialContext as Follows:
public class FinancialContext : DbContext
{
public DbSet<Account> Accounts { get; set; }
public FinancialContext()
{
# Database.EnsureDeleted();
Database.EnsureCreated();
}
protected override void OnConfiguring(DbContextOptionsBuilder optionbuilder)
{
optionbuilder.UseSqlite(#"Data Source="Some Source Folder"\Database.db");
}
}
The Above error is shown at the # symbol point, is there a problem with the way I am coding? I would really like to know what the issue is so I can fix it properly rather than apply a 'fix'. Also I tried the suggestion in the comments, but putting the code line SQLitePCL.raw.SetProvider(new SQLitePCL.SQLite3Provider_e_sqlite3()); in the Console Application gave the error SQLitePCL is not in the current context, which leaves me thinking I am missing a reference?
This happened to me when I tried to avoid any additional dependencies and went for the Microsoft.EntityFrameworkCore.Sqlite.Core package.
You should install and use the Microsoft.EntityFrameworkCore.Sqlite package instead, which has a dependency upon the SQLitePCLRaw package.
Install Nuget Package Microsoft.Data.Sqlite (not Microsoft.Data.Sqlite.Core). (my version is 2.2.2)
and use SQLitePCL.raw.SetProvider(new SQLitePCL.SQLite3Provider_e_sqlite3());
connection = new SqliteConnection("Data Source = Sample.db");
SQLitePCL.raw.SetProvider(new SQLitePCL.SQLite3Provider_e_sqlite3());
connection.Open();
but I advise use nuget package System.Data.SQLite instead Microsoft.Data.Sqlite
I had this very exact error. It turned out that I had package Microsoft.Data.Sqlite.Core (2.2.4) installed, but not SQLitePCLRaw.bundle_winsqlite3.
Installing package SQLitePCLRaw.bundle_winsqlite3 (1.1.13) solved the issue.
Switching from Microsoft.Data.Sqlite.Core to Microsoft.Data.Sqlite as Patrick said here did the trick for me
I got this issue when working with Microsoft.EntityFrameworkCore.Sqlite version 3.1.10. The above solutions did not work for me. Then I have modified the My DbContext as follows (added SQLitePCL.Batteries.Init(); to OnConfiguring method) and the issue is gone!!!
public class ApplicationDbContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite("Data Source=mydb.db");
SQLitePCL.Batteries.Init();
}
}
For some reason the Nuget Package hadn't installed the required references, reinstalled the package and it has corrected the issue!
Missing the SQLitePCL.raw* references.
I had the same issue when I try to use, Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6". What I did was downgrade the version into 2.2.2 which I was previously used. Then issue not occur.
On Xamarin.iOs I had the same problem.
Solution: Call SQLitePCL.Batteries_V2.Init() In the FinishedLaunching method of your AppDelegate class.
Source: https://learn.microsoft.com/en-us/dotnet/standard/data/sqlite/xamarin

ASP.NET: Check if run from migration

I have some code in my ConfigureServices that fails when running a migration:
dotnet ef migrations list
I'm trying to add a Certificate but it can't find the file (it works when starting the project as a whole). So is there a way to do something like this:
if (!CurrentEnvironment.IsMigration()) {
doMyStuffThatFailsInMigration()
}
That way I could keep my code as it is but just execute it when not running it in a migration.
Thanks
Just set a static flag in the Main method (which is not called by the dotnet-ef tool):
public class Program
{
public static bool IsStartedWithMain { get; private set; }
public static void Main(string[] args)
{
IsStartedWithMain = true;
...
}
}
and then check it when needed:
internal static void ConfigureServices(WebHostBuilderContext context, IServiceCollection services)
{
if (Program.IsStartedWithMain)
{
// do stuff which must not be run upon using the dotnet-ef tool
}
}
EDIT: in Dotnet 6.0 there's no separate ConfigureServices method. Everything is initialized in the Main method (can be created with dotnet new .. --use-program-main). In this case a flag can be used for skipping EF stuff:
private static bool IsStartedWithMain =>
Assembly.GetEntryAssembly() == Assembly.GetExecutingAssembly();
My current solution to detecting if a migration has not occurred:
using System.Linq;
// app is of type IApplicationBuilder
// RegisteredDBContext is the DBContext I have dependency injected
using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
var context = serviceScope.ServiceProvider.GetService<RegisteredDBContext>();
if (context.Database.GetPendingMigrations().Any())
{
var msg = "There are pending migrations application will not start. Make sure migrations are ran.";
throw new InvalidProgramException(msg);
// Instead of error throwing, other code could happen
}
}
This assumes that the migrations have been synced to the database already. If only EnsureDatabase has been called, then this approach does not work, because the migrations are all still pending.
There are other method options on the context.Database. GetMigrations and GetAppliedMigrations.

EF 6.1 how to swap migration configurations for testing?

I am trying to use Integration testing using EF 6.1 and run into a problem that my migration configuration settings are used where I dont need them. And I cant figure out how to swap them out for testing.
Here is my Test Class:
[TestClass]
public class SXSeasonConverterTests
{
public void RecreateDatabaseForTesting()
{
Database.SetInitializer(new TestDatabaseSeedingInitializer());
using (var context = new BaseNFLContext("NFLContextIntegrationTests"))
{
context.Database.Initialize(true);
}
}
public SXSeasonConverterTests()
{
RecreateDatabaseForTesting();
}
}
Here is my Initializer class:
public class TestDatabaseSeedingInitializer : DropCreateDatabaseAlways<BaseNFLContext>
{
protected override void Seed(BaseNFLContext context)
{
//Add Teams
context.Teams.Add(new Team { Code = "ARZ", Name = "Arizona Cardinals" });
context.Teams.Add(new Team { Code = "ATL", Name = "Atlanta Falcons" });
...
}
}
However when I try to run the test, I get the error that my AutomaticMigrations are disabled. When I looked further I found that It uses this code on Initialize:
internal sealed class NFLConfiguration : DbMigrationsConfiguration<BaseNFLContext>
{
public NFLConfiguration()
{
AutomaticMigrationsEnabled = false;
AutomaticMigrationDataLossAllowed = false;
}
}
This code is obviously there for production. However when doing testing how can I swap those migration configurations and set AutomaticMigrationsEnabled = true;?
I used to test my EF stuff using a special unittesting database and executed the tests in a TransactionScope which was rolled back at the end of the test. This way, no data was actually stored in the database.
I wasn't fast, but it suited our purpose.
You should create a separate project for testing and have a separate Db context that points to a test database. You can create something like a IDbContext interface that tells you which object models need to be tested. Also, the data access layer needs to allow you to inject this test Db context as a dependency.

Categories