2

ユーザーがログインしたり、パスワードや電子メールを変更したりできるシステムに基本的な監査を実装しようとしています。

監査したい関数はすべてビジネス層にあり、結果を含めて関数が呼び出された日時を格納する Audit オブジェクトを作成したいと考えています。

私は最近カンファレンスに参加しましたが、そのセッションの 1 つは巧妙に作成された Web アプリケーションに関するもので、いくつかのアイデアを実装しようとしています。基本的に、列挙型を使用して関数の結果を返し、switch ステートメントを使用してそのレイヤーの UI を更新しています。関数は、監査の作成、設定、および保存のための時間を残さない早期復帰を使用します。

私の質問は、ビジネス機能を監査するときに他の人がとるアプローチと、私のような機能があればどのようなアプローチをとるかということです (あなたがそれを捨てると言うなら、私は耳を傾けますが、私は不機嫌になります)。

コードは次のようになります。

function Login(string username, string password)
{
 User user = repo.getUser(username, password);

 if (user.failLogic1) { return failLogic1Enum; }
 if (user.failLogic2) { return failLogic2Enum; }
 if (user.failLogic3) { return failLogic3Enum; }
 if (user.failLogic4) { return failLogic4Enum; }

 user.AddAudit(new (Audit(AuditTypeEnum LoginSuccess));
 user.Save();

 return successEnum;
}

if ステートメントを拡張して、それぞれに新しい監査を作成することもできますが、関数が乱雑になり始めます。switch ステートメントの UI レイヤーで監査を行うことができましたが、それは間違っているようです。

すべてを try catch に finally で貼り付け、finally を使用して Audit オブジェクトを作成し、その情報をそこに設定して早期復帰の問題を解決するのは本当に悪いことですか? 私の印象では、finally は監査用ではなくクリーンアップ用です。

私の名前はデビッドです。より良いコードになろうとしています。ありがとう。

4

2 に答える 2

2

使ったことがあるとは言えませんが、これはアスペクト指向プログラミングの候補のようです。基本的に、自動化された方法で、ロギング/監査などのようなものの各メソッド呼び出しにコードを挿入できます。

これとは別に、try / catch / finalブロックを作成することは理想的ではありませんが、コスト/メリットを実行して、それが価値があるかどうかを確認します。コードを使用する必要がないように、コードを安価に合理的にリファクタリングできる場合は、それを実行します。コストが法外な場合は、最後に試してみます。多くの人が「最善の解決策」にとらわれていると思いますが、時間とお金は常に制約であるため、「理にかなっている」ことを実行してください。

于 2011-06-30T13:54:37.060 に答える
1

列挙型の問題は、実際には拡張できないことです。後で新しいコンポーネントを追加すると、監査フレームワークは新しいイベントを処理できなくなります。

EFを使用する最新のシステムでは、エンティティの名前空間に監査イベントの基本的なPOCOを作成しました。

public class AuditEvent : EntityBase
{
    public string Event { get; set; }
    public virtual AppUser AppUser { get; set; }
    public virtual AppUser AdminUser { get; set; }
    public string Message{get;set;}
    private DateTime _timestamp;

    public DateTime Timestamp
    {
        get { return _timestamp == DateTime.MinValue ? DateTime.UtcNow : _timestamp; }
        set { _timestamp = value; }
    }

    public virtual Company Company { get; set; }
// etc.
    }

タスクレイヤーでは、抽象ベースのAuditEventTaskを実装しました。

internal abstract class AuditEventTask<TEntity>
{
    internal readonly AuditEvent AuditEvent;

    internal AuditEventTask()
    {
        AuditEvent = InitializeAuditEvent();
    }

    internal void Add(UnitOfWork unitOfWork)
    {
        if (unitOfWork == null)
        {
            throw new ArgumentNullException(Resources.UnitOfWorkRequired_Message);
        }
        new AuditEventRepository(unitOfWork).Add(AuditEvent);
    }

    private AuditEvent InitializeAuditEvent()
    {
        return new AuditEvent {Event = SetEvent(), Timestamp = DateTime.UtcNow};
    }

    internal abstract void Log(UnitOfWork unitOfWork, TEntity entity, string appUserName, string adminUserName);

    protected abstract string SetEvent();
}

イベントに関連付けられたデータを記録するにはログを実装する必要があり、派生タスクにイベントのタイプを暗黙的に設定させるようにSetEventを実装します。

internal class EmailAuditEventTask : AuditEventTask<Email>
{
    internal override void Log(UnitOfWork unitOfWork, Email email, string appUserName, string adminUserName)
    {
        AppUser appUser = new AppUserRepository(unitOfWork).Find(au => au.Email.Equals(appUserName, StringComparison.OrdinalIgnoreCase));
        AuditEvent.AppUser = appUser;
        AuditEvent.Company = appUser.Company;
        AuditEvent.Message = email.EmailType;
        Add(unitOfWork);
    }

    protected override string SetEvent()
    {
        return AuditEvent.SendEmail;
    }
}

ここでの問題は、内部の基本タスクです。基本タスクは、後でTask名前空間に追加して使用できるように公開できますが、全体としては、これでアイデアが得られると思います。

実装に関しては、他のタスクによってロギングをいつ実行するかが決定されるため、次のようになります。

AuditEventTask task;
if (user.failLogic1) { task = new FailLogin1AuditEventTask(fail 1 params); }
if (user.failLogic2) { task = new FailLogin2AuditEventTask(fail 2 params); }
if (user.failLogic3) { task = new FailLogin3AuditEventTask(etc); }
if (user.failLogic4) { task = new FailLogin4AuditEventTask(etc); }

task.Log();
user.Save();
于 2011-06-30T13:47:39.770 に答える