I am currently working on a project with has a console app and few library projects. One library project is a EF code first project which contains my models and the context:
public class MyDbContext: DbContext
{
public MyDbContext() : base("MyConnectionString")
{
}
public DbSet<File> Files { get; set; }
}
I also have a singleton class through which I want to access the database. The singleton looks like this:
public sealed class DbLogger : IDbLogger
{
private static readonly DbLogger instance = new DbLogger();
private static MyDbContext ctx = new MyDbContext();
static DbLogger() {
Database.SetInitializer<MyDbContext>(new DbInitializer());
}
private DbLogger() { }
public static DbLogger Instance
{
get {
return instance;
}
}
public void AddFile(string fileName)
{
ctx.Files.Add(new File() { FullPath = fileName });
}
}
The db initializer is very simple and just implements the CreateDatabaseIfNotExists. Nothing is done in Seed yet.
In the console all which references the library project I just want to use it as:
private DbLogger logger = DbLogger.Instance;
and call the logger from a Task using:
logger.AddFile("myFileName");
When the app gets to logger.AddFile call I get the following exception:
An exception of type 'System.InvalidOperationException' occurred in
EntityFramework.dll but was not handled in user code
Additional information: The context cannot be used while the model is
being created. This exception may be thrown if the context is used
inside the OnModelCreating method or if the same context instance is
accessed by multiple threads concurrently. Note that instance members
of DbContext and related classes are not guaranteed to be thread safe.
How can I delay the using of the context until the model was created?
I am currently a bit stuck with this and any idea on how to solve this would be appreciated.
Thank you!
I recommend this approach
public void AddFile(string fileName){
using(var ctx = new MyDbContext() ){
ctx.Files.Add(new File() { FullPath = fileName });
ctx.SaveChanges();
}
}
You should only use the DbContext when needed. Open the DB connection, interact with the DB and close the connection. The using statement take care of the opening and closing of the DB connection.
EDIT - updated with SaveChanges()
ad to #Kunukn answer:
I think that you should blame
private static MyDbContext ctx = new MyDbContext();
It was trying to acces context before database initializer run.
If you don't wan't to create new context on every AddFile() call, try create context in static constructor.
I see problem with line
Database.SetInitializer(new DbInitializer());
if you use
public void AddFile(string fileName){
using(var ctx = new MyDbContext() ){
ctx.Files.Add(new File() { FullPath = fileName });
ctx.SaveChanges();
}
}
then your purpose of singleton is not getting solved because it will create a new MyDbContext every time AddFile is called ( and this is recommended)
but even if you insist on having a single dbcontext object then you should create some
initialization fucntion and might be call it after object is created.
might be something like
private DbLogger logger = DbLogger.Instance;
logger.Initialize()
Related
Working with EntityFramework and Ninject i need to dispose the context each time a call to a repository is finished. I need that so each time, a new call to the database is made, instead of using the EF context scope.
Here is my repository for testing:
public class VehicleRepositoryTest : IVehicleRepository
{
private DBEntities _context;
public VehicleRepositoryTest(DBEntities context)
{
_context = context;
}
....
public List<TB_VEHICULO> GetAll()
{
return _context.TB_VEHICULO.ToList();
}
And here is how i implement the ninject module. I use "IntransientScope" with the idea of disposing the context after each call:
Kernel.Bind<DBEntities>().ToSelf().InTransientScope();
Kernel.Bind<IVehicleRepository>().To<Test.VehicleRepositoryTest>().InTransientScope();
The idea is that each time a call "GetAll()" a new context is created, so each time a call to database is made.
But it is not working. If i make a call to "GetAll()", and supose i get data A; then i change in database data A to data B, make a new call to "GetAll()", i still getting data A.
More Info:
My application is a WinForms application, to call the instantiate the injected objects i use the composition pattern:
public static class CompositionRoot
{
public static IKernel kernel { get; private set; }
public static void WireModule(INinjectModule module)
{
kernel = new StandardKernel(module);
}
public static T Resolve<T>()
{
return kernel.Get<T>();
}
}
and the call to the repository is like this:
_vehicleRepository = CompositionRoot.Resolve<IVehicleRepository>();
var test = _vehicleRepository.GetAll();
I was with the same problem.
My old code:
kernel.Bind(typeof(IUnitOfWork)).To<UnitOfWork>().WithConstructorArgument("context", kernel.Get<MyContext>());
My new Code:
kernel.Bind<DbContext>().To<MyContext>();
kernel.Bind(typeof(IUnitOfWork)).To<UnitOfWork>();
This work to me.
I have tried to figure this out, but I am stuck.
I have a Net Core 2 application with Service/Repo/Api/Angular layers - but now I want to 'bolt on' a console application and access all the goodies I have already built up. I seem to be in a mess of static objects and DI and null parameters. Anyway, here is a simplified version of my code.
namespace SimpleExample
{
class Program
{
private static ApplicationDbContext _appDbContext;
public Program(ApplicationDbContext appDbContext)
{
_appDbContext = appDbContext;
}
static void Main(string[] args)
{
var instance = new Program(); // this doesn't work!
var instance = new Program(_appDbContext); // neither does this!
instance.GetData();
}
private void GetData()
{
Console.WriteLine("Let's read some data! Press a key to continue.");
Console.ReadLine();
var data = "my data";
var result = GetId(data);
}
private string GetId(string original)
{
var data = _appDbContext.Data
.Where(x => x.Name == original.Trim)
.FirstOrDefault();
return data;
}
}
}
I am getting the classic
'An object reference is required for the non-static field'
error. Then from investigating on here I changed things to static and then everything becomes null.
It's not just the DbContext I am trying to inject. I'm also trying to inject
private ManagerService _managerService;
but getting same errors.
Update
If I try
private static ApplicationDbContext _appDbContext = new
ApplicationDbContext();
as suggested a few times below, then I get the error
There is no argument given that corresponds to the required formal
parameter 'options' of
'ApplicationDbContext.ApplicationDbContext(DbContextOptions)'
OK, I have figured this out, and I'll post my answer for anyone else struggling in this situation.
When you launch the console app, your normal startup.cs doesn't execute, so you have to put a lot of that code in your console app.
private static SiteService _siteService;
private static ApplicationDbContext _appDbContext;
public static void Main()
{
var services = new ServiceCollection();
services.AddTransient<ISiteInterface, SiteRepo>();
services.AddTransient<SiteService>();
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer("blah-blah"));
var serviceProvider = services.BuildServiceProvider();
_siteService = serviceProvider.GetService<SiteService>();
_appDbContext = serviceProvider.GetService<ApplicationDbContext>();
GetData();
}
and now your _appDbContext will be available throughout the rest of your console app.
Hope that helps!
Basically, if you do not plan extensive usage of DbContext nor use DI, there is no need for ServiceProvider. Just remember to make DbContext instance short living and use it for single unit-of-work, not longer.
Your context may look like this:
using Microsoft.EntityFrameworkCore;
namespace YourNamespace;
public class ApplicationContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(#"Your conn string");
}
public DbSet<YourType> YourEntity { get; set; }
}
You can pass conn string by ApplicationContext ctor as well. This is nicely explained here by Microsoft .
Then you can utilise your ApplicationContext like this:
// Unit-of-work closed in using statement
// Here you can query/update your DbContext
using (var dbContext = new ApplicationContext())
{
var queryResult = dbContext.YourEntity.Where(....);
}
You can prepare number of such units-of-work as separate methods for querying a database.
Your repository service can consist of these methods.
Then you can instantiate the service as needed.
Let's say i've a DbContextFactory which I use in repositories to get DbContext
(I'm not sure it'a the best solution).
public class DbContextFactory : Disposable, IDbContextFactory
{
private readonly Dictionary<Type, System.Data.Entity.DbContext> _dbContexts;
public DbContextFactory()
{
_dbContexts = new Dictionary<Type, System.Data.Entity.DbContext>();
}
public T GetDbContext<T>() where T : System.Data.Entity.DbContext, new()
{
if (!_dbContexts.ContainsKey(typeof(T)))
{
_dbContexts.Add(typeof(T), new T());
}
return _dbContexts[typeof(T)] as T;
}
protected override void DisposeCore()
{
foreach (var kvpDbContext in _dbContexts)
{
kvpDbContext.Value?.Dispose();
}
}
}
And i have UnitOfWork which i inject in BusinessLogic class
public class UnitOfWork<T> : IUnitOfWork
where T : System.Data.Entity.DbContext, new()
{
private readonly IDbContextFactory _dbContextFactory;
private T _dbContext;
public UnitOfWork(IDbContextFactory dbContextFactory)
{
_dbContextFactory = dbContextFactory;
}
public T DbContext => _dbContext ?? (_dbContext = _dbContextFactory.GetDbContext<T>());
public void Commit()
{
DbContext.SaveChanges();
}
}
than i call repository method, and let's say it throws an exception:
public void CreateUser(User user)
{
_userRepository.Add(user);
throw new Exception();
UnitOfWork.Commit();
}
what happend if i call other repository method in the same request (or just do not use factory as instance-per-request), and that method end successfully, and UnitOfWork.Commit() will be called and does it means that changes made in CreateUser method which failed will be saved too ? or just after throwing exception the connection close and there's no risk the changes from that method will be saved?
To make it more clear:
I want to host that in WCF service, let's say in singleton mode.
And then - one request call method which contains multiple (for example 5) repository calls, and first three will success and the fourth will fail, it means i won't call UnitOfWork.Commit() there.
And then other request come, and it's just success. Does it mean, changes from first three repositories calls from previous method will be saved?
Because of singleton - there'll be still the same DbContextFactory sa the same DbContext.
As #Igor said in the comments, whether you handled or not that exception makes a difference, but as your scenario involves calling anothet repository on the same request, I'll assume you did handled It.
As you said you would reuse the same instance of your DbContextFactory and that's the guy holding your DbContext instances, it's safe to say that, unless you disposed that factory somewhere else, your context would still have that same instance of User added to it and thus calling Commit on that same context on anothet repository would still insert said User.
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.
I have a WebAPI solution with an endpoint "DeleteFolder". That looks like this:
public FolderController()
{
_service = new DtoService();
}
[HttpDelete]
public HttpResponseMessage DeleteFolder(int id)
{
_service.DeleteFolder(id);
return Request.CreateResponse(HttpStatusCode.OK, "Deleted");
}
My _service is an instance of DtoService.cs which contains this:
public DtoService()
{
_repository = new RepositoryService();
}
public void DeleteFolder(int folderId)
{
_repository.DeleteFolder(folderId);
}
Finally, in my repository I have this:
public RepositoryService()
{
_db = new AppDbContext();
}
public void DeleteFolder(int folderId)
{
var folder = GetFolder(folderId);
_db.Folders.Remove(folder);
SaveChanges();
}
Where _db is an instance of my project's DbContext, defined once in the constructor of the Repository class.
When I send a bunch of asynchronous AJAX calls to the delete method, I get "The context cannot be used while the model is being created.". I can see a new instance of RepositoryService spinning up for each one, but if I set a breakpoint at
var folder = GetFolder(folderId);
and then step over, it's hit again, so it seems the other instance is trying to hit the same code before the first one completes, which is somehow causing this error to be thrown.
I don't have any references to my dbContext in my WebAPI layer or DTO service layer - and I'd prefer to keep it that way if possible. I tried wrapping the DeleteFolder code in a using (_db = new AppDbContext) but this didn't work either - and I feel like peppering all of my repository methods with a new dbcontext can't possibly be a recommended solution.
What's going on here? Any tips would be awesome, I'm totally at a loss here.
One thread is initializing your context in response to a request (it's a lengthy process), and another comes in attempting to use the context. The second request thinks the context is ready for use and you get this exception: “The context cannot be used while the model is being created.”
The following code shows how to force database initialization in EF Code First at start up:
protected void Application_Start() {
// ...
// Initializes and seeds the database.
Database.SetInitializer(new MyDBInitializer());
// Forces initialization of database on model changes.
using (var context = new ApplicationDB()) {
context.Database.Initialize(force: true);
}
// ...
}