2017-01-07 3 views
1

Mon code fonctionne correctement lorsqu'il est exécuté de manière synchrone, mais échoue lorsqu'il est déclenché via QueueBackgroundWorkItem. MISE À JOUR: le problème semble provenir du gestionnaire de module dynamique Sitefinity que j'utilise et qui nécessite un contexte HTTP dans le thread créé par QueueBackgroundWorkItem. Il s'agit donc d'une question spécifique à Sitefinity. Je l'ai trouvé un article qui semble pointer vers une solution: http://www.sitefinity.com/developer-network/forums/sitefinity-sdk/errors-with-managers-when-multi-threading.ToList sur IQueryable provoque NullReferenceException lorsqu'il est exécuté en arrière-plan

parties pertinentes de mon code

action ApiController qui commence tâche de fond:

// GET: api/Sync/MFP 
[HttpGet] 
public IHttpActionResult MFP() 
{ 
    HostingEnvironment.QueueBackgroundWorkItem(ct => startMFPSync()); 
    return Ok("Sync Started!"); 
} 

private void startMFPSync() 
{ 
    var sourcesConfig = Config.Get<SyncSourcesSettingsConfig>(); 

    MFPApi api = new MFPApi(new MFPConfig 
    { 
     Url = sourcesConfig.MFPUrl, 
     Key = sourcesConfig.MFPKey, 
     Password = sourcesConfig.MFPPassword, 
    }); 

    DataSync dataSync = new DataSync(api); 
    dataSync.RunSync(); 
} 

classe DataSync:

public class DataSync 
{ 
    IDataSource dataSource; 

    public DataSync(IDataSource source) 
    { 
     dataSource = source; 
    } 

    public void RunSync() 
    { 
     dataSource.GetResponse(); 

     List<SyncContent> dataToSync = dataSource.GetDataForSync(); 
     SFDynamicModuleSync destinationSync = new SFDynamicModuleSync(dataToSync); 

     // function call where exception occurs 
     destinationSync.CacheModuleData(); 

     // other sync operations 
    } 
} 

La fonction CacheModuleData() dans la classe ci-dessous est l'endroit où l'exception se produit:

public class SFDynamicModuleSync : IDataDestinationSync 
{ 
    /// <summary> 
    /// List of SyncContent objects to sync 
    /// </summary> 
    private List<SyncContent> dataToSync; 

    /// <summary> 
    /// Used to store results of CacheModuleData 
    /// </summary> 
    private List<List<DynamicContent>> modulesItems; 

    /// <summary> 
    /// Sitefinity dynamic module manager 
    /// </summary> 
    private DynamicModuleManager dynamicModuleManager; 

    /// <summary> 
    /// Initializes a new instance of the <see cref="SFDynamicModuleSync"/> class 
    /// </summary> 
    /// <param name="dataToSync">List of SyncContent objects to sync</param> 
    public SFDynamicModuleSync(List<SyncContent> dataToSync) 
    { 
     this.dataToSync = dataToSync; 
     this.modulesItems = new List<List<DynamicContent>>(); 
     this.dynamicModuleManager = DynamicModuleManager.GetManager(); 
    } 

    /// <summary> 
    /// Retrieves all data from dynamic modules and places in modulesItems 
    /// </summary> 
    public void CacheModuleData() 
    { 
     foreach (string contentType in this.dataToSync.Select(e => e.ContentTypeName)) 
     { 
      Type type = TypeResolutionService.ResolveType(contentType); 

      IQueryable<DynamicContent> moduleItems = this.dynamicModuleManager.GetDataItems(type) 
       .Where(i => i.Status == ContentLifecycleStatus.Master); 

      if(moduleItems != null) 
      { 
       // The .ToList() here causes a NullReferenceException when code is triggered by background job 
       List<DynamicContent> moduleItemsList = moduleItems.ToList(); 
       this.modulesItems.Add(moduleItemsList); 
      } 
     } 
    } 

    // other sync methods - not included here for abbrevity 
} 

Trace de la pile:

