Délais d'expiration Entity Framework

324

J'obtiens des délais d'expiration en utilisant Entity Framework (EF) lorsque j'utilise une fonction d'importation qui prend plus de 30 secondes pour se terminer. J'ai essayé ce qui suit et je n'ai pas pu résoudre ce problème:

J'ai ajouté Default Command Timeout=300000à la chaîne de connexion dans le fichier App.Config dans le projet qui a le fichier EDMX comme suggéré ici .

Voici à quoi ressemble ma chaîne de connexion:

<add 
    name="MyEntityConnectionString" 
    connectionString="metadata=res://*/MyEntities.csdl|res://*/MyEntities.ssdl|
       res://*/MyEntities.msl;
       provider=System.Data.SqlClient;provider connection string=&quot;
       Data Source=trekdevbox;Initial Catalog=StarTrekDatabase;
       Persist Security Info=True;User ID=JamesTKirk;Password=IsFriendsWithSpock;
       MultipleActiveResultSets=True;Default Command Timeout=300000;&quot;"
    providerName="System.Data.EntityClient" />

J'ai essayé de définir le CommandTimeout dans mon référentiel directement comme ceci:

private TrekEntities context = new TrekEntities();

public IEnumerable<TrekMatches> GetKirksFriends()
{
    this.context.CommandTimeout = 180;
    return this.context.GetKirksFriends();
}

Que puis-je faire d'autre pour empêcher l'EF de se terminer? Cela ne se produit que pour les très grands ensembles de données. Tout fonctionne bien avec de petits ensembles de données.

Voici l'une des erreurs que je reçois:

System.Data.EntityCommandExecutionException: une erreur s'est produite lors de l'exécution de la définition de commande. Voir l'exception interne pour plus de détails. ---> System.Data.SqlClient.SqlException: le délai a expiré. Le délai d'expiration s'est écoulé avant la fin de l'opération ou le serveur ne répond pas.


OK - je l'ai fait fonctionner et c'est idiot ce qui s'est passé. J'avais à la fois la chaîne de connexion avec Default Command Timeout=300000et le CommandTimeout défini sur 180. Lorsque j'ai supprimé le Default Command Timeoutde la chaîne de connexion, cela a fonctionné. La réponse est donc de définir manuellement le CommandTimeout dans votre référentiel sur votre objet de contexte comme suit:

this.context.CommandTimeout = 180;

Apparemment, la définition des paramètres de délai d'attente dans la chaîne de connexion n'a aucun effet.

Alcyon
la source
Supprimer & quot; de la chaîne de connexion
Brian Webster
5
@ hamlin11 Dans une chaîne de connexion EF, cela est nécessaire pour définir quelle partie est une chaîne de connexion et quelle partie est des métadonnées EF. Laissez &quot;dans la chaîne.
Chev
2
ma suggestion est avant d'augmenter le délai serait d'enquêter d'abord pour voir pourquoi EF expire. Dans notre cas, nous avons réalisé que nous devions ajouter des NONCLUSTEREDindex à certaines tables, cela a résolu le problème de délai d'attente pour nous.
zulucoda
Je travaille avec le support MS sur un problème de délai d'expiration SQL - c'est lorsque la base de données est hébergée dans SQL Azure. On m'a dit que tous les services Azure PaaS (sites Web PaaS et SQL Azure, etc.) ont un délai d'expiration universel de 230 secondes, et cela a toujours la priorité, même si vous définissez un délai d'expiration manuellement. Il s'agit de protéger les ressources de l'infrastructure PaaS à locataires multiples.
Ian Robertson

Réponses:

552

Il existe un bogue connu avec la spécification du délai d'expiration de la commande par défaut dans la chaîne de connexion EF.

http://bugs.mysql.com/bug.php?id=56806

Supprimez la valeur de la chaîne de connexion et définissez-la sur l'objet de contexte de données lui-même. Cela fonctionnera si vous supprimez la valeur en conflit de la chaîne de connexion.

Entity Framework Core 1.0:

this.context.Database.SetCommandTimeout(180);

Entity Framework 6:

this.context.Database.CommandTimeout = 180;

Entity Framework 5:

((IObjectContextAdapter)this.context).ObjectContext.CommandTimeout = 180;

Entity Framework 4 et inférieur:

this.context.CommandTimeout = 180;
Chev
la source
5
Comment puis-je y parvenir en utilisant edmx?
iroel
2
Dans quelle version de EntityFramework ce problème est-il résolu? Je ne trouve pas le bug EF pour cela.
rudimenter
7
Je ne crois pas que ce soit un bug, mais plutôt par conception, voir la section Remarques ici lien
Mick P
3
Parce que certains paramètres sont en ms et certains en s, je l'ai recherché ici , CommandTimeout est en quelques secondes.
JabberwockyDecompiler
6
Dans Entity Framework 7, vous pouvez définir cela dans le constructeur de DbContext / IdentityDbContext:this.Database.SetCommandTimeout(180);
Thomas Hagström
101

