Unit testing with Effort and SQL CE in parallel fails - c#

I'm evaluating unit tests using EF6 in combination with
http://effort.codeplex.com/ and
SQL CE Server (Local DB file)
http://www.codeproject.com/Articles/460175/Two-strategies-for-testing-Entity-Framework-Effort was a quite good reference but now I'm stuck.
I have 2 test projects (one for Effort and the other for SQL CE). If I'm running both separately everthing's fine. Running both in a row with the ReSharper test runner the last test project always fails. Either
System.InvalidOperationException : The Entity Framework was already
using a DbConfiguration instance before an attempt was made to add an
'Loaded' event handler. 'Loaded' event handlers can only be added as
part of application start up before the Entity Framework is used.
or
System.InvalidOperationException: The default DbConfiguration instance
was used by the Entity Framework before an attempt was made to set an
instance of 'SqlCeConfiguration'.The 'SqlCeConfiguration' instance must
be set at application start before using any Entity Framework features
or must be registered in the application's config file.
It's always the same. The successor inherits the DbConfiguration instance from the predecessor. How can I run both test projects / configuration without side effects?
Here's my DbContext class:
public class DataContext : DbContext
{
public DataContext(string connectionString) : base(connectionString)
{ Configuration.LazyLoadingEnabled = false; }
public DataContext(DbConnection connection) : base(connection, true)
{ Configuration.LazyLoadingEnabled = false; }
}
That's the test fixture with Effort:
[TestFixtureSetUp]
public void TestFixtureSetup()
{
EffortProviderConfiguration.RegisterProvider();
var connection = DbConnectionFactory.CreateTransient();
var dbContext = new DataContext(connection);
...
}
That's the test fixture with SQL CE:
[TestFixtureSetUp]
public void TestFixtureSetup()
{
const string filePath = #"LocalDb.sdf";
var connectionString = string.Format("Data Source={0}; Persist Security Info=False;", filePath);
DbConfiguration.SetConfiguration(new SqlCeConfiguration());
var dbContext = new DataContext(connectionString);
dbContext.Database.Create();
...
}
and the my SqlCeConfiguration:
public class SqlCeConfiguration : DbConfiguration
{
public SqlCeConfiguration()
{
SetProviderServices(SqlCeProviderServices.ProviderInvariantName, SqlCeProviderServices.Instance);
SetDefaultConnectionFactory(new SqlCeConnectionFactory("System.Data.SqlServerCe.4.0"));
}
}
Thank you so much!
Marcel

We were seeing the same errors when running all UnitTests on our build server or when running all UnitTests locally.
System.InvalidOperationException : The Entity Framework was already
using a DbConfiguration instance before an attempt was made to add an
'Loaded' event handler. 'Loaded' event handlers can only be added as
part of application start up before the Entity Framework is used.
Once we moved the Effort Provider registration code from the [TestInitialize] method to the AssemblyInitialize method, everything started working. Based on the message in the reported error, it appears that the registration cannot happen more than once.
[AssemblyInitialize()]
public static void AssemblyInit(TestContext context)
{
Effort.Provider.EffortProviderConfiguration.RegisterProvider();
}

I came across this just now and thought I'd share the cause of my issue.
Everything was working dandy, until I implemented and ActionFilter.
It turned out that in my test the Effort code was running after the instantiation of my web site. Instantiating the web site instantiated my filter, my filter requested a DataContext from the container.
Hence someone had already made use of the datacontext before I tried to configure it with Effort.
Hope this might help someone in the future, albeit it being a bit of a different cause, and I hope you solved your issue int the end!

Related

Where do you call Context.SaveChanges if used in a service

