How to set CommandTimeout for DbContext? - c#

I am looking a way to set CommandTimeout for DbContext. After searching I found the way by casting DbContext into ObjectContext and setting value for CommandTimeout property of objectContext.
var objectContext = (this.DbContext as IObjectContextAdapter).ObjectContext;
But I have to work with DbContext.

It will work with your method.
Or subclass it (from msdn forum)
public class YourContext : DbContext
{
public YourContext()
: base("YourConnectionString")
{
// Get the ObjectContext related to this DbContext
var objectContext = (this as IObjectContextAdapter).ObjectContext;
// Sets the command timeout for all the commands
objectContext.CommandTimeout = 120;
}
}

var ctx = new DbContext();
ctx.Database.CommandTimeout = 120;

This may help you.
public class MyContext : DbContext
{
public MyContext () : base(ContextHelper.CreateConnection("my connection string"), true)
{
((IObjectContextAdapter)this).ObjectContext.CommandTimeout = 300;
}
}

I find that changing the .tt file works for me as I don't lose the change later on:
Add this line:
((IObjectContextAdapter)this).ObjectContext.CommandTimeout = 300;
Right after the DbContext creator and before the !loader.IsLazy construct:
<#=Accessibility.ForType(container)#> partial class <#=code.Escape(container)#> : DbContext
{
public <#=code.Escape(container)#>()
: base("name=<#=container.Name#>")
{
((IObjectContextAdapter)this).ObjectContext.CommandTimeout = 300;
<#
if (!loader.IsLazyLoadingEnabled(container))
It should then appear in your generated Context.cs:
public MyEntities()
: base("name=MyEntities")
{
((IObjectContextAdapter)this).ObjectContext.CommandTimeout = 300;
}

Here's how I solved this problem when using an EDMX file. This solution changes the default T4 template to make the generated class inherit from a custom DbContext class, which specifies a default command timeout, and a property to change it.
I'm using Visual Studio 2012 and EF 5.0. Your experience may differ with other versions.
Create a custom DbContext class
public class CustomDbContext : DbContext
{
ObjectContext _objectContext;
public CustomDbContext( string nameOrConnectionString )
: base( nameOrConnectionString )
{
var adapter = (( IObjectContextAdapter) this);
_objectContext = adapter.ObjectContext;
if ( _objectContext == null )
{
throw new Exception( "ObjectContext is null." );
}
_objectContext.CommandTimeout = Settings.Default.DefaultCommandTimeoutSeconds;
}
public int? CommandTimeout
{
get
{
return _objectContext.CommandTimeout;
}
set
{
_objectContext.CommandTimeout = value;
}
}
}
This has an optional feature: I'm not hard-coding the default command timeout. Instead, I'm loading it from the project settings so that I can change the value in a config file. How to setup and use project settings is not in the scope of this answer.
I'm also not hard-coding the connection string or connection string name. It's already passed into the constructor by the generated context class, so it makes no sense to hard-code it here. This is nothing new; the EDMX file already generates the following constructor for you, so we are just passing along the value.
public MyEntities()
: base("name=MyEntities")
{
}
(This instructs EF to load the connection string named "MyEntities" from the config file.)
I'm throwing a custom exception if the ObjectContext is ever null. I don't think it ever will be, but it's more meaningful than getting a NullReferenceException.
I store the ObjectContext in a field so that I can make a property to access it to override the default.
Modifying the entity context T4 template
In the Solution Explorer, expand the EDMX file so that you see the T4 templates. They have a .tt extension.
Double click the "MyModel.Context.tt" file to open it. Around line 57 you should see this:
<#=Accessibility.ForType(container)#> partial class <#=code.Escape(container)#> : DbContext
This template line generates the class definition of your "MyEntities" class, which inherits DbContext.
Change the line so that the generated class inherits CustomDbContext, instead:
<#=Accessibility.ForType(container)#> partial class <#=code.Escape(container)#> : CustomDbContext
As soon as you save this file it should regenerate the class. If not, you can right-click the EDMX file and select "Run Custom Tool". If you expand the "MyModel.Context.tt" file under your EDMX file, you will see "MyModel.Context.cs". That's the generated file. Open it, and you should see that it now inherits CustomDbContext.
public partial class MyEntities : CustomDbContext
That's all there is to it.
Issues
Once you change the context class from DbContext to CustomDbContext, Visual Studio will give you an error if you try to add a new MVC controller class using the "Controller with read/write actions and views, using Entity Framework" template. It will say "Unsupported context type.". To get around this, open the generated "MyModel.Context.cs" class, and temporarily change the type it inherits back to DbContext. After adding your new controller, you can change it back to CustomDbContext.

I like the extension approach:
public static class DbContextExtensions
{
public static void SetCommandTimeout(this ObjectContext dbContext,
int TimeOut)
{
dbContext.CommandTimeout = TimeOut;
}
}
and then simply
((IObjectContextAdapter)cx).ObjectContext.SetCommandTimeout(300);

If it can help, this is the VB.Net solution:
Dim objectContext As Objects.ObjectContext = CType(Me,IObjectContextAdapter).ObjectContext
objectContext.commandTimeout = connectionTimeout

This is similar to the approach used by #Glazed above but my approach is also to use a custom DbContext class, but I am doing the reverse. Instead of modifying the T4 template (.tt file under your .edmx), I actually inherit from the resulting MyEntities Class instead like so:
MyEntities class generated by the T4 Template:
public partial class MyEntities : DbContext
{
public MyEntities()
: base("name=MyConnectionStringName")
{
}
...
}
Then create a new custom class as a wrapper around MyEntities like the following:
public class MyEntitiesContainer : MyEntities
{
private static readonly int _DEFAULT_TIMEOUT = 100;
public MyEntitiesContainer()
{
((IObjectContextAdapter)this).ObjectContext.CommandTimeout = _DEFAULT_TIMEOUT;
}
//Use this method to temporarily override the default timeout
public void SetCommandTimeout(int commandTimeout)
{
((IObjectContextAdapter)this).ObjectContext.CommandTimeout = commandTimeout;
}
//Use this method to reset the timeout back to default
public void ResetCommandTimeout()
{
((IObjectContextAdapter)this).ObjectContext.CommandTimeout = _COMMAND_TIMEOUT;
}
}
In your code, instantiate the Container class and if you need to use a custom timeout for a specific command, set it manually using the provided methods.
using (var db = new MyEntitiesContainer()) {
db.SetCommandTimeout(300);
db.DoSomeLongCommand();
db.ResetCommandTimeout();
db.DoShorterCommand1();
db.DoShorterCommand2();
...
}
The benefit to this approach is that you can also create an interface for your Container class and use instances of the interface with dependency injection, then you can mock up your database in your unit tests in addition to having easier control over the command timeout and other properties of the object context that you can create methods for (such as lazy loading, etc).

I came here looking for an example of setting the timeout for a single command rather than such a global setting.
I figure that it will probably help someone to have an example of how I achieved this:
var sqlCmd = new SqlCommand(sql, context.Database.Connection as SqlConnection);
sqlCmd.Parameters.Add(idParam);
sqlCmd.CommandTimeout = 90;
if (sqlCmd.Connection.State == System.Data.ConnectionState.Closed)
{
sqlCmd.Connection.Open();
}
sqlCmd.ExecuteNonQuery();
sqlCmd.Connection.Close();

#PerryTribolet's answer looks good for EF6 but it does work for EF5. For EF, here is one way to do it: create an ObjectContext, set the CommandTimeout on that and then create a DBContext from the ObjectContext. I set the flag to have both objects disposed of together. Here is an example in VB.NET:
Dim context As New ObjectContext("name=Our_Entities")
Dim dbcontext As New System.Data.Entity.DbContext(context, True)
With context
.CommandTimeout = 300 'DBCommandTimeout
End With
You don't have to use "With" of course.

Related

How to dispose the ContextDB class automatically after any query done using it

ASP.NET MVC 5 Project.
I know that the best practice of using EF context object as the following
using(var context = new ContextDB())
{
}
But I am working with a large existing project which not used this practice.
the project using the following pattern
public abstract class BaseService
{
private static ContextDB _data { get; set; }
public static ContextDB Data
{
get
{
if (_data== null)
_data= new ContextDB();
return _data;
}
}
}
Actually, because of this pattern, I am receiving this exception (sometimes, not always)
So to solve this I have to change all the code which is using the shared Data
property and replace it with the new instance of ContextDB as I mentioned in the beginning of the question.
The problem that this is a very large modification, and I will not be allowed to do that modification.
The Question, can I solve this problem without changing a ton of code, In another word, can I solve the problems with modifications done only inside the BaseService class, for example, Is there any event which I could handle to know if any query is executed and then dispose of the ContextDB
here is the pseudo-code of the idea in my mind
public abstract class BaseService
{
public static ContextDB Data
{
get
{
ContextDB _data= new ContextDB();
_data.SqlQueryExecuted += () => { this._data.dispose(); }
return _data;
}
}
}
NOTE: the SaveChanged event is not suitable, because not all of the query are updating or inserting.
I may use following solution.
In Global.asax
Begin Request : Create Instance of your dbContext. Store it in HttpContext.Current.Items.
End Request : Grab the context and close / dispose connection.
Another better solution is to use DI. Dependency Injection and limit the scope of your instance. There are many way Like Singleton, PerRequest etc.

how to dynamically set custom conn string for db context?

I have the following db context which was auto-genned through the wizard in VS:
MyEntities : DbContext
When I go to the auto-genned definition, I see the following constructor defintion:
public MyEntities() : base("name=SecurityApiEntities")
{
}
I need to be able to set the connection string dynamically. How can I go about doing this? I don't see a way to do this off hand described in this url:
https://msdn.microsoft.com/en-us/library/jj592674(v=vs.113).aspx
If for some reason you can not keep the connection string in the config file you can do something like this:
var entityBuilder = new EntityConnectionStringBuilder();
// use your ADO.NET connection string
entityBuilder.ProviderConnectionString = conString;
// Set the Metadata location.
entityBuilder.Metadata = #"res://*/Model.csdl|res://*/Model.ssdl|res://*/Model.msl";
var dbContext = new DbContext(entityBuilder.ConnectionString);
To avoid having to do this every single time you want a DbContext you can create a Factory class and method that will return you the DbContext based off of the connection string passed in. Or create a Partial Class that extends your DbContext with a Create function.

ObjectContext instance has been disposed, async method, ToListAsync(), SqlQuery

I've seen other questions similar to this but none close enough to help me figure this out. I'm getting the error here in my repository:
return await Context.Database.SqlQuery<MemberField>(sqlstring).ToListAsync();
How do I fix this? It has worked before without any changes to code so I know all I have to do is keep the context alive. I tried doing this:
Context.Configuration.LazyLoadingEnabled = false;
but it didn't help. This is inside an ASP.NET boilerplate project.
Thanks!
in the constructor I have:
public MyRepository(IDbContextProvider<MyDbContext> dbContextProvider) : base(dbContextProvider)
Then I have a AbpDbContext class:
public class MyDbContext : AbpDbContext{
public MyDbContext() : base("Default"){}
public MyDbContext(string nameOrConnectionString) : base(nameOrConnectionString){}
}
You can grab a template solution just like the project I'm trying to work with from here: http://www.aspnetboilerplate.com/
Update: ABP handles creating and disposing database connections so I don't have any code to show that does that.
I was able to get this working but handling the dbcontext in a using statement, inside the repository. This prevents the Abp frameworking from handling the dbcontext and disposing it too early. So it currently looks like this:
public List<MyObjs> GetObjs(){
using(var db = new MyDbContext()){
var query = from x in db.mytable
select x;
return query.ToList();
}
}

Add WSID in EF connectionstring

I need to pass a WSID in the connectionstring of an Entity Framework context.
I dont want to update all the instanciations that i have, to pass a new connectionstring so i was wondering if there is a method to override that can be of help?
For information, i have this actually:
using(var context = new SampleEntities())
and i dont want to rewrite it like that:
using(var context = new SampleEntities(NewConnectionString))
I tried to override the CreateContext method without success...
With EntityFramework 6, it's not more possible to pass natively a connection string to the constructor of the context (there's no more constructor for that) so i had overload my context class to add the constructor
public partial class MyObjectEntities : DbContext
{
public MyObjectEntities(string connectionString)
: base(connectionString)
{
}
}
In addition, because it's not possible to update the connection string inside the context after instanciation, i had to make the desired connection string before instanciating of my context.
So to do that, i created a dll project (sharable between mutiples projects) able to configure my environnement and containing my desired WSID.
Here is the construtor of a connection string gave by Microsoft http://msdn.microsoft.com/en-US/en-en/library/bb738533%28v=vs.110%29.aspx

Entity Framework Code First : Create Separate Database Per Project

I am developing an application with WPF and Entity Framework CodeFirst.
One requirement is that the user could create separate database file per new project.
DbContext :
public class TestContext : DbContext, IUnitOfWork
{
public TestContext()
: base("TestContext")
{
}
#region Implementation of IUnitOfWork
public new IDbSet<TEntity> Set<TEntity>() where TEntity : class
{
return base.Set<TEntity>();
}
#endregion Implementation of IUnitOfWork
public DbSet<Destination> Destinations { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new DestinationMap());
}
Connection string defined in app config :
<connectionStrings>
<clear />
<add name="TestContext" providerName="System.Data.SqlClient" connectionString="Server=.\sqlexpress;Database=databaseName;Integrated Security=True;Connect Timeout=300;MultipleActiveResultSets=true;" />
</connectionStrings>
I've used StructureMap to inject DbContext in IUnitOfWork interface to implement context per request pattern.
This is StructureMap config :
ObjectFactory.Initialize(x => x.Scan(scanner =>
{
x.For<IUnitOfWork>().CacheBy(InstanceScope.Hybrid).Use<SpitfireContext>();
}));
When user creates new project I try to change connection string and force application create database for new project.
public static void ChangeDatabase(Guid guid)
{
var sqlConnectionStringBuilder =
new SqlConnectionStringBuilder(ConfigHelper.ActiveConnection);
sqlConnectionStringBuilder["Database"] = guid.ToString();
ConfigHelper.ActiveConnection = sqlConnectionStringBuilder.ToString();
Database.DefaultConnectionFactory =
new System.Data.Entity.Infrastructure.SqlConnectionFactory(ConfigHelper.ActiveConnectionString());
Database.SetInitializer(
new MigrateDatabaseToLatestVersion<TestContext, MigrationConfiguration>());
using (var context = new TestContext())
{
context.Database.Initialize(true);
}
}
Application just creates database for first project and after that it tries migrate previews database.
I don't create DbConext object directly in my codes so I can't pass connection string to its constructor.
What do you suggest for my problem ?
What's wrong with my code ?
That's a tricky part - changing connection strings. It kind of works but has issues.
We had a lengthy discussion on that yesterday. Take a look at this (and related) post of mine - which describes some background of troubles for code based on similar approach.
I'd like to help you further if needed - but I'd need some more info...
Why do you say I don't create DbConext object directly in my codes so I can't pass connection string to its constructor ? You have the TestContext - why not pass it through there?
Also I'm not sure about setting the DefaultConnectionFactory, I'll recheck that. It's allowed to be set but it seems recommended on app startup only, not sure. Look up this link in the meantime (it's for EF6 but code is I think similar - and you can look up the source code) - http://msdn.microsoft.com/en-US/data/jj680699
We came to the conclusion that using the DbContext factory was beneficial in such cases.
Would adding a static property on your DbContext class to store the name of the database (Initial Catalog) and then changing the connecting string so that you insert the database name with string.Format work?
Then you could change the database name at any time the next time a context is made it'll be pointing to a new target.

Categories

Resources