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.