6

EFでSaveChangesをオーバーライドして、監査ロガーを追加するのは簡単なようです。以下の監査プロパティ(created、createdby、updated、updatedby)を設定するには、ApplyAuditLoggingメソッドを参照してください。

   public override int SaveChanges()
    {
        var autoDetectChanges = Configuration.AutoDetectChangesEnabled;

        try
        {
            Configuration.AutoDetectChangesEnabled = false;
            ChangeTracker.DetectChanges();
            var errors = GetValidationErrors().ToList();
            if(errors.Any())
            {
                throw new DbEntityValidationException("Validation errors were found during save: " + errors);
            }

            foreach (var entry in ChangeTracker.Entries().Where(e => e.State == EntityState.Added || e.State == EntityState.Modified))
            {
                ApplyAuditLogging(entry);
            }

            ChangeTracker.DetectChanges();

            Configuration.ValidateOnSaveEnabled = false;

            return base.SaveChanges();
        }
        finally
        {
            Configuration.AutoDetectChangesEnabled = autoDetectChanges;
        }
    }

    private static void ApplyAuditLogging(DbEntityEntry entityEntry)
    {

        var logger = entityEntry.Entity as IAuditLogger;
        if (logger == null) return;

        var currentValue = entityEntry.Cast<IAuditLogger>().Property(p => p.Audit).CurrentValue;
        if (currentValue == null) currentValue = new Audit();
        currentValue.Updated = DateTime.Now;
        currentValue.UpdatedBy = "???????????????????????";
        if(entityEntry.State == EntityState.Added)
        {
            currentValue.Created = DateTime.Now;
            currentValue.CreatedBy = "????????????????????????";
        }
    }

問題は、Windowsユーザーのlogon / usernameを取得して、オブジェクトのUpdatedByプロパティとCreatedByプロパティを設定する方法です。したがって、これは使用できませんでした。

また、別のケースでは、新しいCallHistoryレコードを連絡先に自動的に追加したいと思いました。連絡先が変更されるたびに、新しいレコードを子テーブルCallHistoryに追加する必要があります。リポジトリのInsertOrUpdateで実行しましたが、汚れているように感じます。データベースから現在のユーザーを設定する必要があるため、より高いレベルで実行できれば便利です。ここでも問題は、CallHistoryレコードを作成するためにデータベースからユーザーをフェッチする必要があることです(SalesRep = User)。

私のリポジトリのコードは、2つのことを実行します。1つは、オブジェクトが作成または更新されたときにオブジェクトに監査エントリを作成し、2つは、連絡先が更新されるたびにCallHistoryエントリも作成したことです。

ContactRepository.SetCurrentUser(User).InsertOrUpdate(contact)

ユーザーをリポジトリコンテキストに含めるには、次のようにします。

    var prop = typeof(T).GetProperty("Id", BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);

    if (prop.GetValue(entity, null).ToString() == "0")
    {
        // New entity
        _context.Set<T>().Add(entity);
        var auditLogger = entity as IAuditLogger;
        if (auditLogger != null)
            auditLogger.Audit = new Audit(true, _principal.Identity.Name);
    }
    else
    {
        // Existing entity
        _context.Entry(entity).State = EntityState.Modified;
        var auditLogger = entity as IAuditLogger;
        if (auditLogger != null && auditLogger.Audit != null)
        {
            (entity as IAuditLogger).Audit.Updated = DateTime.Now;
            (entity as IAuditLogger).Audit.UpdatedBy = _principal.Identity.Name;
        }

        var contact = entity as Contact;
        if (_currentUser != null)
            contact.CallHistories.Add(new CallHistory
                {
                    CallTime = DateTime.Now,
                    Contact = contact,
                    Created = DateTime.Now,
                    CreatedBy = _currentUser.Logon,
                    SalesRep = _currentUser
                });
    }
}

どういうわけかWindowsユーザーをDbContextのSaveChangesオーバーライドに挿入する方法はありますか?また、WindowsログオンIDに基づいてデータベースからユーザーをフェッチする方法もあります。これにより、CallHistoryでSalesRepを設定できます(上記のコードを参照)。

これがMVCアプリのコントローラーに対する私のアクションです:

[HttpPost]
public ActionResult Create([Bind(Prefix = "Contact")]Contact contact, FormCollection collection)
{
    SetupVOs(collection, contact, true);
    SetupBuyingProcesses(collection, contact, true);

    var result = ContactRepository.Validate(contact);

    Validate(result);

    if (ModelState.IsValid)
    {
        ContactRepository.SetCurrentUser(User).InsertOrUpdate(contact);
        ContactRepository.Save();
        return RedirectToAction("Edit", "Contact", new {id = contact.Id});
    }

    var viewData = LoadContactControllerCreateViewModel(contact);

    SetupPrefixDropdown(viewData, contact);

    return View(viewData);
}
4

3 に答える 3

6

簡単で怠惰な方法は、監査コード内から HttpContext.Current.User.Identity.Name にアクセスすることです。ただし、これにより System.Web.* への依存関係が作成されます。これは、適切に階層化されたアプリケーションを使用している場合はおそらく望ましくありません (また、実際に個別の層を使用している場合は機能しません)。

1 つのオプションは、SaveChanges をオーバーライドする代わりに、ユーザー名を取るオーバーロードを作成することです。次に、作業を行い、後で実際の SaveChanges を呼び出します。欠点は、誰かが誤って (または意図的に) SaveChanges() (実際のもの) を呼び出して、監査をバイパスする可能性があることです。

より良い方法は、単に _currentUser プロパティを DbContext に追加し、コンストラクターを使用してそれを渡すことです。次に、コンテキストを作成するときに、その時点でユーザーを渡すだけです。残念ながら、コンストラクターからデータベース内のユーザーを実際に検索することはできません。

ただし、単に ContactID を保存して、連絡先全体の代わりにそれを追加することができます。あなたの連絡先は既に存在している必要があります。

于 2012-09-24T22:51:11.290 に答える
0

懸念事項の分離の境界を越えている可能性があると思います。リポジトリ パターンは、ビジネス ロジック、データベース マッピング、およびデータベース crud 操作を分離するために使用されます。アプリケーションはログインしているユーザーに関心を持つ必要があり、リポジトリはデータの保存のみに関心を持つ必要があります。リポジトリで HttpContext を参照しないことをお勧めします。参照すると、リポジトリは Web アプリケーションでしか使用できないためです。この種のメタデータの人口を抽象化しようとしている場合は、アプリケーションでそれを行います...たとえば、ベースコントローラーなどで。

于 2013-07-10T11:57:18.923 に答える