Im building a .Net WebApi and I abstract the logic away so I can use unit tests to test it. I use Entity Framework and the DbContext is injected by DI.
The flow of data is like follows:
[controller] -> [Service] -> [Controller]
To Unit test the Service I mock the DbContext and everything else and test the Service
[Test] -> [Service] -> [Test]
Nothing too excited so far.
Now, if for example I want to add a Point Of Interest then in the service I do something like this:
public class CreatePoiService{
public void InsertPoi(PoiData data){
DbContext.Pois.Add(data);
DbContext.SaveChanges();
}
}
No problem with that. But now I have a Console application that imports 100000 Points of interests. And while this Console Application makes use of the same InsertPoi function, the DbContext.SaveChanges() is called after every insert, and this slows things down because its better to do after N inserts.
So Ive added a function to the Service class:
public class CreatePoiService{
private bool SaveToContext = true;
public void InsertPoi(PoiData data){
DbContext.Pois.Add(data);
if(SaveToContext) DbContext.SaveChanges();
}
}
Now I can call in the Console Application CreatePoiService.SaveToContext = false to not have EF execute the changes. And I can call the SaveChanges in the Console Application itself. Which works great. But still I doubt if there are better ways to do this?
The normal pattern is that your services will call SaveChanges, but you can still scope a transaction across multiple service calls.
The transaction will control the durability of all the changes saved by the services because they all share a single DbContext instance as a Scoped dependency.

IDesignTimeDbContextFactory not always needed?

