How to enable Migration when context and models are in separate assemblies - c#

I'm working on a c# windows desktop project using Entity Framework 6. There are 3 different project in the solution. Let's say ProjectA which is the startup project and includes the exe file; ProjectB which is the framework and includes the DbContext; and ProjectC which includes all the models.
Now I really don't know how I should enable migration for these different projects since each one is going to be a separate assembly.
I'm familiar with enable migration and add migration commands however I don't know how to use it when the context is in another assembly and the models in yet another assembly.
Please help.
Thanks in advance.

You can setup initialization classes that can be called from your startup project.
public static class InitializeAndSeed
{
public static void InitializeContext()
{
Database.SetInitializer(new MigrateDatabaseToLatestVersion<YourDbContext
,YourDbContextMigrations>());
using (var db = new YourDbContext())
{
db.Database.Initialize(false);
}
}
}
public class YourDbContextMigrations : DbMigrationsConfiguration<YourDbContext>
{
public YourDbContextMigrations ()
{
AutomaticMigrationsEnabled = true;
AutomaticMigrationDataLossAllowed = true;
}
//Your seeding and your migrations
}
Then, from your startup project you can call the InitializeContext() method to initialize the context from an application startup class or where ever you need. You just want to make sure you only initialize it once per application start.

Related

"No DbContext was found in assembly" error when trying to add migrations in Entity Framework

I am making a C# MVC application using Entity Framework 6.2.0 Everything was going fine until today when I tried to do some migrations.
I had no problem with migrating a week ago but I have no idea what might have caused the error in the package manager:
No DbContext was found in assembly 'Data'. Ensure that you're using
the correct assembly and that the type is neither abstract nor
generic.
I tried reinstalling Entity Framework and made sure that the "Default project" is the right one. I already have a context file that worked properly.
This is my GameContext.cs code:
public class GameContext :DbContext
{
public GameContext()
:base ("name=GameContext")
{
}
public DbSet<Game> Game { get; set; }
public DbSet<Account> Account { get; set; }
}
I need to find a way to fix this problem and manage to update my database.
You maybe need to select ProjectName.Data in project management console
Try adding your assembly name when registering the service, you can find assembly name by going to project properties.
services.AddDbContextPool<AppDbContext>
(
dbContextOptionsBuilder =>
{
dbContextOptionsBuilder.UseSqlServer("yourConnection",
optionsSqlServer => { optionsSqlServer.MigrationsAssembly("ADD_YOUR_ASSEMBLY_NAME");});
}
);

Why is Entity Framework's migrations Configuration.cs file located and executed automatically?

I have a C# project where I'm using Entity Framework 6.1. I have a few migration files and a Configuration.cs file within the same folder. Configuration.cs contains the following class:
namespace SNS.Database.Migrations
{
public class Configuration : DbMigrationsConfiguration<SNSContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}
protected override void Seed(SNSContext context)
{
...
}
}
}
If I understand Entity Framework correctly, the Seed method is supposed to be executed after a new database migration file has been applied. Furthermore, to configure this, it's done with the following call:
System.Data.Entity.Database.SetInitializer(new MigrateDatabaseToLatestVersion<SNSContext, Configuration>());
That works fine. However, the Seed method does always get called, even if I haven't made the SetInitializer call.
So my question is, what tells Entity Framework to run Configuration.cs, and why does it always execute the Seed method?
I have already checked in my App.config file, and it doesn't specify codeConfigurationType.

Can you use a Visual Studio Database Project in a Unit Test Project to setup a empty database for a functional test?

