【问题标题】:How to write data to the Orchard CMS repository from a non HTTP thread如何从非 HTTP 线程将数据写入 Orchard CMS 存储库
【发布时间】:2013-11-05 23:32:20
【问题描述】:

我有一个 Orchard CMS 模块,它加载了一些提供服务功能的代码。服务代码被编写为与主机无关,之前已与 ASP.NET 和 WCF 一起使用。服务代码使用 MEF 加载插件。一个这样的插件用于审计。

为了允许访问 Orchard 数据库进行审计,我修改了服务代码以允许主机传入审计实现实例。因此,我的 Orchard 模块可以在服务启动时传入一个实例,目的是让该实例将审计数据作为记录写入 Orchard DB。

我已经为我的数据库创建了迁移:

    public int UpdateFrom5()
    {
        SchemaBuilder.CreateTable("AuditRecord",
            table => table
                .Column<int>("Id", c => c.PrimaryKey().Identity())
                .Column<int>("AuditPoint")
                .Column<DateTime>("EventTime")
                .Column("CampaignId", DbType.Guid)
                .Column("CallId", DbType.Guid)
                .Column<String>("Data")
                );
        return 6;
    }

我已经在 Models 中创建了我的 AuditRecord 模型:

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; }
    }
}

我添加了一个派生自 IDependency 的 IAuditWriter 接口,以便我可以在我的模块启动时注入一个新实例。

public interface IAuditWriter : IDependency
{
    void WriteAuditRecord(AuditRecord data);
}

要让我的审计编写器实例与现有服务代码一起工作,它必须派生自服务库中定义的抽象类 FlowSinkAudit。抽象类定义了 Audit 方法。当服务需要编写审计时,它会在从 FlowAuditSink 抽象类派生的所有实例上调用审计方法,这些实例已通过 MEF 或通过在启动时传入实例进行实例化。

public class AuditWriter : FlowAuditSink, IAuditWriter
{
    private readonly IComponentContext ctx;
    private readonly IRepository<AuditRecord> repo;
    public AuditWriter(IComponentContext ctx, IRepository<AuditRecord> repo)
    {
        this.ctx = ctx;
        this.repo = repo;
    }

    public void WriteAuditRecord(AuditRecord data)
    {
        // Get an audit repo
        //IRepository<AuditRecord> repo = (IRepository<AuditRecord>)ctx.Resolve(typeof(IRepository<AuditRecord>));
        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<String, Object> 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);
    }
}

我的服务代码是从实现 IOrchardShellEvents 的模块级类开始的

public class Module : IOrchardShellEvents
{
    private readonly IAuditWriter audit;
    private readonly IRepository<ServiceSettingsPartRecord> settingsRepository;
    private readonly IScheduledTaskManager taskManager;
    private static readonly Object syncObject = new object();

    public ILogger logger { get; set; }

    public Module(IScheduledTaskManager taskManager, IRepository<ServiceSettingsPartRecord> settingsRepository, IAuditWriter audit)
    {
        this.audit = audit;
        this.settingsRepository = settingsRepository;
        this.taskManager = taskManager;
        logger = NullLogger.Instance;
    }
...

在“已激活”事件期间启动服务时,我将this.Audit 传递给服务实例。

    public void Activated()
    {
        lock (syncObject)
        {
            var settings = settingsRepository.Fetch(f => 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);
        }
    }

所有这些都按预期工作,我的服务代码调用了审计接收器。

我的问题是,当调用审计接收器并且我尝试使用 this.repo.Create(data) 将审计写入数据库时​​,没有写入任何内容。

我还尝试使用 IComponentContext 接口创建一个新的存储库对象,但是这个错误与对象已经被释放。我认为这是因为审计接收器是一个长期存在的对象实例。

我已经尝试过暂停和不暂停当前事务,这不会影响结果。我认为这是因为调用不是来自 ASP.NET MVC,而是来自服务代码创建的线程。

谁能告诉我如何才能让我的审计数据出现在 Orchard 数据库中?

谢谢

克里斯。

【问题讨论】:

  • 你的 repo.Create 方法是如何工作的?如果可能的话,你能写出这个方法的实现吗?
  • @Mecek - 我还没有实现 repo.Create,我只是调用它。我错过了什么吗?
  • 你确定 Create 方法工作正常吗?
  • @Mecek - 如果我从控制器中的 Http 请求线程调用 repo.Create 它工作正常。在这种情况下,存储库实例由 Autofac 注入到控制器的构造函数中。问题似乎是 Orchard 存储库实现在处理 repo 对象之前不会完成事务,并且没有为应用程序提供任何实现该目标的方法。每次我需要通过实现 IShim 创建记录时,我都尝试解析一个新的 repo 对象,但是当我尝试解析 IRepository 时,我收到的 IOrcharHostContainer 返回 null
  • 如果您使用的是 nhibernate,您的 nhibernate 会话对象可能需要在释放之前刷新。我需要知道的是您使用哪种工具来查询您的数据库。可能是你调用 create 但你的会话没有刷新。

标签: nhibernate orchardcms autofac orchardcms-1.7


【解决方案1】:

好吧,我有一个解决方案,但由于我对 Orchards 架构不是很熟悉,这可能不是最好的方法。

在深入研究了 Orchard 的资源之后,我发现这个问题的症结可以概括为

“如何从不使用 Http 请求管道的线程访问 Orchard autofac 注入机制”。

我认为这是计划任务必须执行的操作,因此我创建了计划任务并在 IScheduledTaskHandler.Process 中设置断点以了解任务的执行方式。查看 Orchard\Tasks\SweepGenerator.cs 为我指明了方向。

我因此修改了我的 AuditWriter:

public interface IAuditWriter : ISingletonDependency
{
}

public class AuditWriter : FlowAuditSink, IAuditWriter
{
    private readonly IWorkContextAccessor _workContextAccessor;

    public AuditWriter(IWorkContextAccessor workContextAccessor)
    {
        _workContextAccessor = workContextAccessor;
    }

    public override void Audit(DateTime eventTime, AuditPoint auditPoint, Guid campaignId, Guid callId, IDictionary<String, Object> 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();

        using (var scope = _workContextAccessor.CreateWorkContextScope())
        {
            // resolve the manager and invoke it
            var repo = scope.Resolve<IRepository<AuditRecord>>();
            repo.Create(ar);
            repo.Flush();
        }
    }
}

scope.Resolve 有效,我的数据已成功写入 Orchard DB。

目前,我认为我对 ISingletonDependency 的使用不能正常工作,因为我的构造函数仅在我的模块在其构造函数中注入 AuditWriter 实例并且它多次发生时才被调用。

无论如何,为了从非 Http 线程访问 Orchard autofac 解析机制,我们使用 IWorkContextAccessor

如果这不正确,请告诉我。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-08-25
    • 1970-01-01
    • 2013-10-13
    • 1970-01-01
    • 2012-05-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多