I am using EF6 in Windows Service / Console Application. I have successfully implemented IOC/DI for my Business layer interfaces and implementation. Using Constructor Injection. Also I am using Object Database, Task Parallel Library. for better performance and I am happy with it.
Also using System.IO.Abstractions to make my code more testable.
EF6 creates POCO clases for all the domain entities using .tt files which are quite handy. In order to perform Database queries I am writing every where
using(var db = new MyContext())
{
// code reading from/writing to database
...
...
}
I know it is not right practise and makes noise in my code various places. I want to make it loosely coupled. Now for my database operations - I am confused how to go forward to make it more testable and loosely coupled..Can anyone point me to a good example,article which can be refereed to.
2 major things I want to achive is to have more control over
Connection string configuration (for various servers deployment) and to have DbContext
very loosely coupled within my code.
DbContext should not be used as a singleton because it is holding a connection object which cannot be used by multiple threads at the same time.
This example registers a DbContext subclass called ApplicationDbContext as a scoped service in the ASP.NET Core application service provider (a.k.a. the dependency injection container). The context is configured to use the SQL Server database provider and will read the connection string from ASP.NET Core configuration.
DbContext generally represents a database connection and a set of tables. DbSet is used to represent a table.
To address decoupling (and testing), you can create your own interface for your DbContext
(IMyDbContext
), and re-expose all the typed entity DbSets
, SaveChanges()
, and possibly a few other methods. You should also make this interface Disposable
.
public interface IMyDbContext : IDisposable
{
IDbSet<Foo> Foos { get; set; }
IDbSet<Bar> Bars { get; set; }
int SaveChanges();
DbEntityEntry<T> Entry<T>(T entity) where T : class;
}
(You might also consider read only and read-write versions of the interface)
Then change your concrete DbContext
to implement this interface. You are now reasonably decoupled from the DbContext
(for Unit Testing, etc), but still have access to the usefulness of IQueryable
, the inherent unit of work, and caching offered by the DbContext
.
Then, here's two options for injecting the IMyDbContext
into your business / service classes
IDbContext
OR
DbContext
s, and then do constructor injection of IMyDbContextFactory
factory interface (you'll want the interface, not the concrete factory, again for Mocking test purposes).The choice here depends on what you need to do with your DbContext
. #1 can be tricky to configure in the IoC container, as you need to hand off lifespan management to the container. But this can be beneficial in web apps if it can be configured new instance per-request, so that if the request (assumed single thread) can use it as a cache.
Personally I prefer #2, as this allows direct management of the context:
using(var db = _myContextFactory.CreateDB())
{
db.SaveChanges();
}
But obviously, we then lose any potential benefit of long-lived contexts such as caching. But there are many other alternative technologies for caching, if needed.
One caveat : DbContext
isn't at all thread safe - if you are using TPL, make sure that each task obtains its own instance of DbContext
- e.g. use the localinit
overloads of Parallel.For
/ ForEach
to instantiate it when using same.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With