For years we have used the following code to setup databases in a base class for our functional tests for our DAL, and this has worked extremely well for us.
/// <summary>
/// Initializes the test class by creating the integration database.
/// </summary>
[TestInitialize]
public virtual void TestInitialize()
{
DataContext = new DataContext(ConnectionString);
CleanupPreviousTestRunDatabases();
if (DataContext.Database.Exists())
{
DataContext.Database.Delete();
}
DataContext.Database.Create();
DataContext.Database.ExecuteSqlCommand(String.Format(Strings.CreateLoginCommand, DatabaseUserName, DatabasePassword));
DataContext.Database.ExecuteSqlCommand(String.Format("CREATE USER {0} FOR LOGIN {0}", DatabaseUserName));
DataContext.Database.ExecuteSqlCommand(String.Format("EXEC sp_addrolemember 'db_owner', '{0}'", DatabaseUserName));
}
However, using Entity Framework does not setup all components of a database and we would like to catch discrepancies between our EF DAL model and the actual database.
We use the SSDT tools / Visual Studio Database Project for all of our database work, and I know you can write SQL unit tests, and in those SQL unit tests, I have seen the ability to setup and create a database based on the database project itself. This is what I would like to do, but from our other functional test libraries.
I can reference the libraries and write some of the setup code, but what I'm looking for is:
a) How do I provide which Database project to use to deploy?
b) How can I specify connection string in code rather than an app.config, such as using localdb instead with a dynamically named database?
namespace Product.Data.Tests
{
using Microsoft.Data.Tools.Schema.Sql.UnitTesting;
using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
public class FunctionalTest
{
[TestInitialize]
public virtual void TestInitialize()
{
SqlDatabaseTestClass.TestService.DeployDatabaseProject();
SqlDatabaseTestClass.TestService.GenerateData();
}
}
}
The app.config in a SQL Unit Test Project doesn't contain any reference back to the original Database project used to create it, and decompiling some of the test code and seeing how it works, I don't see any indication. Does it assume there is only one database project in the solution?
With some direction from the links #Ed Elliott posted, I was able to make this happen. You will need to add Microsoft.SqlServer.Dac as a assembly reference from C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\Extensions\Microsoft\SQLDB\DAC\120\Microsoft.SqlServer.Dac.dll (Visual Studio 2015). It is part of the SSDT tooling, so I'm sure the path may be different for earlier versions.
[TestClass]
public class DatabaseTest
{
protected string DatabaseConnectionString = $#"Data Source=(localdb)\v11.0; Integrated Security=True";
protected DatabaseContext DatabaseContext;
protected string DatabaseName = $"UnitTestDB_{Guid.NewGuid().ToString("N").ToUpper()}";
public TestContext TestContext { get; set; }
[TestInitialize]
public virtual void TestInitialize()
{
var instance = new DacServices(DatabaseConnectionString);
var path = Path.GetFullPath(Path.Combine(TestContext.TestDir,
#"..\..\..\Build\Database\Database.dacpac"));
using (var dacpac = DacPackage.Load(path))
{
instance.Deploy(dacpac, DatabaseName);
}
DatabaseContext = new DatabaseContext(DatabaseConnectionString);
}
[TestCleanup]
public virtual void TestCleanup()
{
DeleteDatabase(DatabaseName);
}
}
Then how it would be used for a functional test in a unit test project.
[TestClass]
public class CustomerTypeTests : DatabaseTest
{
private CustomerType customerType;
[TestInitialize]
public override void TestInitialize()
{
base.TestInitialize();
customerType = new CustomerType
{
Name = "Customer Type"
};
}
[TestMethod]
public void AddOrUpdateCustomerType_ThrowExceptionIfNameIsNull()
{
ExceptionAssert.Throws<ArgumentNullException>(() => DatabaseContext.AddOrUpdateCustomerType(customerType));
}
}
Just a note to others, you should also setup your Build Dependencies so that your unit test project depends on the database project, ensuring it is built first and produces the correct dacpac file.
What this solves for us, is this gives us a true database, not one just based on Entity Framework's model, which lacks quite a lot of SQL constructs (to be expected), especially default constraints, indexes, and other important elements of a database. At our DAL layer, this is essential for us.
I think the process you have is a little over complicated (if I understand it correctly which I might not have!).
What I do for unit testing in ssdt is to:
Build the solution
Deploy each dacpac that I need to my dev instance
Run the tests
To deploy a project there are a few ways, you can:
Create a "Publish Profile" for each project and run that
Right click on the project and choose publish
Use a powershell script (or do it in code in your test initialize) to do a publish of the dacpac.
Once it is deployed run your tests, doing a publish of a dacpac (project) is pretty simple from code or a script, you can either:
call sqlpackage.exe to do it for you
use the dacfx api's to do the deploy (http://blogs.msmvps.com/deborahk/deploying-a-dacpac-with-dacfx-api/)
If you control the publish yourself then it gives you a lot more control plus when you deploly using this you are testing the same deployment system that you will use in other environments (assuming you use dacpac's to deploy).
ed

The default DbConfiguration instance was used before the 'EntityFrameworkConfiguration' type was discovered

public class EntityFrameworkConfiguration : DbConfiguration
{
public EntityFrameworkConfiguration()
{
this.SetModelCacheKey(ctx => new EntityModelCacheKey((ctx.GetType().FullName + ctx.Database.Connection.ConnectionString).GetHashCode()));
}
}
To make the above code work i have added below line in web.config
But for other project where i am using the assembly reference i am getting exception:
{"The default DbConfiguration instance was used by the Entity
Framework before the 'EntityFrameworkConfiguration' type was
discovered. An instance of 'EntityFrameworkConfiguration' must be set
at application start before using any Entity Framework features or
must be registered in the application's config file. See
http://go.microsoft.com/fwlink/?LinkId=260883 for more information."}
Your question doesn't state how you are using this custom DbConfiguration.
You could get this error a couple of different ways.
Configuration style setup
as described here: Entity Framework Config File Settings
Configuration as code
as described here: Entity Framework Code-Based Configuration (EF6 onwards)
You can hack at this style by doing things like DbConfiguration.SetConfiguration(xxxx). I didnt find this useful at all.
What this really comes down to is how you construct your DbContext.
Configuration file style constructors
https://github.com/aspnet/EntityFramework6/blob/master/src/EntityFramework/DbContext.cs#L75
With no arguments - EF6 is uses the configuraiton files to determine the right DbCofniguration to use
with some "connection string-like" arguments again EF6 is using configuration files to determine the DbConfiguration
no config, or bad config - and you will get this sort or exception
Configuration as Code style constructors
https://github.com/aspnet/EntityFramework6/blob/master/src/EntityFramework/DbContext.cs#L139
I think this yields better control.
Attribute your DbContext, then use a manually created DbConnection
public class EntityFrameworkConfiguration : DbConfiguration
{
public EntityFrameworkConfiguration()
{
this.SetModelCacheKey(ctx => new EntityModelCacheKey((ctx.GetType().FullName + ctx.Database.Connection.ConnectionString).GetHashCode()));
}
}
[DbConfigurationType(typeof(EntityFrameworkConfiguration))]
public class MyContext : DbContext
{
public MyContext(DbConnection existingConnection, bool contextOwnsConnection)
: base(existingConnection, contextOwnsConnection)
{ }
public DbSet<Stuff> Stuff { get; set; }
}
using(var conn = new SqlConnection(asqlserverConnectionString))
using (var db = new MyContext(conn, true))
{
var value = await db.Stuff.Where(s => s.xxx.Equals(primaryKey)).Select(s => new { s.BinaryContent } ).SingleOrDefaultAsync();
}
If you are using Code-based configuration, try updating the config file thusly:
<entityFramework codeConfigurationType="MyNamespace.MyDbConfiguration, MyAssembly">
...Your EF config...
</entityFramework>
In my case i have two different edmx files and both are in different class libraries.
I got this error when i added those two libraries in the same project.
I don't have any single clue how is that sorted out but; when i call any method from my first DbContext class, the second one worked like miracle happened. It was throwing this error when second context class called first.
My Ef version is: 6.4

Dynamic Connection String in Entity Framework

GOAL using Database-First Paradigm (not Code-First) for a deployed desktop wpf application, with unique databases for end users:
1) Have EntityFramework use a connection string determined at run time.
2) Not deploy different app.config files.
Things attempted:
1) Overload the constructor - while successful, this solution is undesired as it leaves the door open for developers to make mistakes, makes unit testing more difficult.
2) Attempted modifying the connection / context factory - threw Exception.
3) Change the default constructor - could be successful, this solution is undesired as the default constructor is autogenerated.
4) Attempted modifying the ConfigurationSettings - threw Exception, it is read-only.
5) Have a customer side deployment of app.config - while plausible, this solution is undesired as it requires a rewrite of our deployment engine.
Help?
EDIT:
Some code related to first item we tried (overloading the constructor):
public partial class DatabaseContext
{
public DatabaseContext(EntityConnection con)
: base(con, true)
{
}
}
public static class DbContextHelper
{
public static string ConnectionString { get; set; }
public static CounterpartDatabaseContext GetDbContext()
{
EntityConnectionStringBuilder builder = new EntityConnectionStringBuilder
{
Provider = "System.Data.SqlClient",
ProviderConnectionString = ConnectionString,
Metadata = #"res://*/DatabaseContext.csdl|res://*/DatabaseContext.ssdl|res://*/DatabaseContext.msl"
};
EntityConnection con = new EntityConnection(builder.ToString());
return new DatabaseContext(con);
}
}
Usage:
public void SomeMethod()
{
using(DatabaseContext db = DbContextHelper.GetDbContext())
{
// db things
}
}
EDIT code for adding connection string with config manager:
public MainWindow()
{
InitializeComponent();
ConfigurationManager.ConnectionStrings.Add(new ConnectionStringSettings("DatabaseContext", #"metadata=res://*/DatabaseContext.csdl|res://*/DatabaseContext.ssdl|res://*/DatabaseContext.msl;provider=System.Data.SqlClient;provider connection string="data source=sqldev;initial catalog=Dev;persist security info=True;user id=user;password=password;MultipleActiveResultSets=True;App=EntityFramework"", "System.Data.EntityClient"));
}
the config manager code just throws an exception, so no point in any code after that.
Generated DatabaseContext class is both partial. With partial you can add code in another file (just remember about partial keyword there) and still be to re-generate everything. Generator will only overwrite the file it generated, all other files with extra additions to that partial class will not evaporate. No problem with mantaining generated and handwritten parts there.
Also, the generated class is not sealed. You can inherit from it. So, instead of using DatabaseContext directly, you might try inheriting from it and start using the derived class. This derived class will not inherit the constructors, but will inherit all other public important things. You will be able then to provide your own constructor, even default one, that will i.e. call parameterized base class ctor. Actually, I have not tried it that way, but it looks simple and may work.
What I propose is not using DbContextHelper.GetContext() (which is static obviously) (which you think the devs may misuse or forget), but rolling in your own DbContext class.
In the project where you have the EDMX and generated DatabaseContext context, add a file with:
public partial class DatabaseContext
{
protected DatabaseContext(string nameOrConnstring) : base(nameOrConnstring) { }
}
it will add a new overload, it will expose the base DbContext constructor that takes the connstring.
Then add another file to that:
public class ANewContext : DatabaseContext
{
public ANewContext() : base(DbContextHelper.FindMyConnectionString()){ }
}
and that's all. Since your helper was static anyways, then we can call it like that. Just change it to return the connstring props, which it had needed to determine anyways.
Now rename the classes:
DatabaseContext -> InternalDatabaseContextDontUseMe
ANewContext -> DatabaseContext
or something like that, and I bet noone will be ever confused as to which one of them should be used everywhere. Usage:
public void SomeMethod()
{
using(var db = new DatabaseContext()) // that's ANewContext after renaming
{
...
}
}
With partial in the InternalDatabaseContextDontUseMe, you will be able to regenerate the model, and the extra added ctor will not be deleted. With one extra inheritance level, the autogenerated default constructor will be hidden, and devs using the derived class will not be able to accidentally call it, they'll receive new default ctor that will do what's needed.
If you are really interested in hearing about what I found while digging in the EF source, LazyContext, Factories, Resolvers and etc, look at this article of fine. I put there everything I could recall today and while it's somewhat chaotic, it may help you if you like digging&decompiling. Especially the EntityConnection-DbProviderFactory-Resolvers mentioned at the end.

Categories

Resources