I have a quite complex application that is split into several features.
In order to hide this complexity, I set the IsHidden property of the feature to true, then have created a "master" feature. This feature have a custom feature receiver which purpose is to chain properly the activation of the hidden features.
I use this code :
// Order matters !
private static readonly IEnumerable<Guid> g_SiteFeatures = new string[] {
"8a190c52-37e2-4dc9-bd7d-10a1aa3574eb" // Feat1
}.Select(idStr => new Guid(idStr));
// Order matters !
private static readonly IEnumerable<Guid> g_WebFeatures = new string[] {
"b004f353-38f9-46d4-bd15-20644aa525c5", // Feat2
"fde2b1cc-51e1-4654-b754-addcb571ce12" // Feat3
}.Select(idStr => new Guid(idStr));
private void ActivateHiddenFeatures(SPSite site, SPWeb web)
{
foreach (var siteFeature in g_SiteFeatures)
{
if (site.Features.All(f => f.DefinitionId != siteFeature)) // Avoid to fail with already activated features
{
site.Features.Add(siteFeature);
}
}
foreach (var webFeature in g_WebFeatures)
{
if (web.Features.All(f => f.DefinitionId != webFeature)) // Avoid to fail with already activated features
{
web.Features.Add(webFeature);
}
}
}
This code works, but I'm experiencing randomly failures in this code. Especially, I have failures stating that some list does not exists. However, this lists is created by one the hidden features.
If I let visual studio activate the feature in place of my master feature, everything works fine.
If I attach a debugger to the receiver of the master feature, and I "wait" a bit, the code runs correctly.
All this evidence let me think that the Add method of the site.Features and web.Features are not synchronous, but asynchronous. However, nothing in the documentation is stating something like this.
FYI, the code that fails is an event receiver that intercept ItemAdded events on one of the list I created in one the hidden feature. Just after activating the features, I inject some data :
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
System.Diagnostics.Debugger.Break();
properties.RunContextualized((web, site) => // Utility method that extract web and site from properties
{
ActivateHiddenFeatures(site, web);
LoadInitialData(web, site);
});
}
private void LoadInitialData(SPWeb web, SPSite site)
{
// Insert some data in on the list created from a hidden feature
// the insertion is made using a SPMetal generated context
}
I'm quite stuck with these situation, as I experience an unexpected behavior.
I no solution is found, I'll end-up by writing a custom timer job to inject the data later, but it will lead to other functional issues.