System.NullReferenceException was unhandled by user code 
    HResult=-2147467261 
    Message=Object reference not set to an instance of an object. 
    Source=Unity_ILEmit_DynamicClasses 
    StackTrace: 
     at DynamicModule.ns.Wrapped_OpenAccessDynamicModuleProvider_81d3fcbe95dd4a47b8c1cb1cc5a692ab.ApplyFilters(IDataItem item) 
     at Telerik.Sitefinity.Security.FieldsPermissionsApplierEnumerator`1.Demand(T forItem) 
     at Telerik.Sitefinity.Security.PermissionApplierEnumeratorBase`1.MoveNext() 
     at Telerik.Sitefinity.Data.Linq.DataItemEnumerator`1.MoveNext() 
     at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection) 
     at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source) 
     at TeamSI.Sitefinity.DataSync.DataDestinations.SFDynamicModuleSync.CacheModuleData() in C:\Projects\SIEQ\TeamSI.Sitefinity.DataSync\DataDestinations\SFDynamicModuleSync.cs:line 75 
     at TeamSI.Sitefinity.DataSync.DataSync.RunSync() in C:\Projects\SIEQ\TeamSI.Sitefinity.DataSync\DataSync.cs:line 28 
     at SitefinityWebApp.Mvc.Controllers.SyncController.startMFPSync() in C:\Projects\SIEQ\Main_Site\Mvc\Controllers\SyncController.cs:line 72 
     at SitefinityWebApp.Mvc.Controllers.SyncController.<MFP>b__1_0(CancellationToken ct) in C:\Projects\SIEQ\Main_Site\Mvc\Controllers\SyncController.cs:line 52 
     at System.Web.Hosting.HostingEnvironment.<>c__DisplayClass91_0.<QueueBackgroundWorkItem>b__0(CancellationToken ct) 
     at System.Web.Hosting.BackgroundWorkScheduler.<RunWorkItemImpl>d__7.MoveNext() 
    InnerException: 
+0

est-il tout ce que je peux ajouter qui rendrait la question plus compréhensible ou utile aux autres? Je réalise que c'est une question plutôt spécifique, mais j'aimerais en faire une question qui pourrait aider les autres dans des situations similaires. – jmotes

+0

pourquoi ne pas simplement vérifier si 'moduleItems' est null? Cela ressemble à une question sans question. – Hogan

+0

@Hogan, ce serait une bonne idée - mais dans ce cas précis, il ne devrait pas être nul puisqu'il y a des éléments de module dans ma base de données et qu'il y a plus de 500 éléments quand ils ne sont pas déclenchés par le travail d'arrière-plan. – jmotes

Répondre

1

Transforme c'était un problème avec le CMS Sitefinity j'Interrogation des données de contexte nécessitant HTTP, ce qui est bien sûr perdu dans les discussions. L'exception NullReferenceException s'est produite dans la méthode dynamicModuleManager.GetDataItems(). Avec l'aide d'amis et de super contributeurs, y compris @Igor, @Hogan, @PeterBons et @daramasala, qui m'ont aidé à comprendre le problème que j'ai pu résoudre en augmentant les privilèges de SF et en simulant HttpContext dans le fil.

http://www.sitefinity.com/developer-network/forums/sitefinity-sdk/errors-with-managers-when-multi-threading

Seule la documentation SF en dehors des forums que je pouvais trouver sur l'utilisation de cette méthode est ici: http://docs.sitefinity.com/bug-tracker-create-the-savebug-action

Mise à jour méthode RunSync() dans ma classe DataSync:

public void RunSync() 
{ 
    SystemManager.RunWithElevatedPrivilegeDelegate worker = new SystemManager.RunWithElevatedPrivilegeDelegate(args => { 

     dataSource.GetResponse(); 
     List<SyncContent> dataToSync = dataSource.GetDataForSync(); 
     var destinationSync = new SFDynamicModuleSync(dataToSync); 

     destinationSync.CacheModuleData(); 

     // complete sync operations for each content type 
     for (int i = 0; i < dataToSync.Count; i++) 
     { 
      destinationSync.DeleteOldItems(i); 
      destinationSync.AddItems(i); 
      destinationSync.UpdateItems(i); 
     } 
    }); 

    SystemManager.RunWithElevatedPrivilege(worker); 
}