DbContext

Jun 24, 2011 at 6:32 PM

Hello,

Is it possible to use this framework with DbContexts?

Tks,

André

Jul 12, 2011 at 12:32 PM

I'm also interested in using this project with DbContext. I can get it working with the EF 4.1 Database First DbContext T4 templates by adding this constructor:

 

    public abstract class MyDbContext : DbContext
    {
        protected MyDbContext(string nameOrConnectionString)
            : base(EFTracingProviderUtils.CreateTracedEntityConnection(nameOrConnectionString), true)
        {
            // enable sql tracing
            ((IObjectContextAdapter) this).ObjectContext.EnableTracing();
        }
    }

 

But it doesn't work with Code First because EFTracingProviderUtils.CreateTracedEntityConnection() requires an Entity ConnectionString, not a normal ConnectionString. Is it possible to use a normal (non-EF) ConnectionString?

Jul 13, 2011 at 12:12 PM

I found an old (EF4 CTP3) code first example at http://jkowalski.com/2010/04/23/logging-sql-statements-in-entity-frameworkcode-first/. After some experimenting I was able to get it working with EF 4.1.

 

public abstract class MyDbContext : DbContext
{
    protected MyDbContext(string nameOrConnectionString)
        : base(CreateTracingConnection(nameOrConnectionString), true)
    {
        // enable sql tracing
        ((IObjectContextAdapter) this).ObjectContext.EnableTracing();
    }

    private static DbConnection CreateTracingConnection(string nameOrConnectionString)
    {
        try
        {
            // this only supports entity connection strings http://msdn.microsoft.com/en-us/library/cc716756.aspx
            return EFTracingProviderUtils.CreateTracedEntityConnection(nameOrConnectionString);
        }
        catch (ArgumentException)
        {
            // an invalid entity connection string is assumed to be a normal connection string name or connection string (Code First)

            ConnectionStringSettings connectionStringSetting =
                ConfigurationManager.ConnectionStrings[nameOrConnectionString];
            string connectionString;
            string providerName;

            if (connectionStringSetting != null)
            {
                connectionString = connectionStringSetting.ConnectionString;
                providerName = connectionStringSetting.ProviderName;
            }
            else
            {
                providerName = "System.Data.SqlClient";
                connectionString = nameOrConnectionString;
            }

            return CreateTracingConnection(connectionString, providerName);
        }
    }

    private static EFTracingConnection CreateTracingConnection(string connectionString, string providerInvariantName)
    {
        // based on the example at http://jkowalski.com/2010/04/23/logging-sql-statements-in-entity-frameworkcode-first/
        string wrapperConnectionString =
            String.Format(@"wrappedProvider={0};{1}", providerInvariantName, connectionString);

        EFTracingConnection connection =
            new EFTracingConnection
                {
                    ConnectionString = wrapperConnectionString
                };

        return connection;
    }
}

It will first try EFTracingProviderUtils.CreateTracedEntityConnection(), and if that fails, it will assume code-first and create an EFTracingConnection manually.

 

Jul 28, 2011 at 7:48 AM

When I try the that solution I always get the message:

The model backing the context 'xxxx' has changed since the database was created.

 

Aug 12, 2011 at 8:46 PM
Edited Aug 12, 2011 at 8:47 PM

I am seeing the same behavior. EF code first generates a hash that identifies the schema. It re-computes this hash on startup and compares it to the one stored in the physical database. It appears that something about the use of the trace wrappers causes it to compute a different hash value; basically causing EF to think that the model is different from what's in the database.

How this behaves at runtime depends on which database initialization strategy you are using.

If using the CreateDatabaseIfNotExists<T> then you see the error dcarapic talked about.

In my case though, I'm using a derivative of the DropCreateDatabaseIfModelChanges<T> initializer, which causes EF to re-create the database. This is fine in my case, but for some reason when it re-generated my database it is also generating the tables and columns differently.

