Note that there are some explanatory texts on larger screens.

plurals
  1. POHow to write data to the Orchard CMS repository from a non HTTP thread
    primarykey
    data
    text
    <p>I have an Orchard CMS module that loads up some code which provides service functions. The service code is written to be host agnostic and has been used with ASP.NET and WCF previously. The service code uses MEF to load plugins. One such plugin is for audit. </p> <p>In an attempt to allow access to the Orchard database for audit I have modified the service code to also allow the host to pass in an audit implementation instance. Thus my Orchard module can pass in an instance when the service starts with the intention that this instance writes audit data as records in the Orchard DB.</p> <p>I have created a migration for my database:</p> <pre><code> public int UpdateFrom5() { SchemaBuilder.CreateTable("AuditRecord", table =&gt; table .Column&lt;int&gt;("Id", c =&gt; c.PrimaryKey().Identity()) .Column&lt;int&gt;("AuditPoint") .Column&lt;DateTime&gt;("EventTime") .Column("CampaignId", DbType.Guid) .Column("CallId", DbType.Guid) .Column&lt;String&gt;("Data") ); return 6; } </code></pre> <p>I have created my AuditRecord model in Models:</p> <pre><code>namespace MyModule.Models { public class AuditRecord { public virtual int Id { get; set; } public virtual int AuditPoint { get; set; } public virtual DateTime EventTime { get; set; } public virtual Guid CampaignId { get; set; } public virtual Guid CallId { get; set; } public virtual String Data { get; set; } } } </code></pre> <p>I have added an IAuditWriter interface that derives from IDependency so that I can inject a new instance when my module starts.</p> <pre><code>public interface IAuditWriter : IDependency { void WriteAuditRecord(AuditRecord data); } </code></pre> <p>For my audit writer instance to work with the existing service code it must be derived from an abstract class FlowSinkAudit defined in the service library. The abstract class defines the Audit method. When the service needs to write audit it calls the audit method on all instances derived from the FlowAuditSink abstract class that have been instantiated either through MEF or by passing in an instance at startup. </p> <pre><code>public class AuditWriter : FlowAuditSink, IAuditWriter { private readonly IComponentContext ctx; private readonly IRepository&lt;AuditRecord&gt; repo; public AuditWriter(IComponentContext ctx, IRepository&lt;AuditRecord&gt; repo) { this.ctx = ctx; this.repo = repo; } public void WriteAuditRecord(AuditRecord data) { // Get an audit repo //IRepository&lt;AuditRecord&gt; repo = (IRepository&lt;AuditRecord&gt;)ctx.Resolve(typeof(IRepository&lt;AuditRecord&gt;)); using (System.Transactions.TransactionScope t = new System.Transactions.TransactionScope(System.Transactions.TransactionScopeOption.Suppress)) { this.repo.Create(data); } } public override void Audit(DateTime eventTime, AuditPoint auditPoint, Guid campaignId, Guid callId, IDictionary&lt;String, Object&gt; auditPointData) { // Add code here to write audit into the Orchard DB. AuditRecord ar = new AuditRecord(); ar.AuditPoint = (int)auditPoint; ar.EventTime = eventTime; ar.CampaignId = campaignId; ar.CallId = callId; ar.Data = auditPointData.AsString(); WriteAuditRecord(ar); } } </code></pre> <p>My service code is started from a module level class that implements IOrchardShellEvents</p> <pre><code>public class Module : IOrchardShellEvents { private readonly IAuditWriter audit; private readonly IRepository&lt;ServiceSettingsPartRecord&gt; settingsRepository; private readonly IScheduledTaskManager taskManager; private static readonly Object syncObject = new object(); public ILogger logger { get; set; } public Module(IScheduledTaskManager taskManager, IRepository&lt;ServiceSettingsPartRecord&gt; settingsRepository, IAuditWriter audit) { this.audit = audit; this.settingsRepository = settingsRepository; this.taskManager = taskManager; logger = NullLogger.Instance; } ... </code></pre> <p>When the service is started during the "Activated" event, I pass <code>this.Audit</code> to the service instance. </p> <pre><code> public void Activated() { lock (syncObject) { var settings = settingsRepository.Fetch(f =&gt; f.StorageProvider != null).FirstOrDefault(); InitialiseServer(); // Auto start the server if (!StartServer(settings)) { // Auto start failed, setup a scheduled task to retry var tasks = taskManager.GetTasks(ServerAutostartTask.TaskType); if (tasks == null || tasks.Count() == 0) taskManager.CreateTask(ServerAutostartTask.TaskType, DateTime.Now + TimeSpan.FromSeconds(60), null); } } } ... private void InitialiseServer() { if (!Server.IsInitialized) { var systemFolder = @"C:\Scratch\Plugins"; if (!Directory.Exists(systemFolder)) Directory.CreateDirectory(systemFolder); var cacheFolder = System.Web.Hosting.HostingEnvironment.MapPath("~/App_Data/MyModule/Cache"); if (!Directory.Exists(cacheFolder)) Directory.CreateDirectory(cacheFolder); Server.Initialise(systemFolder, cacheFolder, null, (FlowAuditSink)audit); } } </code></pre> <p>All of this works as expected and my service code calls the audit sink.</p> <p>My problem is that when the audit sink is called and I try to write the audit to the database using <code>this.repo.Create(data)</code> nothing is written.</p> <p>I have also attempted to create a new repository object by using the IComponentContext interface but this errors with object already disposed. I assume this is because the audit sink is a long lived object instance. </p> <p>I have attempted both with and without the current transaction suspended which doesn't affect the result. I assume this is because the call is not coming through ASP.NET MVC but from a thread created by the service code.</p> <p>Can anyone tell my how I can get my audit data to appear in the Orchard database?</p> <p>Thanks</p> <p>Chris.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload