2011-06-30 51 views
2

我想實現基本的審覈系統,用戶可以登錄,更改密碼和電子郵件等如何實現業務層

我要審覈的功能都在業務層審計我想創建一個Audit對象來存儲函數被調用的日期時間,包括結果。

我最近參加了一個會議,其中一個會議是精心設計的Web應用程序,我試圖實現一些想法。基本上我使用Enum來返回函數的結果,並使用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來創建Audit對象並在其中設置信息從而解決早期返回問題?我的印象是,最後是清理而不是審計。

我的名字是大衛,我只是想成爲一個更好的代碼。謝謝。

回答

2

我不能說我已經用它,但是這似乎是Aspect Oriented Programming的候選人。基本上,您可以在每個方法調用中以自動方式注入代碼,例如logging/auditing /等。

另外,製作一個try/catch/finally塊並不理想,但我會運行一個成本/收益來看看它是否值得。如果您可以便宜地合理地重構代碼以便您不必使用它,請執行此操作。如果成本過高,我會盡最後的努力。我認爲很多人都陷入了「最佳解決方案」,但時間/金錢總是受到限制,所以做一些「有意義的事情」。

+0

嗨,閱讀關於面向方面的編程,它會與會話的其他方面一起敲響鈴聲。在整個業務層幾乎靜靜地注入沒有業務邏輯,所以我會看看這個,謝謝。 –

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

這裏的打嗝是內部基本任務 - 基本任務可以公開,以便稍後添加到任務命名空間可以使用它 - 但總體而言,我認爲這給你的想法。

當涉及到實施,我們的其他任務確定發生日誌記錄,所以你的情況:

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(); 
+0

我還沒有完全遵循UnitOfWork部分,所以我需要離開並閱讀一些內容,但我想我會看到它是如何在最終代碼塊中構建的,也許這對我來說是如何工作的,歡呼聲 –

+0

我們的UnitOfWork基本上就是我們的實體框架事務 - 您無需擔心此實現。 –