Swojego czasu opisałem implementację Field Mappera dla SharePoint wraz z wykorzystaniem jego w wzorcu repozytorium. Jednakże pominąłem jedną istotną kwestią o jaką ostatnio dostałem zapytanie:
Jak wykorzystać repozytorium z filed mapper w event receiver w SharePoint?
W przykładach pokazywałem iż możemy za pomocą SharePointServiceLocator uzyskać implementację danego interfejsu repozytorium i następnie już operować na danej implementacji.
_activityTypeRepository = SharePointServiceLocator.Current.GetInstance<IActivityTypeRepository>(); ActivityType at = _activityTypeRepository.GetById(1); at.Name = "jola"; _activityTypeRepository.Update(at);
Jednakże sytuacja ciutkę inaczej wygląda z Event Receivers w których nie ma dostępu do obiektu HttpContext.Current, przez co także do SPContext.Current.
Jest to częściowo nie prawda gdyż przy synchronicznych zdarzeniach możemy dostać się do obiektu HttpContext ale jedynie w konstruktorze klasy. Problem jest trochę bardziej zabawny gdyż nie przy wszystkich synchronicznych zdarzeniach ten kontekst będzie… o dziwo, z tego co pamiętam problem taki istniał w ItemDeleting (do sprawdzenia, nie dam sobie teraz ręki uciąć:)).
Dlatego też cały SharePointServiceLocator nie będzie nam poprawnie działał w Event Receivers. Obejściem problemu jest proste, ale przez to też nie możemy korzystać z service locator. Mianowicie w moich repozytoriach istnieje przeciążenie konstruktora przejmującego jako parametr aktualny obiekt SPWeb.
public class LogRepository : BaseEntityRepository<Log>, ILogRepository { public LogRepository() { this.Initialize(Constants.ListLogsIdConfigKey); base.ListItemFieldMapper.AddMapping(LogFields.Id, log => log.Id); base.ListItemFieldMapper.AddMapping(LogFields.UniqueId, log => log.UniqueId); base.ListItemFieldMapper.AddMapping(LogFields.Title, log => log.Title); base.ListItemFieldMapper.AddMapping(LogFields.LogDate, log => log.Date); base.ListItemFieldMapper.AddMapping(LogFields.LogDesc, log => log.Message); base.ListItemFieldMapper.AddMapping(LogFields.LogFrom, log => log.FromIp); base.ListItemFieldMapper.AddMapping(LogFields.LogItem, log => log.Item); base.ListItemFieldMapper.AddMapping(LogFields.LogList, log => log.List); base.ListItemFieldMapper.AddMapping(LogFields.LogUser, log => log.ByUser); base.ListItemFieldMapper.AddMapping(LogFields.LogType, log => log.LogType); } public LogRepository(SPWeb web) { this.Initialize(Constants.ListLogsIdConfigKey, web); base.ListItemFieldMapper.AddMapping(LogFields.Id, log => log.Id); base.ListItemFieldMapper.AddMapping(LogFields.UniqueId, log => log.UniqueId); base.ListItemFieldMapper.AddMapping(LogFields.Title, log => log.Title); base.ListItemFieldMapper.AddMapping(LogFields.LogDate, log => log.Date); base.ListItemFieldMapper.AddMapping(LogFields.LogDesc, log => log.Message); base.ListItemFieldMapper.AddMapping(LogFields.LogFrom, log => log.FromIp); base.ListItemFieldMapper.AddMapping(LogFields.LogItem, log => log.Item); base.ListItemFieldMapper.AddMapping(LogFields.LogList, log => log.List); base.ListItemFieldMapper.AddMapping(LogFields.LogUser, log => log.ByUser); base.ListItemFieldMapper.AddMapping(LogFields.LogType, log => log.LogType); } // CUT }
Metoda Initialize pochodzi z klasy bazowej:
protected virtual void Initialize(string configKey) { var hierarchicalConfig = SharePointServiceLocator.Current.GetInstance<IConfigManager>(); ListId = hierarchicalConfig.GetFromPropertyBag<Guid>(configKey, SPContext.Current.Web); List = SPContext.Current.Web.Lists[ListId]; Logger = SharePointServiceLocator.Current.GetInstance<ILogger>(); } protected virtual void Initialize(string configKey, SPWeb web) { var hierarchicalConfig = SharePointServiceLocator.Current.GetInstance<IConfigManager>(); Logger = SharePointServiceLocator.Current.GetInstance<ILogger>(); Logger.TraceToDeveloper("hierarchicalConfig is null: " + (hierarchicalConfig == null).ToString()); ListId = hierarchicalConfig.GetFromPropertyBag<Guid>(configKey, web); List = web.Lists[ListId]; }
Uwaga. Pewne odwołoania do SharePointServiceLocator są możliwe nawet bez SPWeb/SPSite.
Wystarczy więc iż w naszym Event Receiver dostaniemy się do obiektu SPWeb i następnie przekażemy go do konstruktora konkretnej implementacji repozytorium. I to jest IMO najlepsze wyjście. Nie polegałbym na konstruktorze naszego Event Receiver.
Czyli kod mniej więcej będzie wyglądał tak:
public override void ItemDeleting(SPItemEventProperties properties) { try { SPSite site = new SPSite(properties.SiteId); SPWeb web = site.OpenWeb(); ILogRepository = new LogRepository(web); // CUT } // CUT }
Oczywiście chętni mogą naprawić implementację servie locator od MS tak by brał on pod uwagę obiekt SPWeb/SPSite co by dało możliwość korzystania z niego w Event Receivers.
PS.: wpis ku pamięci oraz dla tych, którzy będą kiedyś szukać rozwiązania tego problemu.
Witaj,
Post na podstawie mojego maila. Jak miło :)
Twoją implementację mappera wykorzystałem w nowym rozwiązaniu i muszę powiedzieć, że jest bardzo spoko. Zdecydowanie bardziej przypadł mi do gustu niż beznadziejny spmetal…
Jeszcze raz dzięki za pomoc.
:)
ql, ciesze sie ze kod sie przydaje :)
Comments are closed.