4
votes

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>
5
remove this.context from your constructor and add it inside the method AddCockpitEnvironmentDetails and see - Ja9ad335h
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

5 Answers

6
votes

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

3
votes

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; }
}
1
votes

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

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

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

0
votes

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();
    }
....
}