Previously i had to implement IDesignTimeDbContextFactory to get migrations running, e.g:
PM > Add-Migration Initial
PM > Update-Database
If not, the console threw an error and led me here: https://learn.microsoft.com/en-us/ef/core/miscellaneous/configuring-dbcontext#use-idesigntimedbcontextfactory.
So i did what it suggested and got migrations running.
After that i have created new projects, and i didn't have to implement the IDesignTimeDbContextFactory. Migrations worked anyway. How is this possible?
Same .NET Core version (2.0) on all of the projects.
Do we always need to create a IDesignTimeDbContextFactory, or is it just in certain situations?
Thanks!
Okay, here it goes:
If you have a default constructor in your DbContext or are using the Program.BuildWebHost() pattern established in the ASP.NET Core 2.0 project templates, you typically won't need an IDesignTimeDbContextFactory implementation.
In 2.0.0-preview1 Program.BuildWebHost() wasn't used and you needed a design-time factory.
See this thread for full discussion:
https://github.com/aspnet/EntityFrameworkCore/issues/9033
As Dan Banan stated, if a DbContext has a default constructor, it won't need an IDesignTimeDbContextFactory at design time. However, if it needs to be configured from Startup, it will need a constructor which takes accepts DbContextOptions<T> and invokes the corresponding base constructor. However, this will create another issue. The DbContext's OnConfigure method will be called regardless of which constructor is used.
To account for these details, I've found the following pattern to be useful:
DbContext Configuration and Dependency Injection
serviceCollection
.AddDbContext<MyDbContext>(
options => options.UseSqlServer(configuration.GetConnectionString("MyDb"),
ServiceLifetime.Transient
);
MyDbContext.cs
public class MyDbContext : DbContext
{
public SureshotDbContext()
{
// This allows for instantiation at design time.
}
public MyDbContext(DbContextOptions<MyDbContext> options) :
base(options)
{
// This allows for configuration in real applications.
}
protected override void OnConfiguring(
DbContextOptionsBuilder optionsBuilder
)
{
if (optionsBuilder.IsConfigured)
{
return;
}
optionsBuilder.UseSqlServer(nameof(TDbContext));
}
}

Unable to cast object of type 'System.Data.ProviderBase.DbConnectionClosedConnecting' to type 'System.Data.SqlClient.SqlInternalConnectionTds

I am getting the following error on the first db access after the application starts - "Unable to cast object of type 'System.Data.ProviderBase.DbConnectionClosedConnecting' to type 'System.Data.SqlClient.SqlInternalConnectionTds"
The error only thrown once, at the first method tries to read data from the database, after the application starts.
Re-calling the same method for the 2nd time and further, everything works fine.
Using .net core 1.1 with Entity Framework
I recently had this same exception in an ASP.NET Core 2 app with EF Core. In my case, the root cause was a problem with the scopes my dependency-injected DbContext. I had a controller and a service both using an injected DbContext. The service was a singleton, like this:
public class TestService{
public TestService(FooDbContext db)
{
this.db = db;
}
}
public class FooController{
public FooController(FooDbContext db, TestService testService)
{
this.testService = testService;
this.db = db;
}
}
public class Startup{
public void ConfigureServices(IServiceCollection services){
//...
services.AddDbContext<FooDbContext>(options =>
options
.UseSqlServer(Configuration.GetConnectionString("FooDbContext"))
);
services.AddSingleton<TestService>();
}
}
So the controller would use it's instance, and then if the singleton service also tried to use it's own instance, then it would give the error above about 90% of the time. I'm a little fuzzy on why this would be an issue, or be intermittent, but it became pretty clear in debugging that EF was reusing some underlying resources. I didn't dig into EF code debugging, but I suspect the controller instance was closed, and the service instance reused the connection, expecting it to be open. In reading, others suggested MultipleActiveResultSet=true in the connection string would fix, but this did not resolve the issue in my case. In my case, the fix was to change the service to Transient in Startup.cs, which was acceptable in this case, and possibly better:
services.AddTransient<TestService>();

Writing data to two databases with EF within one Transaction Scope

We are using Entity Framework 6 and are dealing with the following requirement:
We are writing data to two different and independent databases let's call them Main and External, they both exist on the same instance of SQL Server and the connection string used to connect to them are identical except for the initial catalog value.
In the application each DB has its own EF DBContext.
The Main DB has an AuditLog table in which data changes get recorded.
Data changes that happen on the External DB need to be recorded on the AuditLog table within Main DB.
Because of certain restrictions that are not important to discuss the way we implemented this was to have ExternalContext to have a reference of MainContext in order to call MainContext.SaveAuditLogs whenever ExternalContext.SubmitChanges was called, like so (only showing relevant code):
public class ExternalContext : IDataContext
{
private readonly IAuditLogContext auditLogContext;
public ExternalContext (IAuditLogContext auditLogContext){
this.auditLogContext = auditLogContext;
}
public override void SaveChanges()
{
base.SaveChanges();
this.auditLogContext.SaveAuditLogs(auditLogs);
}
}
public class MainContext : IAuditLogContext
{
public void SaveAuditLogs(List<AuditLog> auditLogs)
{
this.Set<AuditLog>().AddRange(auditLogs);
this.SaveChanges();
}
}
An example of how this is being used:
public class SomeBusinessClass
{
private readonly IDataContext dataContext;
public SomeBusinessClass(IDataContext dataContext)
{
this.dataContext = dataContext;
}
public void SomeOperation(Entity someEntity)
{
.....
using(var scope = new TransactionScope())
{
this.dataContext.Insert(someEntity);
this.dataContext.SaveChanges();
}
scope.Complete();
}
}
For this to work the Distributed Transaction Coordinator service needs to be running. When tested on development environments it works fine but on QA environments it fails with the error message as if the Distributed Transaction Coordinator is not running even though it is.
Apparently this happens because in our development environments the DB Server and the Web Server are the same computer weather in QA they are two separate boxes and the DTC does not like it when there are multiple servers and the two operations run within a TransactionScope, if we remove the TransactionScope then it works fine on both environments but then there is the risk that if the AuditLog fails the whole transaction is not rolled back.
How can we make this work?
Thank you.

How can I use Sql CE 4 databases for functional tests

Due to the potential differences between Linq-to-Entities (EF4) and Linq-to-Objects, I need to use an actual database to make sure my query classes retrieve data from EF correctly. Sql CE 4 seems to be the perfect tool for this however I have run into a few hiccups. These tests are using MsTest.
The problem I have is if the database doesn't get recreated (due to model changes), data keeps getting added to the database after each test with nothing getting rid of the data. This can potentially cause conflicts in tests, with more data being returned by queries than intended.
My first idea was to initialize a TransactionScope in the TestInitialize method, and dispose the transaction in TestCleanup. Unfortunately, Sql CE4 does not support transactions.
My next idea was to delete the database in TestCleanup via a File.Delete() call. Unfortunately, this seems to not work after the first test is run, as the first test's TestCleanup seems to delete the database, but every test after the first does not seem to re-create the database, and thus it gives an error that the database file is not found.
I attempted to change TestInitialize and TestCleanup tags to ClassInitialize and ClassCleanup for my testing class, but that errored with a NullReferenceException due to the test running prior to ClassInitialize (or so it appears. ClassInitialize is in the base class so maybe that's causing it).
I have run out of ways to effectively use Sql CE4 for testing. Does anyone have any better ideas?
Edit: I ended up figuring out a solution. In my EF unit test base class I initiate a new instance of my data context and then call context.Database.Delete() and context.Database.Create(). The unit tests run a tad slower, but now I can unit test effectively using a real database
Final Edit: After some emails back and forth with Microsoft, it turns out that TransactionScopes are now allowed in SqlCE with the latest release of SqlCE. However, if you are using EF4 there are some limitations in that you must explicitly open the database connection prior to starting the transaction. The following code shows a sample on how to successfully use Sql CE for unit/functional testing:
[TestMethod]
public void My_SqlCeScenario ()
{
using (var context = new MySQLCeModelContext()) //ß derived from DbContext
{
ObjectContext objctx = ((IObjectContextAdapter)context).ObjectContext;
objctx.Connection.Open(); //ß Open your connection explicitly
using (TransactionScope tx = new TransactionScope())
{
var product = new Product() { Name = "Vegemite" };
context.Products.Add(product);
context.SaveChanges();
}
objctx.Connection.Close(); //ß close it when done!
}
}
In your TestInitialize you should do the following:
System.Data.Entity.Database.DbDatabase.SetInitializer<YourEntityFrameworkClass>(
new System.Data.Entity.Database.DropCreateDatabaseAlways<YourEntityFrameworkClass>());
This will cause entity framework to always recreate the database whenever the test is run.
Incidentally you can create an alternative class that inherits from DropCreateDatabaseAlways. This will allow you to seed your database with set data each time.
public class DataContextInitializer : DropCreateDatabaseAlways<YourEntityFrameworkClass> {
protected override void Seed(DataContext context) {
context.Users.Add(new User() { Name = "Test User 1", Email = "test#test.com" });
context.SaveChanges();
}
}
Then in your Initialize you would change the call to:
System.Data.Entity.Database.DbDatabase.SetInitializer<YourEntityFrameworkClass>(
new DataContextInitializer());
I found the approach in the "final edit" works for me as well. However, it's REALLY annoying. It's not just for testing, but any time you want to use TransactionScope with Entity Framework and SQL CE. I want to code once and have my app support both SQL Server and SQL CE, but anywhere I use transactions I have to do this. Surely the Entity Framework team should have handled this for us!
In the meantime, I took it one step farther to make it a little cleaner in my code. Add this block to your data context (whatever class you derive from DbContext):
public MyDataContext()
{
this.Connection.Open();
}
protected override void Dispose(bool disposing)
{
if (this.Connection.State == ConnectionState.Open)
this.Connection.Close();
base.Dispose(disposing);
}
private DbConnection Connection
{
get
{
var objectContextAdapter = (IObjectContextAdapter) this;
return objectContextAdapter.ObjectContext.Connection;
}
}
This makes it a lot cleaner when you actually use it:
using (var db = new MyDataContext())
{
using (var ts = new TransactionScope())
{
// whatever you need to do
db.SaveChanges();
ts.Complete();
}
}
Although I suppose that if you design your app such that all changes are committed in a single call to SaveChanges(), then the implicit transaction would be good enough. For the testing scenario, we want to roll everything back instead of calling ts.Complete(), so it's certainly required there. I'm sure there are other scenarios where we need the transaction scope available. It's a shame it isn't supported directly by EF/SQLCE.

Categories

Resources