Wyobraźcie sobie sytuacje, gdy za każdym razem gdy zostanie dodany nowy element do listy lub jego pole Assigned To zmieni się, musicie zmienić uprawnienia do elementu na podstawie własnie tego pola.

Bardzo prosty przykład w jaki sposób można spowodować deadlock. Kiedy użytkownik doda element wywoła on u was triger na ItemAdded, który w swym kodzie zaktualizuje uprawnienia na elemencie. Aktualizacja ta wymaga wywołania IteamUpdated. Gdzie znów będziecie próbować zmienić uprawnienie.

Typowy błąd występujący w tej okoliczności to:

Save Conflict Your changes conflict with those made concurrently by another user. If you want your changes to be applied, click Back in your Web browser, refresh the page, and resubmit your changes.

Jednak można go się pozbyć :)

Wystarczy, że w metodzie ItemAdded i ItemUpdated wywołacie następujący kod na początku metody (lub w momencie kiedy wykonujecie aktualizację elementu):

this.DisableEventFiring();

Oraz na końcu metody (lub po skończeniu aktualizacji elementu):

this.EnableEventFiring();

Ta mała zmiana zablokuje wywoływanie eventów w danym obszarze kodu. Także zablokuje wywołanie eventów na innych listach. To znaczy, że jeżeli wywołacie DisableEventFiring a następnie zaktualizujecie element na innej liście do której jest podpięty SPItemEventReceiver to ten event receiver też nie zostanie wywołany.

Ma to swoje zalety jak i wady. Sami zdecydujcie. Mi osobiście DisableEventFiring i równoważna metoda EnableEvnetFiring bardzo pomagają, a przy rozsądnym ich wykorzystaniu nie przeszkadzają :)

3 KOMENTARZE

  1. Witam,
    Możliwe, że trochę nie na temat ale…
    Jak utrzymać zmienną pomiędzy wywołaniami itemUpdating oraz itemUpdated.
    Chciałbym wywołać workflow z poziomu metody itemUpdated jeżeli pole “Status” zmieni się z wartości “Odłożony” na wartość “Ukończono”. Przechwytuję daną zmianę w zdarzeniu itemUpdating i stosuję statyczną zmienną bool informującą, że status się zmienił według warunków. Problem mam z zychnornizacją tej zmiennej (dwa zdarzenia itemUpdating w tym samym czasie mogą namieszać tak, że zdarzenia itemUpdated “zobaczą”, że globalna zmienna jest ustawiona na true i rozpoczną operację wywołwania workflowa.

    Pozdrawiam
    peterson

  2. @peterson

    nie ma do tego best practices. W zaleznosci od zapotrzebowania (i liczby uzytkownikow) robilem to na trzy sposoby:
    1) Dodalem pole do listy Users ktore trzymalo mi informacje na temat aktualnego stanu ankiety. Pole to ukrylem przed uzytkownikiem za pomoca wlasnego list iteratora podpietego pod user info.
    2) Dzialanie na web Properties z identyfikatorem uzytkownika jako czesc klucza (dobre przy malej liczbie uzytkownikow)

    Ogolnie z Ankietami jest przwalone. I jeden z najlepszych sposob jaki zrobilem a ktory wplywa na wszystkie ankiety na danym serwerze (instalacji SharePoint) to sposob 3:
    a) tworzysz wlasne linki do wypelnienia ankiety i jej edycji.
    b) na zdarzeniu on click tworzysz sobie zmienna sesyjna dla uzytkownika z odowiednimi informacjami
    c) dodajesz ukryute pole na uzytkowniku przechowujace SurveyID, ItemID w jakis rozsadny sposob tak by mozna bylo tam przechowywac wiele takich tuples
    d) przeciazasz itemadding, itemupdating i itemdeleting porownujac informacje zarowno z zmiennej sesyjnej jak i informacje z listy uzytkownika, na przyklad itemupdating:
    public override void ItemUpdating(SPItemEventProperties properties)
    {
    #region Get Values for Survey Is Complated Test

    object objLevel = properties.ListItem[“Level”];
    int level = Convert.ToInt32(objLevel);
    object ctype = properties.AfterProperties[“ContentType”];

    #endregion Get Values for Survey Is Complated Test

    #region Adding additional informations to Filled Survey

    if (this.Context.Session[Consts.USER_FILLED_SURVEY] != null)
    {
    FilledSurvey fs = (FilledSurvey)this.Context.Session[Consts.USER_FILLED_SURVEY];

    fs.ItemId = properties.ListItemId;
    fs.ListId = properties.ListId;

    this.Context.Session[Consts.USER_FILLED_SURVEY] = fs;
    }

    #endregion Adding additional informations to Filled Survey

    // Klikniecie na Save lub Finish.
    // Nie sprawdzam level, jako ze przy 1 jak i przy 255 mozna kliknac na Save/Finish.
    if (ctype != null)
    {
    // Jezeli przed chwila dodalismy ID do sesji, i wciaz jestesmy na ankiecie
    // to nalezy go usunac by nam nie zawadzal dalej.
    if (this.Context.Session[Consts.USER_SUBMITED_SURVEY_ID] != null)
    {
    this.Context.Session.Remove(Consts.USER_SUBMITED_SURVEY_ID);
    }

    this.Context.Session.Add(Consts.USER_SUBMITING_SURVEY, true);
    this.Context.Session.Add(Consts.USER_SUBMITING_SURVEY_LEVEL, level);
    }
    }

    jezeli masz synchroniczne zdarzenia to masz dostep do contextu:

    public SurveyItemEventReceiver()
    {
    this._current = HttpContext.Current;
    }

    Nastepnie przeciazylem przycisk Save/Finish ktory przekierowywal mnie na odpowiednia strone po zakonczeniu i uruchamial to co trzeba.

    public class SurveySaveButton : SaveButton
    {
    #region Events
    ///

    /// Sets session object to include information’s if user is on the last page of the survey.
    ///

    /// The instance containing the event data.
    protected override void OnPreRender(EventArgs e)
    {
    base.OnPreRender(e);

    Button button = (Button)this.TemplateContainer.FindControl(“diidIOSaveItem”);
    if (button != null)
    {
    // Blokada przycisku by na pierwszej stronie nie byl dostepny
    switch (ItemContext.FormContext.FormMode)
    {
    case SPControlMode.New:
    if (ItemContext.ItemId <= 0) button.Enabled = false; break; default: button.Enabled = true; break; } if (button.Text.Contains("inis")) { if (HttpContext.Current.Session[Consts.USER_FINISHED_SURVEY] == null) { HttpContext.Current.Session.Add(Consts.USER_FINISHED_SURVEY, true); } else { HttpContext.Current.Session[Consts.USER_FINISHED_SURVEY] = true; } } } else { if (HttpContext.Current.Session[Consts.USER_FINISHED_SURVEY] == null) { HttpContext.Current.Session.Add(Consts.USER_FINISHED_SURVEY, false); } else { HttpContext.Current.Session[Consts.USER_FINISHED_SURVEY] = false; } } } #endregion Events #region Public Properties ///

    /// Gets or sets the redirect URL for Save/Finish and Cancel operations.
    ///

    /// The redirect URL for Save/Finish and Cancel operations.
    public new string RedirectUrl
    {
    get
    {
    return base.RedirectUrl;
    }
    set
    {
    #region Set Item ID based on FormMode
    string itemId = string.Empty;

    switch (ItemContext.FormContext.FormMode)
    {
    case SPControlMode.New:
    if (ItemContext.ItemId <= 0) itemId = "-1"; else itemId = ItemContext.ItemId.ToString(); break; default: itemId = ItemContext.ItemId.ToString(); break; } #endregion Set Item ID based on FormMode #region Get the full Web URL so Ivalid Page URL error will not be shown string defaultViewUrl = ItemContext.List.DefaultViewUrl; defaultViewUrl = defaultViewUrl.Substring(0, defaultViewUrl.LastIndexOf('/')); #endregion Get the full Web URL so Ivalid Page URL error will not be shown base.RedirectUrl = string.Format(Links.RedirectUrl, defaultViewUrl, value, ItemContext.ListId.ToString(), itemId); } } ///

    /// Gets the default name of the template.
    ///

    /// The default name of the template.
    protected override string DefaultTemplateName
    {
    get
    {
    return “SaveButton”;
    }
    }

    #endregion Public Properties
    }

    Mimo ze to ogolnie dziala, to NIE POLECAM sie bawic tak w Surveys :( Lepiej napisac wlasny system ankiet lub wykorzystac istniejacy – i.e. KWizCom ma takie cos.

    Surveys w WSS/MOSS sa specyficzna odmiana document library, ktora zezwala tylko RAZ na checkout (uncompleted) i raz na checkin (completed). Kazda kolejna edycja ankiety po jej zakonczeniu bedzie dawala Ci info ze jest ona zakonczona nie zaleznie na jakim jej kroku skonczysz.

    Dziwie sie ze to zostalo tak oprogramowane :( ale co zrobic.

    jakbys mial jeszcze jakies pytania to odezwij sie do mnie na maila ktory jest na stronie http://ms-groups.pl/pgs/Wiki/O%20Nas.aspx

    Gutek

  3. Super!
    Dzięki za odpowiedź ;) Na pewno coś z tego wyciągnę dla siebie ;)
    Pozdrawiam
    Piotr

Comments are closed.