Que puis-je faire pour résoudre une “Ligne pas trouvé ou changé” Exception dans LINQ to SQL sur un Serveur SQL server Compact Edition de la Base de données?

StackOverflow https://stackoverflow.com/questions/45045

  •  09-06-2019
  •  | 
  •  

Question

Lors de l'exécution de SubmitChanges pour le DataContext après la mise à jour d'un couple de propriétés avec LINQ to SQL connexion (par rapport à SQL Server Compact Edition), je reçois une "Ligne pas trouvé ou changé." ChangeConflictException.

var ctx = new Data.MobileServerDataDataContext(Common.DatabasePath);
var deviceSessionRecord = ctx.Sessions.First(sess => sess.SessionRecId == args.DeviceSessionId);

deviceSessionRecord.IsActive = false;
deviceSessionRecord.Disconnected = DateTime.Now;

ctx.SubmitChanges();

La requête génère le code SQL suivant:

UPDATE [Sessions]
SET [Is_Active] = @p0, [Disconnected] = @p1
WHERE 0 = 1
-- @p0: Input Boolean (Size = 0; Prec = 0; Scale = 0) [False]
-- @p1: Input DateTime (Size = 0; Prec = 0; Scale = 0) [9/4/2008 5:12:02 PM]
-- Context: SqlProvider(SqlCE) Model: AttributedMetaModel Build: 3.5.21022.8

Le problème évident est l' OÙ 0=1, Après le record a été chargé, j'ai confirmé que toutes les propriétés dans le "deviceSessionRecord" sont corrects pour inclure la clé primaire.Aussi lors de la capture du "ChangeConflictException" il n'y a pas d'informations supplémentaires sur le pourquoi de cet échec.J'ai également confirmé que cette exception est levée avec exactement un enregistrement dans la base de données (le record, je suis d'essayer de mettre à jour)

Ce qui est étrange, c'est que j'ai très semblable à la mise à jour de l'énoncé dans une autre section de code, et il génère le code SQL suivant et ne fait de mise à jour de mon SQL Server Compact Edition de la base de données.

UPDATE [Sessions]
SET [Is_Active] = @p4, [Disconnected] = @p5
WHERE ([Session_RecId] = @p0) AND ([App_RecId] = @p1) AND ([Is_Active] = 1) AND ([Established] = @p2) AND ([Disconnected] IS NULL) AND ([Member_Id] IS NULL) AND ([Company_Id] IS NULL) AND ([Site] IS NULL) AND (NOT ([Is_Device] = 1)) AND ([Machine_Name] = @p3)
-- @p0: Input Guid (Size = 0; Prec = 0; Scale = 0) [0fbbee53-cf4c-4643-9045-e0a284ad131b]
-- @p1: Input Guid (Size = 0; Prec = 0; Scale = 0) [7a174954-dd18-406e-833d-8da650207d3d]
-- @p2: Input DateTime (Size = 0; Prec = 0; Scale = 0) [9/4/2008 5:20:50 PM]
-- @p3: Input String (Size = 0; Prec = 0; Scale = 0) [CWMOBILEDEV]
-- @p4: Input Boolean (Size = 0; Prec = 0; Scale = 0) [False]
-- @p5: Input DateTime (Size = 0; Prec = 0; Scale = 0) [9/4/2008 5:20:52 PM]
-- Context: SqlProvider(SqlCE) Model: AttributedMetaModel Build: 3.5.21022.8

J'ai confirmé que les principaux domaines de valeurs ont été identifiés à la fois dans le Schéma de Base de données et les DBML qui génère les classes LINQ.

Je suppose que c'est presque une deuxième partie de la question:

  1. Pourquoi l'exception levée?
  2. Après examen de la deuxième série de SQL généré, il semble que pour la détection de conflits, il serait bien de vérifier tous les champs, mais j'imagine que ce serait assez inefficaces.Est-ce la façon dont cela fonctionne toujours?Est-il un paramètre pour vérifier uniquement la clé primaire?

Je me bats avec cette pendant les deux dernières heures de sorte que toute aide serait appréciée.

Était-ce utile?

La solution

C'est méchant, mais simple:

Vérifier si les types de données pour tous les champs de l'O/R-Designer correspondent aux types de données dans ta table SQL.Vérifiez nullable! Une colonne doit être soit d'accepter les valeurs null dans les deux O/R-Concepteur et SQL, ou de ne pas accepter les valeurs null dans les deux.

