1

サービス機能を提供するコードをロードする Orchard CMS モジュールがあります。サービス コードは、ホストに依存しないように記述されており、以前は ASP.NET および WCF で使用されていました。サービス コードは MEF を使用してプラグインをロードします。そのようなプラグインの 1 つが監査用です。

監査のために 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;
    }

モデルで 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 メソッドを定義します。サービスが監査を書き込む必要がある場合、サービスは、MEF を介して、または起動時にインスタンスを渡すことによってインスタンス化された FlowAuditSink 抽象クラスから派生したすべてのインスタンスで監査メソッドを呼び出します。

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

"Activated" イベント中にサービスが開始さ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 経由ではなく、サービス コードによって作成されたスレッドから来ているためだと思います。

監査データをオーチャード データベースに表示する方法を誰か教えてもらえますか?

ありがとう

クリス。

4

1 に答える 1

2

まあ、私には解決策がありますが、私はオーチャードのアーキテクチャにあまり詳しくないので、最善の方法ではないかもしれません.

オーチャードの情報源をかなり掘り下げた後、この問題の核心は次のように要約できることに気づきました。

「Http リクエスト パイプラインを使用しないスレッドからオーチャードの 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動作し、私のデータはオーチャード DB に正常に書き込まれます。

現時点では、ISingletonDependency の使用が正しく機能しているとは思いません。コンストラクターは、モジュールがそのコンストラクターに AuditWriter インスタンスを挿入したときにのみ呼び出され、それが複数回発生するためです。

とにかく、HTTP 以外のスレッドから Orchard の autofac 解決メカニズムにアクセスするには、IWorkContextAccessor を使用するようです。

これが正しくない場合はお知らせください。

于 2013-11-06T14:08:52.717 に答える