Si vous utilisez un DbContext, utilisez le constructeur suivant pour définir le délai d'expiration de la commande:

public class MyContext : DbContext
{
    public MyContext ()
    {
        var adapter = (IObjectContextAdapter)this;
        var objectContext = adapter.ObjectContext;
        objectContext.CommandTimeout = 1 * 60; // value in seconds
    }
}
saille
la source
3
@ErickPetru, donc vous pouvez facilement le changer en un nombre différent de minutes :), je ne serais pas trop surpris si le compilateur optimise cette multiplication!
Joel Verhagen
2
@JoelVerhagen, ne soyez pas surpris. Voici une bonne explication du moment où l'optimisation automatique se produit: stackoverflow.com/questions/160848/… . Dans ce cas, je suppose que cela se produit même (car ce sont deux valeurs littérales), mais honnêtement, je pense que le code est un peu étrange de cette façon.
Erick Petrucelli
33
meh ... les enfants meurent de faim ... qui se soucie de 1 * 60?
Timmerz
9
@ErikPetru, c'est en fait une pratique très courante et rend le code plus lisible.
Calvin
Quelle est la meilleure façon de gérer cela étant donné que ma DbContextclasse dérivée a été générée automatiquement à partir d'un edmxfichier?
Matt Burland
41

Si vous utilisez DbContextet EF v6 +, vous pouvez également utiliser:

this.context.Database.CommandTimeout = 180;
Paul
la source
13

Habituellement, je gère mes opérations au sein d'une transaction . Comme je l'ai expérimenté, il ne suffit pas de définir le délai d'expiration de la commande de contexte, mais la transaction a besoin d'un constructeur avec un paramètre de délai d'expiration. J'ai dû définir les deux valeurs de délai d'expiration pour que cela fonctionne correctement.

int? prevto = uow.Context.Database.CommandTimeout;
uow.Context.Database.CommandTimeout = 900;
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, TimeSpan.FromSeconds(900))) {
...
}

À la fin de la fonction, j'ai remis le délai d'expiration de la commande à la valeur précédente dans prevto.

Utilisation d'EF6

pillesoft
la source
Pas du tout une bonne approche. J'avais l'habitude d'ajouter beaucoup de portée aux transactions et cela devenait un cauchemar pour moi dans un projet. Éventuellement remplacé toute l'étendue des transactions par un seul SAVEChanges () dans EF 6+. Consultez ce coderwall.com/p/jnniww/…
Lunes
Cette réponse devrait avoir un vote plus élevé. J'ai essayé toutes les différentes façons d'augmenter le délai d'expiration, mais seulement lorsque j'ai défini les deux délais d'expiration de la commande de contexte et la portée de la transaction, cela a fonctionné.
Gang
3

Je sais que ce thread est très ancien, mais EF n'a toujours pas corrigé cela. Pour les personnes utilisant la génération automatique, vous DbContextpouvez utiliser le code suivant pour définir le délai d'expiration manuellement.

public partial class SampleContext : DbContext
{
    public SampleContext()
        : base("name=SampleContext")
    {
        this.SetCommandTimeOut(180);
    }

    public void SetCommandTimeOut(int Timeout)
    {
        var objectContext = (this as IObjectContextAdapter).ObjectContext;
        objectContext.CommandTimeout = Timeout;
    }
Shiva N
la source
3

Si vous utilisez Entity Framework comme moi, vous devez définir le délai d'expiration au démarrage comme suit:

 services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"), o => o.CommandTimeout(180)));
parismiguel
la source
1

C'est ce que j'ai financé. Peut-être que cela aidera quelqu'un:

Alors c'est parti:

Si vous utilisez LINQ avec EF à la recherche de certains éléments exacts contenus dans la liste comme ceci:

await context.MyObject1.Include("MyObject2").Where(t => IdList.Contains(t.MyObjectId)).ToListAsync();

tout va bien jusqu'à ce que IdList contienne plus d'un ID.

Le problème de «timeout» apparaît si la liste contient un seul ID. Pour résoudre le problème, utilisez la condition if pour vérifier le nombre d'identifiants dans IdList.

Exemple:

if (IdList.Count == 1)
{
    result = await entities. MyObject1.Include("MyObject2").Where(t => IdList.FirstOrDefault()==t. MyObjectId).ToListAsync();
}
else
{
    result = await entities. MyObject1.Include("MyObject2").Where(t => IdList.Contains(t. MyObjectId)).ToListAsync();
}

Explication:

Essayez simplement d'utiliser Sql Profiler et vérifiez l'instruction Select générée par Entity frameeork. …

tosjam
la source