Par exemple, un type NVARCHAR colonne "titre" est marqué comme NULLable dans votre base de données, et contient la valeur NULL.Même si la colonne est marqué comme NON NULLable dans votre O/R Mapping, LINQ pour le charger avec succès et de définir la colonne de Chaîne de valeur null.

  • Vous pouvez maintenant changer quelque chose et appeler SubmitChanges().
  • LINQ va générer une requête SQL contenant "OÙ [titre] EST NULL", assurez-vous que le titre n'a pas été modifié par quelqu'un d'autre.
  • LINQ regarde les propriétés de [titre] dans la cartographie.
  • LINQ trouverez [titre] PAS les valeurs null.
  • Depuis ['titre'] n'est PAS NULLable, par logique, il n'a jamais pu être NULLE!
  • Ainsi, l'optimisation de la requête LINQ remplace par "0 = 1", le SQL équivalent de "jamais".

Le même problème apparaît lorsque les types de données d'un champ ne correspond pas au type de données dans SQL, ou si les champs sont manquants, depuis LINQ ne sera pas en mesure de s'assurer de la de données SQL n'a pas changé depuis la lecture des données.

Autres conseils

D'abord, il est utile de savoir, ce qui est à l'origine du problème.Googler la solution de l'aide, vous pouvez enregistrer les détails (table, colonne, ancienne valeur, la valeur à neuf) sur le conflit à trouver la meilleure solution pour résoudre le conflit, plus tard:

public class ChangeConflictExceptionWithDetails : ChangeConflictException
{
    public ChangeConflictExceptionWithDetails(ChangeConflictException inner, DataContext context)
        : base(inner.Message + " " + GetChangeConflictExceptionDetailString(context))
    {
    }

    /// <summary>
    /// Code from following link
    /// https://ittecture.wordpress.com/2008/10/17/tip-of-the-day-3/
    /// </summary>
    /// <param name="context"></param>
    /// <returns></returns>
    static string GetChangeConflictExceptionDetailString(DataContext context)
    {
        StringBuilder sb = new StringBuilder();

        foreach (ObjectChangeConflict changeConflict in context.ChangeConflicts)
        {
            System.Data.Linq.Mapping.MetaTable metatable = context.Mapping.GetTable(changeConflict.Object.GetType());

            sb.AppendFormat("Table name: {0}", metatable.TableName);
            sb.AppendLine();

            foreach (MemberChangeConflict col in changeConflict.MemberConflicts)
            {
                sb.AppendFormat("Column name : {0}", col.Member.Name);
                sb.AppendLine();
                sb.AppendFormat("Original value : {0}", col.OriginalValue.ToString());
                sb.AppendLine();
                sb.AppendFormat("Current value : {0}", col.CurrentValue.ToString());
                sb.AppendLine();
                sb.AppendFormat("Database value : {0}", col.DatabaseValue.ToString());
                sb.AppendLine();
                sb.AppendLine();
            }
        }

        return sb.ToString();
    }
}

Créer helper pour envelopper vos sumbitChanges:

public static class DataContextExtensions
{
    public static void SubmitChangesWithDetailException(this DataContext dataContext)
    {   
        try
        {         
            dataContext.SubmitChanges();
        }
        catch (ChangeConflictException ex)
        {
            throw new ChangeConflictExceptionWithDetails(ex, dataContext);
        }           
    }
}

Et puis l'appel de soumettre les modifications de code:

Datamodel.SubmitChangesWithDetailException();

Enfin, journal de l'exception dans votre gestionnaire global d'exception:

protected void Application_Error(object sender, EventArgs e)
{         
    Exception ex = Server.GetLastError();
    //TODO
}

Il existe une méthode sur DataContext appelé Actualiser ce qui peut aider à ici.Il vous permet de recharger l'enregistrement de base de données avant que les modifications soient soumis, et offre différents modes pour déterminer les valeurs à conserver."KeepChanges" semble le plus intelligent pour mes fins, il est prévu de fusionner mes changements avec tout conflit changement qui s'est produit dans la base de données dans le temps.

Si je comprends bien.:)

J'ai résolu cette erreur par redragging sur une table à partir de l'explorateur de serveurs pour le concepteur et re-construction.

Cela peut aussi être causé par l'utilisation de plus d'un DbContext.

Ainsi, par exemple:

protected async Task loginUser(string username)
{
    using(var db = new Db())
    {
        var user = await db.Users
            .SingleAsync(u => u.Username == username);
        user.LastLogin = DateTime.UtcNow;
        await db.SaveChangesAsync();
    }
}

protected async Task doSomething(object obj)
{
    string username = "joe";
    using(var db = new Db())
    {
        var user = await db.Users
            .SingleAsync(u => u.Username == username);

        if (DateTime.UtcNow - user.LastLogin >
            new TimeSpan(0, 30, 0)
        )
            loginUser(username);

        user.Something = obj;
        await db.SaveChangesAsync();
    }
}

