Question

J'ai un peu de LINQ à code entités dans une application Web. Il garde en fait un compte de combien de fois une application a été téléchargée. Je crains que cela pourrait se produire:

  • Session 1 lit le nombre de téléchargement (par exemple. 50)
  • Session 2 lit le nombre de téléchargements (encore une fois, 50)
  • Session 1 incrémente et écrit à la db (base de données stocke 51)
  • Session 2 incrémente et écrit à la db (base de données stocke 51)

Ceci est mon code:

private void IncreaseHitCountDB()
{
    JTF.JTFContainer jtfdb = new JTF.JTFContainer();

    var app =
        (from a in jtfdb.Apps
         where a.Name.Equals(this.Title)
         select a).FirstOrDefault();

    if (app == null)
    {
        app = new JTF.App();
        app.Name = this.Title;
        app.DownloadCount = 1;

        jtfdb.AddToApps(app);
    }
    else
    {
        app.DownloadCount = app.DownloadCount + 1;
    }

    jtfdb.SaveChanges();
}

Est-il possible que cela pourrait se produire? Comment pourrais-je l'empêcher?

Merci, Fidel

Était-ce utile?

La solution

Vous pouvez empêcher que cela happenning si vous n'interrogez le droit de la colonne de comptage de téléchargement avant que vous êtes sur le point d'incrémenter, plus le temps passé entre la lecture et incrémenter plus le temps une autre session doit le lire (et réécriture plus tard - à tort -. incrémentée nombre) et déconner ainsi le nombre

avec une seule requête SQL:

UPDATE Data SET Counter = (Counter+1)

depuis ses entités LINQ To, cela signifie que l'exécution différée, pour une autre session pour bousiller le comte (incrémenter la même base, perdant 1 nombre là-bas), il faudrait essayer d'augmenter le app.Download compte i beleive entre les deux lignes:

    else
    {
        app.DownloadCount += 1; //First line
    }

    jtfdb.SaveChanges();  //Second line
}

des thats signifie que la fenêtre pour que le changement se produit, rendant ainsi le nombre précédent vieux, est si faible que pour une application comme cela est pratiquement impossible.

Depuis Im pas LINQ pro, je ne sais pas si LINQ obtient réellement app.DownLoadCount avant d'ajouter un ou ajoute juste une par une commande SQL, mais dans les deux cas vous ne devriez pas avoir à vous soucier que IMHO

Autres conseils

Entity Framework, par défaut, utilise un modèle d'accès concurrentiel optimiste. Google dit moyen optimiste « Plein d'espoir et de confiance dans l'avenir », et c'est exactement comment Entity Framework agit. Autrement dit, lorsque vous appelez SaveChanges() est « plein d'espoir et confiant » qu'aucun problème de concurrence se produira, il essaie juste d'enregistrer vos modifications.

L'autre modèle Entity Framework peut utiliser devrait être appelé modèle concurrentiel pessimiste ( " attendre le pire résultat possible "). Vous pouvez activer ce mode sur une entité par entité. Dans votre cas, vous l'activer sur l'App entité. C'est ce que je fais:

Étape 1. Activation de la vérification d'une entité sur la concurrence

  1. Cliquez droit sur le fichier .edmx et choisissez Ouvrir avec ...
  2. Choisissez XML (texte) Editor dans la boîte de dialogue pop-up, puis cliquez sur OK.
  3. Recherchez l'entité App dans la section ConceptualModels . Je suggère simplement décrire et basculer l'expansion des balises au besoin. Vous cherchez quelque chose comme ceci:

    <edmx:Edmx Version="2.0" xmlns:edmx="http://schemas.microsoft.com/ado/2008/10/edmx">
      <!-- EF Runtime content -->
      <edmx:Runtime>
        <!-- SSDL content -->
        ...
        <!-- CSDL content -->
        <edmx:ConceptualModels>
          <Schema Namespace="YourModel" Alias="Self" xmlns:annotation="http://schemas.microsoft.com/ado/2009/02/edm/annotation" xmlns="http://schemas.microsoft.com/ado/2008/09/edm">
            <EntityType Name="App">
    
  4. Dans le EntityType vous devriez voir un tas de balises <Property>. Si l'on existe avec Name="Status" modifier en ajoutant ConcurrencyMode="Fixed". Si la propriété n'existe pas, copiez celui-ci dans:

    <Property Name="Status" Type="Byte" Nullable="false" ConcurrencyMode="Fixed" />
    
  5. Enregistrez le fichier et double cliquez sur le fichier .edmx pour revenir à la vue design.

Étape 2. lors de l'appel Gestion de la concurrence SaveChanges()

SaveChanges() va lancer une des deux exceptions. Le familier UpdateException ou OptimisticConcurrencyException

si vous avez apporté des modifications à une entité qui a ensemble ConcurrencyMode="Fixed", Entity Framework vérifie d'abord la banque de données pour toutes les modifications apportées. S'il y a des changements, un OptimisticConcurrencyException sera jeté. Si aucune modification n'a été apportée, il continuera normalement.

Lorsque vous prenez le OptimisticConcurrencyException vous devez appeler le Refresh () méthode de votre ObjectContext et refaire vos calculs avant d'essayer à nouveau. L'appel à Refresh() met à jour l'entité (s) et des moyens de RefreshMode.StoreWins conflits seront résolus en utilisant les données dans la banque de données. Le DownloadCount étant changé en même temps est un conflit.

Voici ce que je ferais votre look de code comme. Notez que ceci est plus utile lorsque vous avez beaucoup d'opérations entre l'obtention de votre entité et appeler SaveChanges().

    private void IncreaseHitCountDB()
    {
        JTF.JTFContainer jtfdb = new JTF.JTFContainer();

        var app =
            (from a in jtfdb.Apps
             where a.Name.Equals(this.Title)
             select a).FirstOrDefault();

        if (app == null)
        {
            app = new JTF.App();
            app.Name = this.Title;
            app.DownloadCount = 1;

            jtfdb.AddToApps(app);
        }
        else
        {
            app.DownloadCount = app.DownloadCount + 1;
        }

        try
        {
            try
            {
                jtfdb.SaveChanges();
            }
            catch (OptimisticConcurrencyException)
            {
                jtfdb.Refresh(RefreshMode.StoreWins, app);
                app.DownloadCount = app.DownloadCount + 1;
                jtfdb.SaveChanges();
            }
        }
        catch (UpdateException uex)
        {
            // Something else went wrong...
        }
    }

Vous pouvez facilement test ce qui se passerait dans ce scénario - commencer un fil, le sommeil, puis commencer une autre

.
else
{
    app.DownloadCount = app.DownloadCount + 1;
}

System.Threading.Thread.Sleep(10000);
jtfdb.SaveChanges();

Mais la réponse est simple: non, Entity Framework ne pas effectuer une vérification par défaut de concurrence (MSDN - Enregistrer les modifications et la gestion Concurrency).

Ce site fournira des renseignements généraux pour vous.

Vos options sont

  • pour activer la vérification de la concurrence, ce qui signifie que si deux utilisateurs téléchargent en même temps et les premières mises à jour après la seconde a lu, mais avant la seconde a mis à jour, vous aurez une exception.
  • créer une procédure stockée qui incrémente la valeur dans le tableau directement, et appeler la procédure stockée à partir du code en une seule opération - par exemple, IncrementDownloadCounter. Cela permettra d'assurer qu'il n'y a pas « lire » et donc aucune possibilité d'un «sale lecture.
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top