One example, Take a model that looks like this:

 

    public class Thing
    {
        public int ID { get; set; }

        [Required]
        [MaxLength(100)]
        public string Name { get; set; }

        public string ShortName { get; set; }
    }

 

 

When I use an EF DbContext without the trace wrapper stuff (with SQL CE 4.0) it generates an nvarchar(4000) column for ShortName. When I use the DbContext that includes the wrapped connection then the model generates this as an ntext column.  I can work around that specific issue, but I'm very uncomfortable that the generation behavior changes that drastically just because we're wrapping the connection. If it changes this one column type mapping rule, what else does it do differently?

I  also haven't had much luck figuring out why the behavior would be different yet (anyone have any ideas?).

Aug 13, 2011 at 9:52 PM
Edited Aug 13, 2011 at 9:53 PM

The solution that has worked for me (in limited testing) was to have two operational modes depending on if tracing is enabled or not.

When tracing:

  • EF is not allowed to manage the DB schema; set the database initializer to null
  • The DbContext uses the EFTracingConnection.

When not tracing:

  • EF is allowed to manage the DB Schema; use whatever initializer you want
  • The DbContext does NOT use the EFTracingConnection at all.

 

I'm doing this in an MVC app and I'm controlling the code via an appSetting in web.config. I'm also not using entity connection strings, so I removed the try/catch that supported it.  

Here is my Global.asax.cs

protected void Application_Start()
{ 
    // ... other stuff ...

    if (string.Equals(ConfigurationManager.AppSettings["EFTracingProvider.enabled"], "true", StringComparison.InvariantCultureIgnoreCase))
    {	
	Database.SetInitializer<MyDbContext>(null);
	MyDbContext.TraceEnabled = true;
} else { Database.SetInitializer<MyDbContext>(new MyDataInitializer()); }
    // ... other stuff ...
}

Here is the actual Db Context;

 

 

public class MyDbContext: DbContext
{ #region Trace Mechanics public static bool TraceEnabled = false; public MyDbContext() : this("MyDbContextConnectionName") { } public MyDbContext(string nameOrConnectionString) : base(CreateConnection(nameOrConnectionString), true) { if (TraceEnabled) { ((IObjectContextAdapter)this).ObjectContext.EnableTracing(); } } private static DbConnection CreateConnection(string nameOrConnectionString) { // does not support entity connection strings
            EFTracingProviderFactory.Register();
                
            ConnectionStringSettings connectionStringSetting =
                ConfigurationManager.ConnectionStrings[nameOrConnectionString];
            string connectionString;
            string providerName;

            if (connectionStringSetting != null)
            {
                connectionString = connectionStringSetting.ConnectionString;
                providerName = connectionStringSetting.ProviderName;
            }
            else
            {
                providerName = "System.Data.SqlClient";
                connectionString = nameOrConnectionString;
            }

            return CreateConnection(connectionString, providerName);
    }

    private static DbConnection CreateConnection(string connectionString, string providerInvariantName)
    {
        DbConnection connection = null;
        if (TraceEnabled)
        {
            connection = CreateTracingConnection(connectionString, providerInvariantName);
        }
        else
        {
            DbProviderFactory factory = DbProviderFactories.GetFactory(providerInvariantName);
            connection = factory.CreateConnection();
            connection.ConnectionString = connectionString;
        }
        return connection;
    }


    private static EFTracingConnection CreateTracingConnection(string connectionString, string providerInvariantName)
    {
      
        string wrapperConnectionString =
            String.Format(@"wrappedProvider={0};{1}", providerInvariantName, connectionString);

        EFTracingConnection connection =
            new EFTracingConnection
            {
                ConnectionString = wrapperConnectionString
            };

        return connection;
    }

    #endregion

    // ... other stuff ...
}

 

MyDbContext
Apr 27, 2013 at 12:03 PM
Thanks @StephenRedd! Database.SetInitializer<MyDbContext>(null); did the trick for me.