Getting new DbContext when unit testing with Asp.Net Boilerplate - c#

I'm working on a project built on Asp.Net Boilerplate, and now I have to unit test the services using the real repositories with a real database connection (no mocking). I've been using the last post of BringerOd in https://gist.github.com/hikalkan/1e5d0f0142484da994e0 as a guide for setting up my UnitOfWorkScope instance. So, my code currently looks something like this:
IDisposableDependencyObjectWrapper<IUnitOfWork> _unitOfWork;
[TestInitialize]
public void SetUpService()
{
//initialize service
_unitOfWork = IocManager.Instance.ResolveAsDisposable<IUnitOfWork>();
UnitOfWorkScope.Current = _unitOfWork.Object;
UnitOfWorkScope.Current.Initialize(true);
UnitOfWorkScope.Current.Begin();
}
[TestCleanup]
public void CleanUpService()
{
UnitOfWorkScope.Current.Cancel();
_unitOfWork.Dispose();
UnitOfWorkScope.Current = null;
}
This works like a charm for the first unit test, but when I try to make a repository call in a second test, I get: "The operation cannot be completed because the DbContext has been disposed."
My guess is that when the TestInitialize method runs again, the unit of work scope is getting assigned with the same (disposed) DbContext, rather than a new one. I suppose, inside my actual test methods, I could set up my UnitOfWorkScope inside a using block with the IUnitOfWork. However, I really don't want to repeat that logic inside inside every single test. Does anyone know how to manually get the effect of a using block so that I get a brand new DbContext each time?

Check: http://aspnetboilerplate.com/Pages/Documents/Repositories
You must mark the calling method with [UnitOfWork] attribute.
The reason for this, as explained in the linked document is
When you call GetAll() out of a repository method, there must be an open database connection. This is because of deferred execution of IQueryable<T>. It does not perform database query unless you call ToList() method or use the IQueryable<T> in a foreach loop (or somehow access to queried items). So, when you call ToList() method, database connection must be alive. This can be achieved by marking caller method with the [UnitOfWork] attribute of ASP.NET Boilerplate. Note that Application Service methods are already using [UnitOfWork] as default, so, GetAll() will work without adding the [UnitOfWork] attribute for application service methods.

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.

Managing DbContext lifetime in ASP.NET Core SignalR

I have implemented an ASP.Core SignalR Application.
A shared hub class is invoking a signal to all its client on every 10 seconds
from class SharedHub (this class is not inherited from Hub it has logic to get IHubContext to invoke)
public void Tick(){
var time = _context.table.time;
invoke('tick', time.tick);
}
Also in the same class once a new connection established a method called to update database
public void UpdateSocketConnection(int connectionId){
var connection =_context.connection;
connection.id = connectionId;
_context.saveChanges();
}
Problem with this implementation is if the connection is currently calling Tick() method and also a client connected the same time. _context throws an error saying:
_context in use.
(I'll update exact error message once I reproduce).
What I have done ?
I have implemented a factory method to get a new instance of _context on top of every method
public void Tick(){
var time = factory.GetContext().time;
invoke('tick', time.tick);
}
public void UpdateSocketConnection(int connectionId){
var context = Factory.getContext();
var connection =context.connection;
connection.id = connectionId;
context .saveChanges();
}
This actually solved the problem. But it seems not the right thing to do. I am not sure of the performance when getting a new context every time on top of every method. this seems bad practice.
I want to know what are the possible implementation for this scenario.
In the first approach DbContext is shared between operations at the same time and it cause error and unexpected result. To avoid create and dispose DbContext every time in the second approach, DbContextPooling can help performance.
A pool of reusable instances can be created. Instead of disposing an instance, it returns to the pool and resets the instance to its default state. So instead of creating a new instance every time, the code will first check if there is an instance available in the pool or not.
You can enable DbContextPooling in Configure method in startup class:
services.AddDbContextPool<YourContext>(options => options.UseSqlServer(connection));
The default pool size value is 128. Read this article for more info.

How to "unhost" from HostingEnvironment

I am currently writing Unit tests fo ASP Net MVC application, and one of the things I have to mock is an HTTP Request.
For this I use HttpSimulator library (Subtext.TestLibrary).
I call SimulateRequest() method, and one of things it does is it creates a HostingEnvironment object.
_httpSimulator = new HttpSimulator().SimulateRequest(new Uri());
Before simulating HTTP request, I have to use the value of HostingEnvironment.IsHosted property in my code (in condition statement).
All works fine for the first test, because I get the proper value for HostingEnvironment.IsHosted (which is FALSE).
However, when the SimulateRequest() creates a HostingEnvironment object, HostingEnvironment.IsHosted becomes TRUE, so when the second test runs I receive a TRUE value, which leads to wrong results (or exception).
HostingEnvironment environment = new HostingEnvironment();
Therefore I am trying to find a way to reset HostingEnvironment.IsHosted back to FALSE. This property, however, is read-only.
I also cannot see any method, which would reset this property (I expect either Terminate, Finalize or whatever else it could be).
It sounds like your tests are too tightly coupled with multiple tests using the same instance of HostingEnvironment().
Each test should typically be executed independently of one another so that the results of e.g. Test 1 do not have any effect on the results of e.g. Test 2.
Just use separate instances of HostingEnvironment():
[Test]
public void InitializeContext()
{
using (HttpSimulator simulator = new HttpSimulator())
{
//Test #1...
}
}
[Test]
public void Simulator_Assigns_CurrentContext()
{
using (HttpSimulator simulator = new HttpSimulator())
{
//Test #2...
}
}

Quartz.NET job instance created but execute never called

I am using 2.2.4.400 of http://www.quartz-scheduler.net/. It is successfully working in other projects but not in this new one which is:
ASP.NET MVC 3, SQL Server back end
Based on ASP Boilerplate template (http://aspnetboilerplate.com/)
If I put a break point in the constructor for the class:
public class EmailQueueProcessJob : IJob
it is hit, however the execute never gets called. I have a break point placed at the top of:
public void Execute(IJobExecutionContext aContext)
{
}
Looking in the database I can see the jobs being created that they are supposedly "EXECUTING" but my Execute method is never called.
There is no code in the constructor, but clearly there must be an exception or something happening somewhere to prevent the execution. Any ideas on how I can track this down?
The solution ended up being to configure common logging and then have a look to see what is going wrong. The following will log to the trace window in VS. In my case there was an exception in one of the IJob constructor.
using colAlias = Common.Logging;
//Setup logging
colAlias.LogManager.Adapter = new colAlias.Simple.TraceLoggerFactoryAdapter { Level = colAlias.LogLevel.Info };

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