4

I am trying to create a console application where I am trying to use Entity Framework to dynamically create database and tables to store the data. However, when I am trying to add data to my DbSet. I am getting the following error.

An unhandled exception of type 'System.InvalidOperationException' occurred in EntityFramework.dll

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.

I have referred to other posts with similar error but none of the solutions worked. This is a single threaded application.

The error occurs at line

this.context.Environments.Add(entity)

DataSource.cs

public class DataSource
{
        private static DataSource instance;

        public DataSource()
        {
            this.context = new CounterContext();
        }

        public static DataSource Instance
        {
            get
            {
                return instance ?? (instance = new DataSource());
            }
        }

        private CounterContext context;

        public void AddCockpitEnvironmentDetails(IList<Environment> environmentList)
        {
            foreach (var entity in environmentList)
            {
                this.context.Environments.Add(entity);
            }

            this.context.SaveChanges();
        }
...
}

My context:

public class CounterContext : DbContext
{
        public CounterContext()
            : base("name=CounterDbString")
        {
            Database.SetInitializer<CounterContext>(new CreateDatabaseIfNotExists<CounterContext>());
        }
        public DbSet<CounterData> CounterDetails { get; set; }

        public DbSet<Environment> Environments { get; set; }
    }

Model

public class Environment
{
    [Key]
    public Guid Id { get; set; }
    public string EnvironmentName { get; set; }
    public string EnvironmentNotes { get; set; }

    public virtual ICollection<CounterDetail> CounterDetails { get; set; }
};

public class CounterData 
{
    [Key]
    [Column(Order = 1)]
    public DateTime counterTime { get; set; }
    [Key]
    [Column(Order = 2)]
    public int counterName { get; set; }
    [Key]
    [Column(Order = 3)]
    public Guid EnvId {get; set;}
    public int count { get; set; }

    [ForeignKey("EnvId")]
    public virtual CockpitEnvironment Machine { get; set; }
}

App.config:

<connectionStrings>
    <add name="CounterDbString" 
         connectionString="Data Source=.\SQLEXPRESS;Initial Catalog=Counters_Test;Integrated Security=True;Connect Timeout=30"
         providerName="System.Data.SqlClient"/>
</connectionStrings>
SohamC
  • 2,367
  • 6
  • 22
  • 34
  • remove `this.context` from your constructor and add it inside the method `AddCockpitEnvironmentDetails` and see – Ja9ad335h Aug 11 '16 at 16:21
  • Not working. However I did an experiment by adding mdf file first in SQL Server and then ran the code. It worked. So the issue is it is not able to create the table. But not sure how to fix the issue. – SohamC Aug 12 '16 at 06:18

5 Answers5

6

Using VS2015, I was getting this error when debugging (F5). After simply stopping the debugger and restarting the problem went away.

Rob Bowman
  • 7,632
  • 22
  • 93
  • 200
3

If this is coming up when your database is not yet created, it is usually a connection string issue or a database initializer issue. Your connection string looks OK, so it could be having the initializer inside the instance constructor. Try moving the initializer to a static constructor:

public class CounterContext : DbContext
{
    static CounterContext  // runs once
    {
        Database.SetInitializer<CounterContext>(new CreateDatabaseIfNotExists<CounterContext>());
    }

    public CounterContext()
        : base("name=CounterDbString") { }

    public DbSet<CounterData> CounterDetails { get; set; }

