سؤال

عند إجراء تغييرات باستخدام SubmitChanges(), ، يموت LINQ أحيانًا بـ ChangeConflictException استثناء مع رسالة الخطأ Row not found or changed, ، دون أي إشارة إلى الصف الذي يحتوي على التعارض أو الحقول التي تحتوي على تغييرات متعارضة، عندما يقوم مستخدم آخر بتغيير بعض البيانات في هذا الصف.

هل هناك أي طريقة لتحديد الصف الذي يحتوي على التعارض والحقول التي يحدث فيها، وأيضًا هل هناك طريقة لجعل LINQ يتجاهل المشكلة ويلتزم بالبيانات ببساطة بغض النظر؟

بالإضافة إلى ذلك، هل يعرف أحد ما إذا كان هذا الاستثناء يحدث متى أي هل تغيرت البيانات الموجودة في الصف، أم فقط عندما يتم تغيير البيانات في حقل تحاول LINQ تغييره؟

هل كانت مفيدة؟

المحلول

إليك طريقة لمعرفة أماكن التعارضات (هذا مثال على MSDN، لذا ستحتاج إلى التخصيص بشكل كبير):

try
{
    db.SubmitChanges(ConflictMode.ContinueOnConflict);
}
catch (ChangeConflictException e)
{
    Console.WriteLine("Optimistic concurrency error.");
    Console.WriteLine(e.Message);
    Console.ReadLine();
    foreach (ObjectChangeConflict occ in db.ChangeConflicts)
    {
        MetaTable metatable = db.Mapping.GetTable(occ.Object.GetType());
        Customer entityInConflict = (Customer)occ.Object;
        Console.WriteLine("Table name: {0}", metatable.TableName);
        Console.Write("Customer ID: ");
        Console.WriteLine(entityInConflict.CustomerID);
        foreach (MemberChangeConflict mcc in occ.MemberConflicts)
        {
            object currVal = mcc.CurrentValue;
            object origVal = mcc.OriginalValue;
            object databaseVal = mcc.DatabaseValue;
            MemberInfo mi = mcc.Member;
            Console.WriteLine("Member: {0}", mi.Name);
            Console.WriteLine("current value: {0}", currVal);
            Console.WriteLine("original value: {0}", origVal);
            Console.WriteLine("database value: {0}", databaseVal);
        }
    }
}

لجعله يتجاهل المشكلة ويلتزم على أي حال:

db.SubmitChanges(ConflictMode.ContinueOnConflict);

نصائح أخرى

