SharePoint–rozwiązywanie konfliktów zapisu

Jednym z częstych problemów występujących podczas developmentu rozwiązania dla SharePoint jest występowanie konfliktów zapisu. Sytuacja taka zachodzi gdy dopuszczona zostanie sytuacja w której dwóch lub więcej użytkowników może zmienić zawartość elementu listy jednocześnie (czy to poprzez wybraną architekturę rozwiązania, czy też przez błąd w kodzie). Problemem w stwierdzeniu (a właściwie uwierzeniu w to), że występuje konflikt zapisu, zwłaszcza przez początkujących jest komunikat występujący w tym przypadku. Zobaczmy zatem taką sytuację na przykładzie oraz potencjalny sposób jej rozwiązania.

W zaprezentowanym poniżej przykładzie zasymulowana została sytuacja próby jednoczesnej aktualizacji elementu listy przez dwóch użytkowników:

public partial class InitialPage : LayoutsPageBase
 {
    private SPList _employeesList; 
    public SPList EmployeesList
    {
        get
        {
            if (_employeesList != null)
            {
                return _employeesList;
            }
 
            return Web.GetList(string.Format("{0}{1}", Web.ServerRelativeUrl, "/Lists/EmployeesList")); 
        }
    }
 
    protected void Page_Load(object sender, EventArgs e)
    {
        try
        {
            if (Page.IsPostBack)
            {
                // clear error message
                lblErrorMessage.Text = string.Empty; 
            }
        }
        catch (Exception ex)
        {
            ShowErrorMessage(ex);
        }
    }
 
    /// <summary>
    /// Adds one item to list
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    protected void btnPopulateList_Click(object sender, EventArgs e)
    {
        try
        {
            SPListItem item = EmployeesList.AddItem();
            
            item[SPBuiltInFieldId.Title] = "First";
            item.Update(); 
        }
        catch (Exception ex)
        {
            ShowErrorMessage(ex);
        }
    }
 
    private void ShowErrorMessage(Exception ex)
    {
        lblErrorMessage.Text = ex.Message; 
    }
}
 

Powyższy kod jest wycinkiem dla utworzonego Application Page, przygotowującym do właściwych operacji. Przy deployu tworzona jest lista o nazwie “EmployeesList”, która po wciśnięciu przycisku “Populate list” wstawia na listę element o tytule “First”.

Zobaczmy teraz kod właściwy dla aktualizacji elementu listy przez dwóch użytkowników:

protected void btnSave_Click(object sender, EventArgs e)
{
    try
    {
        string name = txtName.Text.Trim();
        if (string.IsNullOrEmpty(name))
        {
            return;
        }
 
        // Simulate situation where two users try to update the same item at once
        SPListItem firstUserItem = EmployeesList.GetItemById(1);
        SPListItem secondUserItem = EmployeesList.GetItemById(1);
 
        // Try to update item 
        firstUserItem[SPBuiltInFieldId.Title] = name;
        secondUserItem[SPBuiltInFieldId.Title] = "concurrent change";
 
        firstUserItem.Update(); // ok - I was first
        secondUserItem.Update(); // error - save conflict
 
        // If exception not thrown then show message
        lblErrorMessage.Text = "Title of item has been changed";
    }
    catch (SPException ex)
    {
        ShowErrorMessage(ex); 
    }
    catch (Exception ex)
    {
        ShowErrorMessage(ex);
    }
}
Dla pierwszego użytkownika będziemy próbowali zmienić tytuł pobranego elementu na wpisany ciąg znaków do pola tekstowego. Drugi natomiast będzie próbował zmienić tytuł stałym wpisem “concurrent change”. Konflikt zapisu wystąpi w tym przypadku ponieważ dwóch użytkowników pobrało ten sam element z listy, pierwszy z nich zmienił tytuł i zdążył wykonać update na elemencie, zanim wykonał tę samą operację drugi. Zobaczmy jak to wygląda w praktyce:

conflict

Do pola tekstowego wpisany został tekst “changed by first”. Ze względu na to, że wystąpił konflikt zapisu dla drugiego użytkownika, który próbował zapisać tekst “concurrent change”, na liście pojawi się tekst wpisany do pola tekstowego:

conflict result

Rozwiązaniem dla konfliktu zapisu jest ponowienie operacji zapisu, z zaznaczeniem jednak, że element na którym chcemy wykonać operację musi posiadać aktualne dane zapisane do bazy contentowej. Element taki musi zatem zostać pobrany z listy ponownie w sytuacji kiedy wystąpił konflikt i jeśli ponownie nie zmieni on swoich danych w trakcie zapisu operacja zapisu powiedzie się. Wykrycie konfliktu zapisu następuje poprzez wyłapanie wyjątku typu SPException. Zobaczmy zatem odpowiednią modyfikację kodu dla zdarzenia btnSave_Click:

 

try
{
    // ... existing not modified coded
}
catch (SPException ex)
{
    // Exception has been thrown but that gave second user chance to update item once again 
    SPListItem secondUserConflictedItem = EmployeesList.GetItemById(1); 
    secondUserConflictedItem[SPBuiltInFieldId.Title] = "Save conflict but i'm gonna win :)";
    secondUserConflictedItem.Update();
}
catch (Exception ex)
{
    ShowErrorMessage(ex);
}
 

Po wyłapaniu wyjątku i zapisaniu zmian powinniśmy na liście zobaczyć zamiast tytułu elementu “changed by first” wstawiony w tym bloku tekst “Save conflict but i’m gonna win : )” :

second user wins

Reklamy

Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Wyloguj / Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Wyloguj / Zmień )

Zdjęcie na Facebooku

Komentujesz korzystając z konta Facebook. Wyloguj / Zmień )

Zdjęcie na Google+

Komentujesz korzystając z konta Google+. Wyloguj / Zmień )

Connecting to %s

%d blogerów lubi to: