How to use DbContext with DI in desktop applications? - c#

I've been working on several non-web applications with Entity Framework and always it was struggling for me to find a correct approach for implementing Generic Repository with DbContext.
I've searched a lot, many of articles are about web applications which have short-living contexts. In desktop approaches I can't find suitable method.
One approach is DbContext per ViewModel but I don't agree with coupling View with Repository layers.
Another one is using using clause this way:
using(var context = new AppDbContext())
{
// ...
}
but this way we will not have Unit of Work and also can't use IoC Containers.
So what is the best practice for using DbContext in desktop applications?

A DbContext is meant to be short-lived: it represents a unit-of-work in itself. If you need long-term object state management then you can use the ObjectStateManager in Entity Framework directly.
For ensuring access to a DbContext, add an interface IDbContextFactory<TDbContext> (or just IMyDbContextFactory if you only have a single DbContext type) and inject that into your ViewModels and use a short-lived DbContext from it:
interface IDbContextFactory<TDbContext>
where TDbContext : DbContext
{
TDbContext Create();
}
// Configure:
void ConfigureServices( YourContainer container )
{
container.RegisterSingleton( IDbContextFactory<YourDbContextType1>, // etc );
container.RegisterSingleton( IDbContextFactory<YourDbContextType2>, // etc );
container.RegisterSingleton( IDbContextFactory<YourDbContextType3>, // etc );
}
// Usage:
public class LongLivedViewModel
{
private readonly IDbContextFactory<YourDbContextType3> dbFactory;
public LongLivedViewModel( IDbContextFactory<YourDbContextType3> dbFactory)
{
this.dbFactory = dbFactory ?? throw new ArgumentNullException(nameof(dbFactory));
this.DoSomethingCommand = new RelayCommand( this.DoSomethingAsync )
}
public RelayCommand DoSomethingCommand { get; }
public async RelayCommand DoSomethingAsync()
{
using( YourDbContextType3 db = this.dbFactory.Create() )
{
// do stuff
await db.SaveChangesAsync();
}
}
}

Entity Framework Core has a built in IDbContextFactory interface.
If using SQL Server, for instance, you declare the following in the ConfigureServices method (which in WPF is generally put in App.xaml.cs).
private static void ConfigureServices(IServiceCollection services)
{
services.AddDbContextFactory<MyDbContext>(
options =>
options.UseSqlServer(MyConnectionString));
}
Make sure MyDbContext exposes this constructor:
public class MyDbContext : DbContext
{
public MyDbContext(DbContextOptions<MyDbContext> options)
: base(options)
{
}
}
After that use constructor injection in the class that will be using the context (which could be either in the ViewModel layer or the Model layer, depending on your architecture):
private readonly IDbContextFactory<MyDbContext> _contextFactory;
public ModelClass(IDbContextFactory<MyDbContext> contextFactory)
{
this._contextFactory = contextFactory;
}
public void DatabaseOperationMethod()
{
using (var context = this._contextFactory.CreateDbContext())
{
// Do database stuff
}
}

Related

ASP.Net MVC single controller with multiple repositories

I have a data access layer which returns repositories.
For example, I have the following repository interfaces:
I have Entity Framework implementations of these repositories. These implementations get injected at a runtime with Ninject.
I have One controller with multiple repositories given below
IUploadRepository _uploadRepository;
ISalesRepository _salesRepository;
ITRSalesRepository _trsalesRepository;
ILocalPurchaseRepository _localRepository;
with single controller named -HomeController
In order to functional operation , I need to use DBContext into implementation thats why I implement all those interface like given below:
public class UploadRepository : IUploadRepository
{
private readonly XMANEntities _entities;
public UploadRepository(XMANEntities entities)
{
_entities = entities;
}
*here goes all implementation with context for specific*
}
Here now I have a constructor within a HomeController which looks this:
public class HomeController : Controller
{
private IUploadRepository uploadRepository;
public HomeController()
{
this.uploadRepository = new UploadRepository(new XMANContext());
}
public HomeController(IUploadRepository uploadRepository)
{
this.uploadRepository = uploadRepository;
}
}
How can I use others in this controller.Is it bad practice to inject multiple repo's into a controller?
i have tried this way like given below:
public HomeController() : this(new UploadRepository(
new XMANEntities()), new SalesRepository(new XMANEntities()),
new TRSalesRepository(
new XMANEntities()), new LocalPurchaseRepository(new XMANEntities()))
{
}
public HomeController(UploadRepository uploadRepository, SalesRepository salesRepository,
TRSalesRepository trsalesRepository, LocalPurchaseRepository localPurchaseRepository)
{
this.uploadRepository = uploadRepository;
this.salesRepository = salesRepository;
this.trsalesRepository = trsalesRepository;
this.localPurchaseRepository = localPurchaseRepository;
}
It's perfectly acceptable to inject multiple dependencies on an object. However, avoid it as much as possible (Except for transactional objects) to prevent mixing of responsibilities. An approach that you can look at is stored procedures.
private IUploadRepository _uploadRepository;
private ISalesRepository _salesRepository;
private ITRSalesRepository _trsalesRepository;
private ILocalPurchaseRepository _localRepository;
public HomeController(
IUploadRepository uploadRepository,
ISalesRepository salesRepository,
ITRSalesRepository trsalesRepository,
ILocalPurchaseRepository localRepository
)
{
this._uploadRepository = uploadRepository;
this._salesRepository= salesRepository;
this._trsalesRepository= trsalesRepository;
this._localRepository= localRepository;
}

Funq: Register the same object multiple times and using an identifier (from session) to resolve them

What I currently have:
I have a simple dictionary of type string, DbContext
I am registering my Dictionary like this
container.Register<IDictionary<string, DbContext>>(x => dbContexts).ReusedWithin(ReuseScope.Request);
and using the dictionary again by simply injecting it in my constructors. So I am always injecting the whole dictionary with every DbContext in it. Later I then get the right DbContext by using an identifier, stored in my usersession. So using a DbContext looks like this:
private readonly IDictionary<string, DbContext> _dbContexts;
public FooService(IDictionary<string, DbContext> dbContexts)
{
_dbContexts = dbContexts;
}
public void Bar()
{
var userSession = GetSession();
var data = _dbContexts[userSession.TargetConnectionIdentifier].Table.ToList();
}
What I want
I want to inject only one DbContext into my classes. Using the same property from my session as an identifier. I have seen that Funq offers methods to register instances with a name (e.g. RegisterAs<>()). But I am not quite sure how to use them correctly.
private readonly DbContext _dbContext;
public FooService(DbContext dbContext)
{
_dbContext = dbContext;
}
I want that Funq automatically resolves the right object for me, depending on what value a specific property from my session has.
If anybody knows an answer I would highly appreciate it.
The IOC doesn't have access to the runtime Request context so you couldn't do this within the IOC.
My approach would be to register a factory with the db contexts, e.g:
container.Register<IDbContexts>(c => new DbContexts(dbContexts));
Then you could have a base class that provides a helper to access the DbContext behind a helper, e.g:
public class ServiceBase : Service
{
public IDbContexts DbContexts { get; set; }
DbContext dbContext;
public DbContext DbContext => dbContext ?? (dbContext = DbContexts.Get(GetSession()));
}
Or you could use Extension methods if you don't want a base class, e.g:
public static class MyServiceExtensions
{
public static DbContext GetDbContext(this Service service)
{
var dbContexts = service.TryResolve<IDbContexts>();
return dbContexts.Get(service.GetSession());
}
}
Then lazily load it in your Service class like:
DbContext dbContext;
public DbContext DbContext => dbContext ?? (dbContext = this.GetDbContext());

IOption pattern - unit testing and passing through layers

I have some code (C# .Net Core WebAPI) I wish to unit test but need some help as the dependencies looks a bit odd to me.
The code came from some sample code (I found on the web) for accessing MongoDb using .Net Core WebAPI, which initially looked ok, until now..
Both the DbContext and the Repository have the same dependency - and the Repository just passes it through to the DbContext anyway - as the Repository instantiates the DbContext:
public class LogItemRepository : ILogItemRepository
{
private readonly DbContext _context = null;
public LogItemRepository(IOptions<DbSettings> settings)
{
_context = new DbContext(settings);
}
...
public class DbContext
{
private readonly IMongoDatabase _database = null;
public DbContext(IOptions<DbSettings> settings)
{
var client = new MongoClient(settings.Value.ConnectionString);
if (client != null)
_database = client.GetDatabase(settings.Value.Database);
}
public IMongoCollection<LogItem> LogItemsCollection
{
get
{
return _database.GetCollection<LogItem>("LogItem");
}
}
}
}
I'm not familiar with the Options pattern, but from a quick read it looks good. But I'm not convinced it's good practice to make child dependencies (the options), dependencies of the parent (as in the example above).
Instead should I be making an interface, IDbContext, and using that as the dependency for the repository? That's what I would have done in the past - but not sure if this breaks the options pattern.
I suspect this is subjective, but I'd like some others input.
Thanks
Tim
While primarily opinion based, common practice is to not instantiate the db context within the constructor of the repository. That tightly couples the repository to the context. Inject an abstraction as you stated in your OP.
I may be splitting hairs here but there is still too much tight coupling in the example provided.
First abstract the context
public interface IDbContext {
IMongoCollection<LogItem> LogItemsCollection { get; }
}
and also have IMongoDatabase be an explicit dependency
public class DbContext : IDbContext {
private readonly IMongoDatabase database = null;
public DbContext(IMongoDatabase database)
this.database = database;
}
public IMongoCollection<LogItem> LogItemsCollection {
get {
return database.GetCollection<LogItem>("LogItem");
}
}
}
configure service with what ever options are needed at the composition root (Startup). You would even consider encapsulating it in an extension method.
services.AddScoped<IMongoDatabase>(provider => {
var settings = provider.GetService<IOptions<DbSettings>>();
var client = new MongoClient(settings.Value.ConnectionString);
return client.GetDatabase(settings.Value.Database);
});
services.AddScoped<IDbContext, DbContext>();
services.AddScoped<ILogItemRepository, LogItemRepository>();
//...NOTE: Use the desired service lifetime. This is just an example
That now leaves the repository to be explicitly dependent on the context abstraction
public class LogItemRepository : ILogItemRepository {
private readonly IDbContext context = null;
public LogItemRepository(IDbContext context) {
this.context = context;
}
//...other code
}
All layers are now decoupled and explicitly state what their dependencies are, allowing for more isolated unit tests to be done as needed.

DbContext with Ninject ADO.NET

I am working on a big project that 80% completed (Some features need to be implemented though).But recently we discovered that the project doesn't allow concurrent requests (I mean multiple users request to same repository). Sometime we get null referece & sometimes "Executed can not open available connection , connection state is closed" etc.
Our source code is strongly restricted outside of the world. Here is some code.Let me know if there is any architectural problem, as architectural guys left company. It's using ninject 3.0. I already used InRequestScope() for all manager's repositories but no luck
Update: I am not using any ORM here, I am trying to connect SqlServer through data adapter in my DbContext class
public class DbContext
{
//execute query , nonquery etc using adapter & datatable
//Example
var dt=new DataTable();
_adapter=new _dbfactory.CreateAdapter();
_adapter.Fill(dt);
return dt;
}
//MyController
public class MyController
{
private readonly IMyManager_iMyManager;
public MyController(IMyManager iMyManager){_iMyManager=iMyManager}
public ActionResult Save()
{
_iMyManager.Save()
}
}
// My Manager
public class MyManager:IMyManager
{
private readonly IMyRepository _iMyRepository;
DbContext _dbContext=new
DbContext("someParameter","connectionstring");
public MyManager
(
IMyRepository iMyRepository, DbContext dbContext
)
{
_iMyRepository=iMyRepository;
_dbContext=dbContext;
}
Public DataTable GetDataTable()
{
try
{
_dbContext.Open();
_iMyRepository.GetDataTable()
}
catch(Exception ex){}
finally{_dbContext.Close()}
}
}
// here is the repository
Public class MyRepository:IMyRepository
{
public _dbContext;
public MyRepository(DbContext dbContext)
{
_dbContext=dbContext;
}
public DataTable GetDataTable()
{ return _dbContext.ExecuteQuery()}
}
Finally Here is our ninject binding
public class NinjectDependencyResolver()
{
var context=new DbContext("someparameter","connectionStrin");
kernel.Bind<IMyManager>().To<MyManager>().WithConstructorArgument("_dbContext",context);
kernel.Bind<IMyRepository >().To<MyRepository >().WithConstructorArgument("_dbContext",context);
}
there can have some typo in my code as I wrote everything in so editor
I think you did this too complicated in Ninject Dependency Resolver.
You shouldn't create DbContext with a new keyword. Instead you should make Ninject to be resolving DbContext in request scope or in thread scope.
To register DbContext you can do it like this:
kernel.Bind<DbContext>().To<MyDbContext>().WithConstructorArgument("someArgument", "someValue").InRequestScope();
kernel.Bind<IMyManager>().To<MyManager>().InRequestScope();
kernel.Bind<IMyRepository>().To<MyRepository>().InRequestScope();
You don't need to precise the constructor argument to DbContext as DbContext is only once registered in the Ninject.
You can also register DbContext to a DbContextProvider class and there you can add some specific logic to resolve object.
Example:
kernel.Bind<DbContext>().ToProvider<MyDbContextProvider>().InRequestScope();
internal class MyDbContextProvider : Ninject.Activation.IProvider
{
public object Create(IContext context)
{
return new MyDbContext("connectionStringArgument";
}
public Type Type { get { return typeof (MyDbContext); } }
}
I hope this helps.
You need to remove this initialization in the MyManager since you pass the initialized DbContext via IoC.
DbContext _dbContext=new
DbContext("someParameter","connectionstring");
You also need to remove the finally block in the GetDataTable in the MyManager class since as a rule of thumb, if the object is initialized via IoC, it should be destroyed by IoC as well.
finally{_dbContext.Close()}
If you are initializing something in the field level then why would you initialize it again from the constructor?
private readonly IMyRepository _iMyRepository;
DbContext _dbContext=new DbContext("someParameter","connectionstring");
public MyManager(IMyRepository iMyRepository, DbContext dbContext)
{
_iMyRepository=iMyRepository;
_dbContext=dbContext;
}
This may also be a typo. Either remove the _dbContext initialization from the constructor or delegate the task of initialization to the caller of this class.
Multiple initialization can also be the problem. since you are doing dbcontext initialization both in NinjectDependencyResolver() and MyManager. For this you are getting two different exceptions. This is a platform design issue i guess
Two problems:
// My Manager
public class MyManager:IMyManager
{
private readonly IMyRepository _iMyRepository;
DbContext _dbContext=new
DbContext("someParameter","connectionstring");
public MyManager
(
IMyRepository iMyRepository, DbContext dbContext
)
{
_iMyRepository=iMyRepository;
_dbContext=dbContext;
}
The new that is created for the field will be overwritten when the constructor is called.
public class NinjectDependencyResolver()
{
var context=new DbContext("someparameter","connectionStrin");
kernel.Bind<IMyManager>().To<MyManager>().WithConstructorArgument("_dbContext",context);
kernel.Bind<IMyRepository >().To<MyRepository >().WithConstructorArgument("_dbContext",context);
}
You create the context here once and pass it to each object creation. So you are still reusing the context object instead of creating it for each request scope.

Separating EF from the BLL

I have the following piece of database transaction wrapper written :
public class ExampleWrapper : IDisposable
{
public DbContextTransaction Transaction { get; set; }
public DcatContext DatabaseContext { get; set; }
public ExampleWrapper ()
{
DatabaseContext = new someContext();
Transaction = DatabaseContext.Database.BeginTransaction();
}
public void Dispose()
{
Transaction.Dispose();
}
public void Commit()
{
Transaction.Commit();
}
public void RollBack()
{
Transaction.Rollback();
}
}
I have code something similar to the following in my DAL :
public async Task<Object> Retrieve(string Id)
{
using (var context= new SomeContext())
{
return Context.Object.Find(id);
}
}
I am trying to keep the business layer separate from any Entity Framework dependency and that's the reason for the wrapper Class. If I am trying to do a transaction from the BLL for example:
using(var wrapper = new ExampleWrapper())
{
//make calls to the DAL
something.Retrieve(Id)
}
My question is I am initializing the Dbcontext twice (Here while creating a transaction, as well as in the DAL) . Is there any suggestion any of you have how I can do this better? .
PS: Just using retrieve in a transaction as an example .
You can use Dependency Injection (e.g Ninject) to inject the DbContext to the
Wrapper. Then you can define the lifetime of the DBContext instances. If it is a web application, then you can define the lifetime of the DbContext to be within the life of the Web Request (InRequestScope), so that it will automatically dispose of the DbContext after the Web Request ends.
You would no longer require creating the DbContext, since Ninject creates it for you and manages its disposal.

Categories