هذه (والتي يمكنك إضافتها في فصل جزئي إلى سياق البيانات الخاص بك قد تساعدك على فهم كيفية عمل ذلك:

public void SubmitKeepChanges()
{
    try
    {
        this.SubmitChanges(ConflictMode.ContinueOnConflict);
    }
    catch (ChangeConflictException e)
    {
        foreach (ObjectChangeConflict occ in this.ChangeConflicts)
        {
            //Keep current values that have changed, 
//updates other values with database values

            occ.Resolve(RefreshMode.KeepChanges);
        }
    }
}

public void SubmitOverwrite()
{
    try
    {
        this.SubmitChanges(ConflictMode.ContinueOnConflict);
    }
    catch (ChangeConflictException e)
    {
        foreach (ObjectChangeConflict occ in this.ChangeConflicts)
        {
            // All database values overwrite current values with 
//values from database

            occ.Resolve(RefreshMode.OverwriteCurrentValues);
        }
    }
}

public void SubmitKeepCurrent()
{
    try
    {
        this.SubmitChanges(ConflictMode.ContinueOnConflict);
    }
    catch (ChangeConflictException e)
    {
        foreach (ObjectChangeConflict occ in this.ChangeConflicts)
        {
            //Swap the original values with the values retrieved from the database. No current value is modified
            occ.Resolve(RefreshMode.KeepCurrentValues);
        }
    }
}

لقد تلقيت هذا الخطأ في ظرف لا علاقة له على الإطلاق بما تصفه رسالة الخطأ.

ما فعلته هو تحميل كائن LINQ عبر DataContext واحد، ثم حاولت SubmitChanges() للكائن عبر DataContext مختلف - أعطى نفس الخطأ بالضبط.

ما كان علي فعله هو استدعاء DataContext.Table.Attach(myOldObject)، ثم استدعاء SubmitChanges()، وكان يعمل كالسحر.

يستحق المشاهدة، خاصة إذا كنت ترى أنه لا ينبغي أن يكون هناك أي صراعات على الإطلاق.

سيظهر الخطأ "لم يتم العثور على الصف أو تغييره" أيضًا في بعض الأحيان عندما لا تتطابق الأعمدة أو الأنواع في O/R-Designer مع الأعمدة الموجودة في قاعدة بيانات SQL، خاصة إذا كان أحد الأعمدة NULLable في SQL ولكنه غير قابل للإلغاء في O/ مصمم R.

لذا تحقق مما إذا كان تعيين الجدول الخاص بك في O/R-Designer يتطابق مع قاعدة بيانات SQL الخاصة بك!

شكرا لvzczc.لقد وجدت المثال الذي قدمته مفيدًا جدًا ولكنني بحاجة إلى الاتصال بـ SubmitChanges مرة أخرى بعد الحل.إليكم طرقي المعدلة - آمل أن تساعد شخصًا ما.

    /// <summary>
    /// Submits changes and, if there are any conflicts, the database changes are auto-merged for 
    /// members that client has not modified (client wins, but database changes are preserved if possible)
    /// </summary>
    public void SubmitKeepChanges()
    {
        this.Submit(RefreshMode.KeepChanges);
    }

    /// <summary>
    /// Submits changes and, if there are any conflicts, simply overwrites what is in the database (client wins).
    /// </summary>
    public void SubmitOverwriteDatabase()
    {
        this.Submit(RefreshMode.KeepCurrentValues);
    }

    /// <summary>
    /// Submits changes and, if there are any conflicts, all database values overwrite
    /// current values (client loses).
    /// </summary>
    public void SubmitUseDatabase()
    {
        this.Submit(RefreshMode.OverwriteCurrentValues);
    }

    /// <summary>
    /// Submits the changes using the specified refresh mode.
    /// </summary>
    /// <param name="refreshMode">The refresh mode.</param>
    private void Submit(RefreshMode refreshMode)
    {
        bool moreToSubmit = true;
        do
        {
            try
            {
                this.SubmitChanges(ConflictMode.ContinueOnConflict);
                moreToSubmit = false;
            }
            catch (ChangeConflictException)
            {
                foreach (ObjectChangeConflict occ in this.ChangeConflicts)
                {
                    occ.Resolve(refreshMode);
                }
            }
        }
        while (moreToSubmit);

    }

لم يتم العثور على الصف أو تم تغييره في أغلب الأحيان يمثل مشكلة التزامن

إذا قام مستخدم مختلف بتغيير نفس السجل، فستنبثق هذه الأخطاء لأن السجل قد تم تغييره بالفعل بواسطة هذا المستخدم الآخر.لذا، عندما تريد التخلص من تلك الأخطاء، يجب عليك التعامل مع التزامن في تطبيقك.إذا تعاملت مع التزامن جيدًا فلن تحصل على هذه الأخطاء بعد الآن.تعتبر نماذج التعليمات البرمجية المذكورة أعلاه طريقة للتعامل مع أخطاء التزامن.ما هو مفقود هو أنه في حالة وجود خطأ في التزامن يجب عليك وضع ملف refresh متغير في تلك الأساليب حتى متى refresh يكون true يجب تحديث البيانات على الشاشة بعد التحديث حتى ترى أيضًا التحديث الذي أجراه المستخدم الآخر.

    /// <remarks>
    ///     linq has optimistic concurrency, so objects can be changed by other users, while
    ///     submitted keep database changes but make sure users changes are also submitted
    ///     and refreshed with the changes already made by other users.
    /// </remarks>
    /// <returns>return if a refresh is needed.</returns>
    public bool SubmitKeepChanges()
    {
        // try to submit changes to the database.
        bool refresh = false;
        try
        {
            base.SubmitChanges(ConflictMode.ContinueOnConflict);
        }

        /* 
         * assume a "row not found or changed" exception, if thats the case:
         * - keep the database changes already made by other users and make sure
         * - this users changes are also written to the database
         */
        catch (ChangeConflictException)
        {
            // show where the conflicts are in debug mode
            ShowConflicts();

            // get database values and combine with user changes 
            base.ChangeConflicts.ResolveAll(RefreshMode.KeepChanges);

            // submit those combined changes again to the database.
            base.SubmitChanges();

            // a refresh is needed
            refresh = true;
        }

        // return if a refresh is needed.
        return refresh;
    }

"وهل هناك أيضًا طريقة لجعل LINQ يتجاهل المشكلة ويلتزم بالبيانات ببساطة بغض النظر؟"

يمكنك تعيين خاصية "التحقق من التحديث" في كيانك على "أبدًا" لإيقاف استخدام هذا الحقل للتحقق المتفائل من التزامن.

تستطيع ايضا استخذام:

db.SubmitChanges(ConflictMode.ContinueOnConflict)
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top