web-dev-qa-db-fra.com

Entity Framework Core: une deuxième opération a été lancée dans ce contexte avant la fin d'une opération précédente

Je travaille sur un projet ASP.Net Core 2.0 utilisant Entity Framework Core 

<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.0.1" />
  <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.0.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.0.0"/>

Et dans l'une de mes méthodes de liste, j'obtiens cette erreur:

InvalidOperationException: A second operation started on this context before a previous operation completed. Any instance members are not guaranteed to be thread safe.
Microsoft.EntityFrameworkCore.Internal.ConcurrencyDetector.EnterCriticalSection()

Ceci est ma méthode:

    [HttpGet("{currentPage}/{pageSize}/")]
    [HttpGet("{currentPage}/{pageSize}/{search}")]
    public ListResponseVM<ClientVM> GetClients([FromRoute] int currentPage, int pageSize, string search)
    {
        var resp = new ListResponseVM<ClientVM>();
        var items = _context.Clients
            .Include(i => i.Contacts)
            .Include(i => i.Addresses)
            .Include("ClientObjectives.Objective")
            .Include(i => i.Urls)
            .Include(i => i.Users)
            .Where(p => string.IsNullOrEmpty(search) || p.CompanyName.Contains(search))
            .OrderBy(p => p.CompanyName)
            .ToPagedList(pageSize, currentPage);

        resp.NumberOfPages = items.TotalPage;

        foreach (var item in items)
        {
            var client = _mapper.Map<ClientVM>(item);

            client.Addresses = new List<AddressVM>();
            foreach (var addr in item.Addresses)
            {
                var address = _mapper.Map<AddressVM>(addr);
                address.CountryCode = addr.CountryId;
                client.Addresses.Add(address);
            }

            client.Contacts = item.Contacts.Select(p => _mapper.Map<ContactVM>(p)).ToList();
            client.Urls = item.Urls.Select(p => _mapper.Map<ClientUrlVM>(p)).ToList();
            client.Objectives = item.Objectives.Select(p => _mapper.Map<ObjectiveVM>(p)).ToList();
            resp.Items.Add(client);
        }

        return resp;
    }

Je suis un peu perdu surtout parce que cela fonctionne lorsque je l'exécute localement, mais lorsque je déploie sur mon serveur de stockage intermédiaire (IIS 8.5), cette erreur me provient et il fonctionne normalement. L'erreur a commencé à apparaître après l'augmentation de la longueur maximale de l'un de mes modèles. J'ai également mis à jour la longueur maximale du modèle de vue correspondant. Et il y a beaucoup d'autres méthodes de liste qui sont très similaires et qui fonctionnent.

J'avais un travail Hangfire en cours d'exécution, mais ce travail n'utilise pas la même entité. C'est tout ce que je peux penser pour être pertinent. Des idées sur ce qui pourrait causer cela?

14
André Luiz

L'exception signifie que _context est utilisé par deux threads en même temps; soit deux threads dans la même requête, soit deux requêtes.

Est-ce que votre _context déclaré statique peut-être? CA ne devrait pas être.

Ou appelez-vous GetClients plusieurs fois dans la même demande, quelque part dans votre code?

Vous le faites peut-être déjà, mais idéalement, vous utiliseriez injection de dépendance pour votre DbContext, ce qui signifie que vous utiliserez AddDbContext() dans votre Startup.cs et le constructeur de votre contrôleur comme ça:

private readonly MyDbContext _context; //not static

public MyController(MyDbContext context) {
    _context = context;
}

Si votre code n'est pas comme ça, montrez-nous et peut-être que nous pourrons aider davantage.

15
Gabriel Luci

Je ne sais pas si vous utilisez IoC et Dependency Injection pour résoudre votre DbContext où que ce soit. Si vous utilisez et utilisez l'IoC natif de .NET Core (ou tout autre IoC-Container) et que vous obtenez cette erreur, assurez-vous d'enregistrer votre DbContext en tant que Transient. Faire

services.AddTransient<MyContext>();

OR

services.AddDbContext<MyContext>(ServiceLifetime.Transient);

au lieu de

services.AddDbContext<MyContext>();

AddDbContext ajoute le contexte comme étant étendu, ce qui peut causer des problèmes lorsque vous travaillez avec plusieurs threads. 

De plus, les opérations async/wait peuvent être à l'origine de ce problème lors de l'utilisation d'expressions lambda asynchrones.

L'ajouter comme transitoire a aussi ses inconvénients. Vous ne pourrez pas modifier certaines entités sur plusieurs entités utilisant le contexte car chaque classe aura sa propre instance de votre DbContext.

24
alsami

Cette erreur est dans certains cas causée par ce scénario: vous appelez une méthode async mais n’avez pas utilisé wait avant d’appeler la méthode. mon problème résolu en ajoutant wait avant la méthode. Cependant, la réponse peut ne pas être liée à la question mentionnée mais elle peut aider à une erreur similaire.

4
Hamid Nasirloo

J'ai fait face au même problème mais la raison en était aucune de celles énumérées ci-dessus. J'ai créé une tâche, créé une étendue à l'intérieur de la tâche et demandé au conteneur d'obtenir un service. Cela a bien fonctionné, mais j’ai utilisé un deuxième service à l’intérieur de la tâche et j’ai oublié de le demander également. À cause de cela, le deuxième service utilisait un DbContext déjà supprimé.

                Task task = Task.Run(() =>
                {
                    using (var scope = serviceScopeFactory.CreateScope())
                    {
                        var otherOfferService = scope.ServiceProvider.GetService<IOfferService>();
                        // everything was ok here. then I did: 
                        productService.DoSomething(); // (from the main scope) and this failed because the db context associated to that service was already disposed.
                        ...

                    }
                }

J'aurais dû faire ceci:

var otherProductService = scope.ServiceProvider.GetService<IProductService>();
otherProductService.DoSomething();
1

J'ai reçu le même message. Mais cela n’a aucun sens dans mon cas. Mon problème est que j'ai utilisé une propriété "NotMapped" par erreur. Cela signifie probablement uniquement une erreur de syntaxe ou de classe de modèle Linq dans certains cas. Le message d'erreur semble trompeur. La signification originale de ce message est que vous ne pouvez pas appeler plusieurs fois async sur le même dbcontext dans la même requête.

[NotMapped]
public int PostId { get; set; }
public virtual Post Post { get; set; }

Vous pouvez consulter ce lien pour plus de détails, https://www.softwareblogs.com/Posts/Details/5/error-a-second-operation-started-on-this-context-before-a-previous -operation-complete

0
Sharon Zhou