    public DbSet<Environment> Environments { get; set; }
}
Steve Greene
  • 12,029
  • 1
  • 33
  • 54
  • Figured out the issue. Please see comment above. Trying to figure out the solution. I am pretty sure I am missing something trivial here. Any thoughts? – SohamC Aug 12 '16 at 06:20
  • By creating your database manually, your initializer doesn't run (CreateDatabaseIfNotExists). You shoved the issue under the carpet, but it could come back later. – Steve Greene Aug 12 '16 at 15:51
  • Adding the static constructor gives errors: `The name 'Database.SetInitializer' does not exist in the current context.` and on the brackets (even though I matched them up), `Invalid token '{' in class, struct, or interface member declaration`. @SteveGreene : Not necessarily. If it worked, all that line means is that it will create the instance if needed - if not, it won't. If it doesn't, it means it has what it needs, so it should never fail (at least in theory). – vapcguy Jan 29 '20 at 21:47
  • @vapcguy Are you asking about an error or offering a correction? – Steve Greene Jan 30 '20 at 03:16
  • @SteveGreene Ah-didn't realize you were both the question answerer & commenter (d'oh!). I was trying to address both in 1 post & thought you were speaking AGAINST your own answer by saying it sweeps "the issue under the carpet". I was trying to correct that-that using `CreateDatabaseIfExists` should not do that. But I found the error in adding `Database.SetInitializer(new CreateDatabaseIfNotExists());` to my project as suggested in your original answer. If you have any additional information on what you have to do to avoid the error I got, it's appreciated. – vapcguy Jan 31 '20 at 15:38
  • @vapcguy Well, for starters you are using your own context name and have a `using` statement to that context? Ask a new question with details of your error and configuration if needed. – Steve Greene Jan 31 '20 at 15:50
  • @SteveGreene Yeah, I'm using the `using(db) {..}`. So weirdest thing-I was trying to pull a database view instead of a table. I got the idea to just try pulling a basic, small 2-row table, instead. After I did that & just did a rebuild and ran the application, the error went away and was able to grab that. I went back to grabbing the view, and I get an error in trying to pull that view because it will say `Invalid object name 'dbo.viwReports'`, when it's `dbo.viwReport` in my database & I don't even call it `viwReports` in my EF data context. Oh, well, separate issue. Quirks, I guess. Thanks. – vapcguy Jan 31 '20 at 16:56
  • @SteveGreene I figured out how to fix my issue: in `OnModelCreating`, I had to add this: `modelBuilder.Conventions.Remove();` -- https://stackoverflow.com/questions/7924758/entity-framework-creates-a-plural-table-name-but-the-view-expects-a-singular-ta . To fix my original issue that brought me here, I found if I added a `Dispose` method in my controller, it seemed to help: https://stackoverflow.com/questions/12118562/ef-the-context-cannot-be-used-while-the-model-is-being-created-exception-durin – vapcguy Jan 31 '20 at 17:46
1

This worked for me: initializing the database after creating the context

context.Database.Initialize(force: false);
0

For me the problem was that it was static or shred function...

Change it to Public.

Thread Safety: Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.

Found here

Zvi Redler
  • 1,708
  • 1
  • 18
  • 29
0

I found that this issue seems to occur because the Controller doesn't take care of disposing the DataContext and it stays open. You can handle this either on the DataContext side or the controller side, or both.

I do this in my DataContext:

public partial class Model1 : DbContext
{
    public Model1()
        : this(true)
    { }

    public Model1(bool enableLazyLoading = true)
        : base("name=Model1")
    {
        // You can also do this instead of the other lines
        //Database.SetInitializer<Model1>(new CreateDatabaseIfNotExists<Model1>());            
        //this.Configuration.LazyLoadingEnabled = false;

        Database.SetInitializer<Model1>(null);
        this.Configuration.ProxyCreationEnabled = false;

        ((IObjectContextAdapter)this).ObjectContext.ContextOptions.ProxyCreationEnabled = enableLazyLoading;
        ((IObjectContextAdapter)this).ObjectContext.ContextOptions.LazyLoadingEnabled = enableLazyLoading;
    }

and this in the Controller:

public class ReportController : Controller
{
    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            db.Dispose();
        }
        base.Dispose(disposing);
    }

and then you have your normal stuff after that...

    public ActionResult Index()
    {
        //....
    }

But why are you grabbing data like that? And why use a separate class? This should all happen in the Controller. And you don't want to try and make it public and static across everything - it is dynamic and needs to be called & disposed per Controller, not left open.

I would do this:

public class ReportController : Controller
{
    CounterContext db = new CounterContext();

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
           db.Dispose();
        }
        base.Dispose(disposing);
    }

    public void AddCockpitEnvironmentDetails(IList<Environment> environmentList)
    {
        // You could've gotten it like this instead of passing it in...
        // IEnumerable<Environments> environmentList = db.Environments;

        foreach (Environment entity in environmentList)
        {
            db.Environments.Add(entity);
        }

        db.SaveChanges();
    }
....
}
vapcguy
  • 7,097
  • 1
  • 56
  • 52