Ce code va échouer de temps en temps, par des moyens qui semblent imprévisibles, parce que l'utilisateur est utilisé dans les deux contextes, modifiée et enregistrée dans l'un, puis enregistré dans les autres.La représentation en mémoire de l'utilisateur qui possède "quelque Chose" ne correspond pas à ce qui est dans la base de données, et ainsi, vous obtenez ce qui rôdent bug.

Une façon d'éviter cela est d'écrire du code qui pourrait jamais être appelé comme une méthode de bibliothèque de telle manière qu'il prend une option DbContext:

protected async Task loginUser(string username, Db _db = null)
{
    await EFHelper.Using(_db, async db =>
    {
        var user = await db.Users...
        ... // Rest of loginUser code goes here
    });
}

public class EFHelper
{
    public static async Task Using<T>(T db, Func<T, Task> action)
        where T : DbContext, new()
    {
        if (db == null)
        {
            using (db = new T())
            {
                await action(db);
            }
        }
        else
        {
            await action(db);
        }
    }
}

Alors maintenant, votre méthode prend une option de base de données, et si il n'y en a pas, va et fait un lui-même.Si c'est juste de l'réutilise ce qui a été passé.La méthode d'assistance facilite la réutilisation de ce modèle à travers votre application.

Je ne sais pas si vous avez trouvé des réponses satisfaisantes à votre question, mais j'ai posté une question similaire, et finalement répondu à moi-même.Il s'est avéré que le NOCOUNT de connexion par défaut de l'option a été activée pour la base de données, ce qui a provoqué une ChangeConflictException pour chaque mise à jour effectuée avec Linq to Sql.Vous pouvez vous référer à mon post à ici.

J'ai corrigé cela en ajoutant (UpdateCheck = UpdateCheck.Never) à tous [Column] les définitions.

Ne vous sentez pas comme une solution appropriée, cependant.Dans mon cas, il semble être lié au fait que ce tableau est une association à une autre table, d'où une ligne est supprimée.

C'est sur Windows Phone 7.5.

C'est ce que vous avez besoin de remplacer cette erreur sur code C#:

            try
            {
                _db.SubmitChanges(ConflictMode.ContinueOnConflict);
            }
            catch (ChangeConflictException e)
            {
                foreach (ObjectChangeConflict occ in _db.ChangeConflicts)
                {
                    occ.Resolve(RefreshMode.KeepChanges);
                }
            }

Je sais que cette question a depuis longtemps été répondu, mais ici, j'ai passé les dernières heures à se taper la tête contre un mur, et je voulais juste partager ma solution qui s'est avérée ne pas être lié à l'un des éléments de ce fil:

La mise en cache!

Le select() partie de mon objet de données a l'aide de la mise en cache.Quand il est venu à la mise à jour de l'objet à une Rangée ne Trouve Pas Ou Changé d'erreur a été de tailler.

Plusieurs réponses ont mentionné à l'aide de différents DataContext et, rétrospectivement, c'est probablement ce qui se passait, mais il n'a pas instantanément, me conduisent à penser que la mise en cache, donc j'espère que cela va aider quelqu'un!

J'ai récemment rencontré cette erreur, et a trouvé le problème n'était pas avec mon Contexte de Données, mais avec une mise à jour de la déclaration de tir à l'intérieur d'un déclencheur après Commit a été appelée sur le Contexte.Le déclencheur a été d'essayer de mettre à jour un non nullable champ avec une valeur nulle, et il était à l'origine le contexte de l'erreur avec le message mentionné ci-dessus.

Je suis en ajoutant cette réponse uniquement pour aider les autres à s'occuper de cette erreur et de ne pas trouver une résolution dans les réponses ci-dessus.

J'ai aussi eu cette erreur en raison de l'utilisation de deux différents contextes.J'ai résolu ce problème en utilisant une seule de contexte de données.

Dans mon cas, le problème était avec le serveur à l'échelle de d'options de l'utilisateur.Suivantes:

https://msdn.microsoft.com/en-us/library/ms190763.aspx

J'ai activé l'option NOCOUNT dans l'espoir d'obtenir des avantages de performance:

EXEC sys.sp_configure 'user options', 512;
RECONFIGURE;

et cela s'avère pause Linq vérifie le nombre de Lignes Affectées (autant que je peux le comprendre, à partir .NET sources), conduisant à ChangeConflictException

Réinitialiser les options d'exclure les 512 bits résolu le problème.

Après employant qub1n de réponse, j'ai trouvé que le problème pour moi est que j'ai, par inadvertance, a déclaré une colonne de base de données décimal(18,0).J'ai été affectation d'une valeur décimale, mais la base de données a été modifié, le décapage de la partie décimale.Cela a abouti à la ligne changé au problème.

Juste en ajoutant cette si quelqu'un d'autre se heurte à un problème similaire.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top