我有一个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;
}
...
在&#34;激活&#34;期间启动服务时事件,我将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数据库中吗?
由于
克里斯。
答案 0 :(得分:2)
嗯,我有一个解决方案,但由于我对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
如果不正确,请告诉我。