Enable Migrations with Context in Separate Assembly? - c#

I have one project that I want to run my update-database against but I have my Models and Context in a separate project.
If I run enable-migrations I get this error:
No context type was found in the assembly 'MyProject'.
This is presumably because my Context is in MyProject.MVC.
If I run enable-migrations against MyProject.MVC I have to add an app config file. I don't want to do that as I want to use the code across many projects.
So can I run enable-migrations against MyProject and somehow tell it to look in MyProject.MVC for the Context?

This will only work in EF 6, but there was a release that added the -ContextProjectName parameter to the -enable-migrations command. By using this command you could do the following:
enable-migrations -ContextProjectName MyProject.MVC -StartUpProjectName MyProject.MVC
-ContextTypeName MyProject.MVC.MyContextFolder.MyContextName -ProjectName MyProject
This will add migrations to your MyProject project using the context in the MyProject.MVC.
You need to make sure that the project with the Migrations has a reference to the project with your Context, i.e., MyProject references MyProject.MVC

You may only run "Enable-Migrations" in the project containing the Database Context class.
Your solution will contain 2 projects:
1) MyProject.Models
|- Migrations
|- 201401061557314_InitialCreate.cs
|- Configuration.cs
|- MyContext.cs
|- App.config (no connection string)
App.config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
</configSections>
<entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" />
<providers>
<provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
</providers>
</entityFramework>
</configuration>
2) MyProject.MVC
|- Filters
|- InitializeSimpleMembershipAttribute.cs
InitializeSimpleMembershipAttribute.cs
namespace MyProject.MVC.Filters
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public sealed class InitializeSimpleMembershipAttribute : ActionFilterAttribute
{
private static SimpleMembershipInitializer _initializer;
private static object _initializerLock = new object();
private static bool _isInitialized;
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Ensure ASP.NET Simple Membership is initialized only once per app start
LazyInitializer.EnsureInitialized(ref _initializer, ref _isInitialized, ref _initializerLock);
}
private class SimpleMembershipInitializer
{
public SimpleMembershipInitializer()
{
try
{
Database.SetInitializer<MyContext>(new MigrateDatabaseToLatestVersion<MyContext, MyProject.Model.Migrations.Configuration>());
using (var context = new MyContext())
{
context.Database.Initialize(force: true);
if (!context.Database.Exists())
{
// Create the SimpleMembership database without Entity Framework migration schema
((IObjectContextAdapter)context).ObjectContext.CreateDatabase();
}
}
WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "UserName", autoCreateTables: true);
}
catch (Exception ex)
{
throw new InvalidOperationException("The ASP.NET Simple Membership database could not be initialized. For more information, please see http://go.microsoft.com/fwlink/?LinkId=256588", ex);
}
}
}
}
}
Set MyProject.MVC as startup project
In package manager, select project: MyProject.Models
Then run "Enable-Migrations" to create "Migrations" folder in MyProject.Models
Followed by "Update-Database" -> migrations will use connection string in Web.config from startup project to perform migration

Here is a workaround:
Add a class into MyProject(the project for migrations). Make this class inherit the dbcontext(the one in MyProject.MVC).
Then run EF migration commands on MyProject.

install Microsoft.EntityFrameworkCore.Tools to Enable Using package manager Console

I had the same problem, and I'm using EntityFramework 4.3.1. It seems that EF6 solves this issue (according to the answer by #SOfanatic) but I didn't want to upgrade to EF6 because of some breaking changes (in the DataAnnotations, for example).
So, what I did to solve this (and what I learned in the process):
Create a new solution (empty project) and add the project where you have the model you want to enable migrations for (in your case MyProject.MVC). You may need to install the required NuGet packages for it before you are able to add the existing project.
Add a config file with a connection string (don't worry, this is only to trick the migrations engine). Copy your existing database to the model project output folder (should be MVC\bin\Debug in your case). Make sure the connection string in the config file points to that database:
<connectionStrings>
<add name="MyDB" providerName="System.Data.SqlServerCe.4.0" connectionString="DataSource=|DataDirectory|\MyDB.sdf"/>
</connectionStrings>
Since you are in a new solution, set your model project as a startup project (you can remove the default project).
Run the enable-migrations command in the package manager console. It should create a Migrations folder with two files: a Configuration.cs and a timestamped InitialCreate.cs file. It's nice to have the InitialCreate, that's why you put your existing database in the model project's output folder (but this is optional).
Reload your original solution so that these changes are updated.
What I learned (as far as I understand):
The migrations engine needs something that looks like a valid connection to work. I was creating my connection string in code (in another project) and that didn't work. I just gave the Migrations engine a "valid" connection string to make it work.
Put your database where the migrations engine can find it (aka model project's output folder) so it creates a starting point for migrations. This starting point is basically your database schema written in the migrations API.
You can restore everything to your previous state once the migrations are set in place, and it works ok.
Everytime you want to manually add a migration, you must "trick" the migrations engine again, just like the first time. I haven't tried with automatic migrations, by I guess this approach works as well.
By th way, I am using a SQL Server CE 4.0 database, so some things about the connection string have a little twist compared to a standard SQL Server DB or LocalDB. Besides that, everything's the same.
Hope this is helpful and gives you some insight. Please comment if you know more about the way this migrations work.

Related

Entity Developer/EntityFramework - The specified named connection is either not found in the configuration

Using Devart Entity Developer with Entity Framework 6. I have my main class library project. I had a different Winform project altogether that uses Entity Developer/Entity Framework that I developed to do some database work with Sql. I decided to add this secondary Winform project to the main class library.
First I tried adding the Winform project to the solution. That did not work well. Even though I added a reference to the exe of the Winform project it seems not to find it ok. I removed that project from the solution.
As a second attempt. I copied the classes from the Winform project (two of them) to the main class library project and recreated the Entity Developer/Entity Framework stuff. Now when I run the project everything runs (including the new form) but I cannot connect to the database. I get the error:
The specified named connection is either not found in the configuration, not intended to be used with the EntityClient provider, or not valid.
The line that has the error is the line calling base:
public SWPDMEngVaultEntities() :
base(#"name=SWPDMEngVaultEntitiesConnectionString", "SWPDMEngVaultEntities")
{
this.ContextOptions.LazyLoadingEnabled = true;
OnContextCreated();
}
My connection string is in the app.config file and looks like this:
<connectionStrings>
<add name="SWPDMEngVaultEntitiesConnectionString" connectionString="metadata=res://*/DataModel1.csdl|res://*/DataModel1.ssdl|res://*/DataModel1.msl;provider=System.Data.SqlClient;provider connection string="Data Source=XXXXXX\SQLEXPRESS;Initial Catalog=XXXXXX;Integrated Security=False;Persist Security Info=True;User ID=XXXXXX;Password=XXXXXX""
providerName="System.Data.EntityClient" />
</connectionStrings>
All of the XXXXXX's in the above string have been confirmed as correct. Also, Entity Developer has a "Test Connection" and the test connection connects just fine. Entity Developer also gets the database information OK so I know it is able to connect without issue. The problem is at runtime on the solution.
I have looked at other posts about this error and they all seem to be saying make sure your connection string is good. What else should I check?
The answer to this issue came from Devart. Class libraries do not use app.config files for settings. By default the connection string in the Devary generated templates are stored in the app.config file. There is a place to turn this off when the model is generated.

Code First Entity Framework with ASP.NET Core: Initial migration seems to expect tables already exist

I'm trying to create a Code First Entity Framework ASP.NET Core 2 project in Visual Studio Code. I've been following the Create a Web API with ASP.NET Core MVC and Visual Studio Code on Linux, macOS, and Windows tutorial, which uses an in-memory datastore as its DbContext. I'm trying to move this to LocalDB.
The tutorial Getting Started with EF Core on ASP.NET Core with a New database suggests I should be able to do this with a migration.
Once you have a model, you can use migrations to create a database.
Open the PMC:
Tools –> NuGet Package Manager –> Package Manager Console
Run Add-Migration InitialCreate to scaffold a migration to create the initial set of tables for your model. If you receive an error stating The term 'add-migration' is not recognized as the name of a cmdlet, close and reopen Visual Studio.
Run Update-Database to apply the new migration to the database. This command creates the database before applying migrations.
The VS Code equivalent of using the Package Manager Console seems to be:
dotnet ef migrations add InitialCreate
I've added EF's Design namespace with...
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet restore
And have the reference in my csproj:
<ItemGroup>
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.1" />
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.0" />
</ItemGroup>
But when I try that the dotnet ef migrations add command afterwards, it acts like the table for my TodoItems model needed to already exist in the database. It was my understanding that the migration would create the tables based on my models.
c:\Projects\TodoApi>dotnet ef migrations add InitialCreate -v
Using project 'c:\Projects\TodoApi\TodoApi.csproj'.
Using startup project 'c:\Projects\TodoApi\TodoApi.csproj'.
Writing 'c:\Projects\TodoApi\obj\TodoApi.csproj.EntityFrameworkCore.targets'...
dotnet msbuild /target:GetEFProjectMetadata /property:EFProjectMetadataFile=C:\Users\UserName\AppData\Local\Temp\tmp945E.tmp /verbosity:quiet /nologo c:\Projects\TodoApi\TodoApi.csproj
Writing 'c:\Projects\TodoApi\obj\TodoApi.csproj.EntityFrameworkCore.targets'...
dotnet msbuild /target:GetEFProjectMetadata /property:EFProjectMetadataFile=C:\Users\UserName\AppData\Local\Temp\tmp96FF.tmp /verbosity:quiet /nologo c:\Projects\TodoApi\TodoApi.csproj
dotnet build c:\Projects\TodoApi\TodoApi.csproj /verbosity:quiet /nologo
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed 00:00:02.29
dotnet exec --depsfile c:\Projects\TodoApi\bin\Debug\netcoreapp2.0\TodoApi.deps.json --additionalprobingpath C:\Users\UserName\.nuget\packages --additionalprobingpath "C:\Program Files (x86)\Microsoft SDKs\NuGetPackagesFallback" --additionalprobingpath "C:\Program Files\dotnet\sdk\NuGetFallbackFolder" --runtimeconfig c:\Projects\TodoApi\bin\Debug\netcoreapp2.0\TodoApi.runtimeconfig.json C:\Users\UserName\.nuget\packages\microsoft.entityframeworkcore.tools.dotnet\2.0.0\tools\netcoreapp2.0\ef.dll migrations add InitialCreate --assembly c:\Projects\TodoApi\bin\Debug\netcoreapp2.0\TodoApi.dll --startup-assembly c:\Projects\TodoApi\bin\Debug\netcoreapp2.0\TodoApi.dll --project-dir c:\Projects\TodoApi\ --verbose --root-namespace TodoApi
Using assembly 'TodoApi'.
Using startup assembly 'TodoApi'.
Using application base 'c:\Projects\TodoApi\bin\Debug\netcoreapp2.0'.
Using working directory 'c:\Projects\TodoApi'.
Using root namespace 'TodoApi'.
Using project directory 'c:\Projects\TodoApi\'.
Finding DbContext classes...
Finding IDesignTimeDbContextFactory implementations...
Finding application service provider...
Finding BuildWebHost method...
Using environment 'Development'.
Using application service provider from BuildWebHost method on 'Program'.
Found DbContext 'DatabaseContext'.
Finding DbContext classes in the project...
fail: Microsoft.EntityFrameworkCore.Database.Command[20102]
Failed executing DbCommand (7ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT CASE
WHEN EXISTS (
SELECT 1
FROM [TodoItems] AS [t])
THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT)
END
System.Data.SqlClient.SqlException (0x80131904): Invalid object name 'TodoItems'.
What needs to be done to ensure tables are created based on my models?
The only gotcha I can think of offhand is that I have the connection string inline rather than in appsettings.json, but I'm not sure why that'd be a big deal, unless the migration is looking for a config by default.
public void ConfigureServices(IServiceCollection services)
{
// In memory: services.AddDbContext<DatabaseContext>(opt => opt.UseInMemoryDatabase("TodoList")); // <<< REMOVED
string strCxn = "Server=(localdb)\\mssqllocaldb;Database=Contacts;Trusted_Connection=True;MultipleActiveResultSets=true"; // <<< ADDED
services.AddDbContext<DatabaseContext>(options => options.UseSqlServer(strCxn)); // <<< ADDED
services.AddMvc();
}
Fwiw, those are the only changes I've made to the TodoApi tutorial's code to move from the in-memory db to LocalDB.
Update: Fwiw, I tried changing the database name in the connection string..
string strCxn = "Server=(localdb)\\mssqllocaldb;Database=Contacts2;Trusted_Connection=True;MultipleActiveResultSets=true";
(Changing Contacts to Contacts2, just in case it thought that, since it found the Contacts db initially, a migration had already taken place...)
That didn't work either, though the error changed in a way that suggests the connection string is working and being read.
Cannot open database "Contacts2" requested by the login. The login failed.
Login failed for user 'COMPUTER-NAME\UserName'.
As part of the DbContext's constructor, I was seeding data...
public class DatabaseContext : DbContext {
public DatabaseContext(DbContextOptions<DatabaseContext> options) : base(options)
{
this.MinimallySeedDatabase();
}
public void MinimallySeedDatabase()
{
State NY = this.States.Where(s => s.Abbr.Equals("NY")).FirstOrDefault();
if (null == NY)
{
NY = new State {
Name = "New York",
Abbr = "NY"
};
this.States.Add(NY);
this.SaveChanges();
}
// etc etc etc...
}
public DbSet<Contact> Contacts {get; set;}
public DbSet<Address> Addresses {get; set;}
public DbSet<AddyType> AddyTypes {get; set;}
public DbSet<State> States {get; set;}
}
Part of the migration's execution involves instantiating the DbContext obviously before the tables for the DbSets are migrated over. Since the constructor, in this example, requires accessing States, and because the migration hasn't gotten to the point it's created the States table to pull from yet, the migration blows up.
As Kirk mentions, if I take out the seeding, I'm fine. And if you want to seed, you need to move that somewhere other than the constructor, even though the seeding in the constructor works fine outside of a migration attempt/in normal Kestrel testing.
For me, I just commented out this.MinimallySeedDatabase(); when I ran the migration.

Can't read app.config in C# .NET Core unit test project with ConfigurationManager

I've created a simple unit test project to read an app.config file. Target framework is Core 2.0. I also created a Core 2.0 console app, to sanity-check myself to make sure I wasn't doing anything weird (same test passed as expected in a .NET 4.6.1 unit test project).
The console app reads the app.config fine, but the unit test method fails and I cannot figure out why. Both are using a copy of the same app.config (not added as a link) and both have the System.Configuration.ConfigurationManager v4.4.1 NuGet package installed.
The App.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="Test1" value ="This is test 1."/>
<add key="Test2" value ="42"/>
<add key="Test3" value ="-42"/>
<add key="Test4" value="true"/>
<add key="Test5" value="false"/>
<add key="Test6" value ="101.101"/>
<add key="Test7" value ="-1.2345"/>
</appSettings>
</configuration>
The Unit Test
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Configuration;
namespace ConfigTest
{
[TestClass]
public class UnitTest1
{
[TestMethod()]
public void ConfigTest()
{
foreach (string s in ConfigurationManager.AppSettings.AllKeys)
{
System.Console.WriteLine(s);
System.Diagnostics.Debug.WriteLine(s);
}
//AllKeys.Length is 0? Should be 7...
Assert.IsTrue(ConfigurationManager.AppSettings.AllKeys.Length == 7);
}
}
}
The Console App
using System;
using System.Configuration;
namespace ConfigTestApp
{
class Program
{
static void Main(string[] args)
{
foreach (string s in ConfigurationManager.AppSettings.AllKeys)
{
Console.WriteLine(s);
System.Diagnostics.Debug.WriteLine(s);
}
//Outputs 7 as expected
Console.WriteLine(ConfigurationManager.AppSettings.AllKeys.Length);
}
}
}
Given that I'm still pretty new to the whole .NET Core world, am I doing something totally incorrect here? I sort of just feel crazy at the moment...
Looking through the github issue's comments, I found a work around that can go in the msbuild file...
<Target Name="CopyCustomContent" AfterTargets="AfterBuild">
<Copy SourceFiles="app.config" DestinationFiles="$(OutDir)\testhost.dll.config" />
</Target>
This makes it easier to verify existing tests under .NET Core before porting the configuration data over to json configuration files.
Edit
If running under Resharper, the previous answer doesn't work as Resharper proxies the assembly, so you need
<Target Name="CopyCustomContent" AfterTargets="AfterBuild">
<Copy SourceFiles="app.config" DestinationFiles="$(OutDir)\ReSharperTestRunner64.dll.config" />
</Target>
If you check the result of the call to ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
It should tell you where the required configuration file should be while running unit tests for that assembly.
I found that instead of having an app.config file, ConfigurationManager was looking for a testhost.dll.config file.
This was for a project targeting netcoreapp2.1, with a reference to Microsoft.NET.Test.Sdk,NUnit 3.11 and Nunit3TestAdapter 3.12.0
.CORE 3.1
To find out what dll.config file was being used, I debugged the test by adding this line and looking to see what the value is.
string path = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None).FilePath;
Then I found out resharper was using testhost.dll.config and VStest was using testhost.x86.dll.config. I needed to add the following lines to the project file.
<Target Name="CopyCustomContent" AfterTargets="AfterBuild">
<Copy SourceFiles="app.config" DestinationFiles="$(OutDir)\testhost.dll.config" />
<Copy SourceFiles="app.config" DestinationFiles="$(OutDir)\testhost.x86.dll.config" />
</Target>
I came across the same issue with my xunit tests and solved it by using the instance of Configuration from ConfigurationManager. I put the static (normal) way it works in core, framework (but not unit tests) before I show the alternative way it works in all three:
var appSettingValFromStatic = ConfigurationManager.AppSettings["mySetting"];
var appSettingValFromInstance = ConfigurationManager.OpenExeConfiguration(Assembly.GetExecutingAssembly().Location).AppSettings.Settings["mySetting"].Value;
And here is a similar/related issue. In case anyone needs to get a section you can do a similar thing, though the type must change in the app config:
<configSections>
<section name="customAppSettingsSection" type="System.Configuration.AppSettingsSection"/>
<section name="customNameValueSectionHandlerSection" type="System.Configuration.NameValueSectionHandler"/>
</configSections>
<customAppSettingsSection>
<add key="customKey" value="customValue" />
</customAppSettingsSection>
<customNameValueSectionHandlerSection>
<add key="customKey" value="customValue" />
</customNameValueSectionHandlerSection>
Code to grab section:
var valFromStatic = ((NameValueCollection)ConfigurationManager.GetSection("customNameValueSectionHandlerSection"))["customKey"];
var valFromInstance = ((AppSettingsSection)ConfigurationManager.OpenExeConfiguration(Assembly.GetExecutingAssembly().Location).GetSection("customAppSettingsSection")).Settings["customKey"].Value;
I feel like I am also crazy, and I know there are newer ways of doing config in core, but if one wants to do something cross-platform this is the only way I know how. I'd be very interested if anyone has alternatives
For my mixed .NET-Core & .NET-Framework project, I added the following to the unit test global setup:
#if NETCOREAPP
using System.Configuration;
using System.IO;
using System.Reflection;
#endif
...
// In your global setup:
#if NETCOREAPP
string configFile = $"{Assembly.GetExecutingAssembly().Location}.config";
string outputConfigFile = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None).FilePath;
File.Copy(configFile, outputConfigFile, true);
#endif
This copies the config file to the output path testhost.dll.config but should be resilient enough to account for future changes in the testing framework.
Or you can copy to below, which amounts to the same thing:
string outputConfigFile = Path.Combine(Path.GetDirectoryName(configFile), $"{Path.GetFileName(Assembly.GetEntryAssembly().Location)}.config");
Credit to #stop-cran and #PaulHatcher's solutions, this is a combination of those two.
None of the answers given here provide a viable workaround when you're dealing with code accessing directly the static ConfigurationManager properties such as AppSettings or ConnectionStrings.
The truth is, it is not possible at the moment. You can read through the discussion here to understand why:
https://github.com/dotnet/corefx/issues/22101
There is talk to implement the support for it here:
https://github.com/Microsoft/vstest/issues/1758
In my opinion it makes sense to support this scenario since it's been working on the .NET Framework plus System.Configuration.ConfigurationManager is a .NET Standard 2.0 library now.
A hacky, but working way is to copy the config to the same folder as an entry assembly, whatever it is:
[SetUpFixture]
public class ConfigKludge
{
[OneTimeSetUp]
public void Setup() =>
File.Copy(
Assembly.GetExecutingAssembly().Location + ".config",
Assembly.GetEntryAssembly().Location + ".config",
true);
[OneTimeTearDown]
public void Teardown() =>
File.Delete(Assembly.GetEntryAssembly().Location + ".config");
}
Apart from adding this class, the only thing to make it work is to include app.config file in test project (without any copying options). It should be copied to the output folder as <your test project name>.dll.config at the build step, because it's kind of default logic.
Note the documentation for OneTimeSetUpAttribute:
Summary:
Identifies a method that is called once to perform setup before any child tests are run.
Although it should work for parallel test runs for a single project, there could be obvious troubles when running two test projects simultaneously, since the config would get overwritten.
However, it is still suitable for containerized test runs, like in Travis.
When we answer such well-researched and well-articulated question, we should better assume that it is being asked by an informed and intelligent being. And instead of patronizing them with the obvious about new, great ways of writing tonnes of boilerplate code to parse all sort of JSON et al, being forced on us and shoved down our throat by know-betters, we should focus on answering to the point.
Since the OP is already using System.Configuration to access settings, they already know how to arrive at this point. The only thing that is missing is one little touch: adding this line to the post-build event:
copy $(OutDir)<appname>.dll.config $(OutDir)testhost.dll.config
where <appname> is the project being unit-tested.
I applaud everyone who is still using (originally lame but workable) implementation of app.config because doing so protects our and our clients' investment in technology instead of reinventing the wheel. Amen.
Mercifully there is now a way to set the name of the expected configuration file at runtime. You can set the APP_CONFIG_FILE data for the current app domain.
I created the following SetUpFixture to do this automatically:
[SetUpFixture]
public class SetUpFixture
{
[OneTimeSetUp]
public void OneTimeSetUp()
{
var testDllName = Assembly.GetAssembly(GetType())
.GetName()
.Name;
var configName = testDllName + ".dll.config";
AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", configName);
}
}
The relevant GitHub discussions are:
ConfigurationManager doesn't find config file with "dotnet test" · Issue #22720 · dotnet/runtime
Provide a way to override the global configuration file path · Issue #931 · dotnet/runtime
Respect AppContext.SetData with APP_CONFIG_FILE key by krwq · Pull Request #56748 · dotnet/runtime
The ConfigurationManager API will only use the configuration of the app that is currently running. In a unit test project, this means the app.config of the test project, not the console application.
.NET Core Applications aren't supposed to use app.config or ConfigurationManager, as it is a legacy "full framework" configuration system.
Consider using Microsoft.Extensions.Configuration instead to read JSON, XML or INI configuration files. See this doc: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration
Usually in .NET Framework projects, any App.config file was copied to the bin folder by Visual Studio, with the name of your executable (myApp.exe.config) so it could be reachable in runtime. Not anymore in .NET Standard or Core Framework. You must manually copy and set the file in the bin/debug or release folder. After that it could be get with something like:
string AssemblyName = System.IO.Path.GetFileName(System.Reflection.Assembly.GetEntryAssembly().GetName().CodeBase);
AppConfig = (System.Configuration.Configuration)System.Configuration.ConfigurationManager.OpenExeConfiguration(AssemblyName);
While app.config exists in the root project folder add below string to Post-build event command line
xcopy /Y $(ProjectDir)app.config $(ProjectDir)$(OutDir)testhost.dll.config*
Add the configuration file
First, add a appconfig.json file to the Integration test project
Configure the appconfig.json file to be copied to the output
directory by updating
Add NuGet package
Microsoft.Extensions.Configuration.Json
Use the configuration in your unit tests
[TestClass]
public class IntegrationTests
{
public IntegrationTests()
{
var config = new ConfigurationBuilder().AddJsonFile("appconfig.json").Build();
_numberOfPumps = Convert.ToInt32(config["NumberOfPumps"]);
_numberOfMessages = Convert.ToInt32(config["NumberOfMessages"]);
_databaseUrl = config["DatabaseUrlAddress"];
}
}

how to properly promote Entity Framework code-first migrations into production?

In my ASP.NET EF 6 app, I have the following Configuration:
internal sealed class Configuration : DbMigrationsConfiguration<Gcim.Management.Module.BusinessObjects.ManagementDbContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
}
protected override void Seed(Gcim.Management.Module.BusinessObjects.ManagementDbContext context)
{
}
}
In my project, I have EF code-first migrations enabled.
I'm able to run add-migration, as well as update-database from the VS Package Manager prompt.
However, when my entities change, adding a migration, and updating database only works in dev environment.
When I publish my ASP.NET project, install it on production IIS, and run, I still get this error:
The model backing the 'ManagementDbContext' context has changed since the database was created. Consider using Code First Migrations to update the database (http://go.microsoft.com/fwlink/?LinkId=238269).
What else do I need to do in order to force the incremental DB changes in production, without data loss?
You can execute a given migration file against a given environment (in this case, your production environment) by using the migrate.exe application included as part of the Entity Framework nuget package. You'll need to execute your latest migration against your production environment before publishing your web application - once this has been done, the newly deployed application will work correctly.
You do this by specifying the connection string and .dll containing the migration class(es), as well as possibly a specific migration. For example:
Migrate.exe Gcim.Management.Module.BusinessObjects.dll /connectionString="Data Source=[YourProductionDatabase];Initial Catalog=[YourTable];Integrated Security=SSPI" /connectionProviderName="System.Data.SqlClient" /targetMigration="[NameofYourMigration]"
Documentation on migrate.exe can be found here: https://msdn.microsoft.com/en-us/data/jj618307.aspx

System.Data.SqLite DataProvider not found when executing Test using NUnit

I am facing a problem with my UnitTest. I want to Test my Data-Access which is done using a repository based on NPoco. I have therefore written a couple of tests and the test project retrieves NUnit, NPoco, System.Data.SQLite, and some other Stuff via NuGet.
This is the app.config of the TestProject:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<connectionStrings>
<add name="RepositoryTests.Properties.Settings.ConnectionString" connectionString="Data Source=db.sqlite;Version=3" />
</connectionStrings>
<system.data>
<DbProviderFactories>
<remove invariant="System.Data.SQLite"/>
<add name="SQLite Data Provider" invariant="System.Data.SQLite" description=".NET Framework Data Provider for SQLite" type="System.Data.SQLite.SQLiteFactory, System.Data.SQLite" />
</DbProviderFactories>
</system.data>
</configuration>
In VS, the Project builds fine. Triggering the tests in Visual Studio works as well.
Building the Projects with MSBUILD works as well. Running the tests via nunit after building them with msbuild.exe raises a Exception though:
Unable to find the requested .Net Framework Data Provider. It may not be installed.
This is only the case, when executing the tests using nunit directly (sth. like nunit-console.exe myproject.csproj /config:Release). Triggering them in VS is no problem.
Does anybody know how to solve this problem?
The problem is caused by the nunit test runner having its own app.config in which your settings are not present.
The simplest way to solve the problem is to move the configuration out of your app.config and into the code itself. Whatever you have in app.config can be done inside code.
Another solution is to move the configuration into a seperate file and then explicitly load that file's configuration using code. Just make sure that the file is copied to the output folder on build.
You could use something like:
public static class TestFactory
{
public static DatabaseFactory DbFactory { get; set; }
public static void Setup()
{
var fluentConfig = FluentMappingConfiguration.Configure(new OurMappings());
//or individual mappings
//var fluentConfig = FluentMappingConfiguration.Configure(new UserMapping(), ....);
DbFactory = DatabaseFactory.Config(x =>
{
// Load the connection string here or just use a constant in code...
x.UsingDatabase(() => new Database("connString");
x.WithFluentConfig(fluentConfig);
x.WithMapper(new Mapper());
});
}
}
See here for more details.
Then in the test fixture:
[TestFixture]
public class DbTestFixture
{
[TestFixtureSetUp]
public void Init()
{
TestFactory.Setup();
}
[Test]
public void YourTestHere()
{
var database = TestFactory.DbFactory.GetDatabase();
...
}
}
I got it working by creating an copy of the app.config-file and give that copy the name of the Test-Project, followed by .config. So if we assume, that the project is named Test.Project the copy must be named Test.Project.config. Nunit doesn't seem to load the automatically generated Test.Project.dll.config. This info can be found somewhat disguised in the NUnit-docs (Configuration Files -> Test Configuration File -> 3rd paragraph, last sentence).
In VS, in the Properties-Section of that copied file, i set the copy-to-output-directory-property to always.
Executing the tests with nunit-console.exe then lead to another exception (Bad-Image), which was caused by NUnit not finding the SQLite.Interop.dll-file. This could be solved kind of hacky by adding that file, which already resides in the x64 or x86 folder, as an existing element to the solution in VS and also setting the copy-to-output-dir-property to always.

Categories