Domanda

I'm using a timestamp column to check the concurrency in my entity. The exception is correctly thrown when data is not the same in 2 different contexts.

When such an exception occurs when saving, I call the following method to handle it:

public static void HandleOptimisticConcurrencyException(ObjectContext context, OptimisticConcurrencyException ex)
{
    string msg = @"The data has changed while you were editing it.
If you save, your changes will override the previous ones.
If you don't, your changes will be lost.

Do you want to save your changes ?";

    var ret = System.Windows.MessageBox.Show(msg, "Concurrency error...", MessageBoxButton.YesNo, MessageBoxImage.Warning);

    if (ret ==  MessageBoxResult.Yes)
    {
        if (ex.StateEntries != null)
        {
            foreach (var item in ex.StateEntries)
            {
                context.Refresh(RefreshMode.ClientWins, item.Entity);
            }
        }
    }
    else
    {
        if (ex.StateEntries != null)
        {
            foreach (var item in ex.StateEntries)
            {
                context.Refresh(RefreshMode.StoreWins, item.Entity);
            }
        }
    }

    context.SaveChanges();
}

I checked, and the refresh is executed on each faulted entity. However, the second SaveChanges() always rethrows an OptimisticConcurrencyException.

Am I doing something wrong ?

Thanks in advance

Edit

I've noticed that the problem arises because of a method call before the first SaveChanges()

try
{
    this.UpdateFlags();

    this.repository.Context.SaveChanges();
}
catch (OptimisticConcurrencyException ex)
{
    ExceptionHelpers.HandleOptimisticConcurrencyException(this.repository.Context, ex);
}

If I comment out the UpdateFlags() call, I have no problem.

Here is the code for this method:

private void UpdateFlags()
{
    DateTime now = DateTime.Now;

    int masterId = (int)this.navigationContext.Item;

    var master = this.repository.Context.Masters.Where(e => e.Id == masterId).FirstOrDefault();

    foreach (var project in master.Projects)
    {
        // update flags for each project.

        if (project.Dashboard == null)
        {
            project.Dashboard = new Dashboard();
        }

        var flags = project.Dashboard;
        flags.ModifiedOn = now;

        // Update DP flags
        var dpFlag = (int)project.Tasks.Where(e => e.TaskDP != null)
                                   .Select(e => this.CalculateCompletionStatus(e, now))
                                   .DefaultIfEmpty(CompletionStatusType.Ok)
                                   .Max();
        flags.DP = dpFlag;

        // Update TRS flags
        var trsFlag = (int)project.Tasks.Where(e => e.TaskTRSs != null)
                                   .Select(e => this.CalculateCompletionStatus(e, now))
                                   .DefaultIfEmpty(CompletionStatusType.Ok)
                                   .Max();
        flags.TRS = trsFlag;

        // Update REV flags
        var revFlag = (int)project.Tasks.Where(e => e.TaskREV != null)
                                   .Select(e => this.CalculateCompletionStatus(e, now))
                                   .DefaultIfEmpty(CompletionStatusType.Ok)
                                   .Max();
        flags.REV = revFlag;

        // Update DTP flags
        var dtpFlag = (int)project.Tasks.Where(e => e.TaskDTP != null)
                                   .Select(e => this.CalculateCompletionStatus(e, now))
                                   .DefaultIfEmpty(CompletionStatusType.Ok)
                                   .Max();
        flags.DTP = dtpFlag;

        // Update DEL flags
        var delFlag = (int)project.Tasks.Where(e => e.TaskDEL != null)
                                   .Select(e => this.CalculateCompletionStatus(e, now))
                                   .DefaultIfEmpty(CompletionStatusType.Ok)
                                   .Max();
        flags.DEL = delFlag;

        // Update FIN Flag
        var finFlag = (int)project.SalesTasks.Select(e => this.CalculateCompletionStatus(e, now))
                                             .DefaultIfEmpty(CompletionStatusType.Ok)
                                             .Max();
        flags.FIN = finFlag;

        // Update Project flag
        if (flags.REV == (int)CompletionStatusType.Client && project.DTPBeforeReview.HasValue && project.DTPBeforeReview.Value == false)
        {
            // Corner case : Review is late because of an external person (= grey) and DTP Before REV is not set
            // => all moments after REV are not taken in account.
            var projFlag = new List<int> { dpFlag, trsFlag, revFlag }.Max();
            flags.ProjectStatus = projFlag;
        }
        else
        {
            var projFlag = new List<int> { dpFlag, trsFlag, revFlag, dtpFlag, delFlag, finFlag }.Max();
            flags.ProjectStatus = projFlag;
        }
    }
}

However I don't see where the problem is, as this is made before the first SaveChanges()

È stato utile?

Soluzione

Ok I think I found how to solve that.

The problem doesn't come from the first object who causes the first exception, but deeper in this object. To solve that, I updated my method like this:

public static void HandleOptimisticConcurrencyException(ObjectContext context, OptimisticConcurrencyException ex)
{
    string msg = @"The data has changed while you were editing it.
If you save, your changes will override the previous ones.
If you don't, your changes will be lost.

Do you want to save your changes ?";

    var ret = System.Windows.MessageBox.Show(msg, "Concurrency error...", MessageBoxButton.YesNo, MessageBoxImage.Warning);

    if (ret ==  MessageBoxResult.Yes)
    {
        if (ex.StateEntries != null)
        {
            foreach (var item in ex.StateEntries)
            {
                context.Refresh(RefreshMode.ClientWins, item.Entity);
            }
        }
    }
    else
    {
        if (ex.StateEntries != null)
        {
            foreach (var item in ex.StateEntries)
            {
                context.Refresh(RefreshMode.StoreWins, item.Entity);
            }
        }
    }

    do
    {
        try
        {
            context.SaveChanges();
            break;
        }
        catch (OptimisticConcurrencyException ex2)
        {
            if (ret == MessageBoxResult.Yes)
            {
                foreach (var item in ex2.StateEntries)
                {
                    context.Refresh(RefreshMode.ClientWins, item.Entity);
                }
            }
            else
            {
                foreach (var item in ex2.StateEntries)
                {
                    context.Refresh(RefreshMode.StoreWins, item.Entity);
                }
            }
        }
    }
    while (true);
}

I'm open to any other better suggestion though...

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top