Как внедрить аудит на бизнес-уровне - PullRequest
2 голосов
/ 30 июня 2011

Я пытаюсь внедрить базовый аудит для системы, в которой пользователи могут войти в систему, изменить свои пароли, электронную почту и т. Д.

Все функции, которые я хочу проверять, находятся на бизнес-уровне, и я хотел бы создатьобъект Audit, в котором хранится дата и время, когда была вызвана функция, включая результат.

Недавно я присутствовал на конференции, и одна из сессий была посвящена хорошо продуманным веб-приложениям, и я пытаюсь реализовать некоторые идеи.По сути, я использую Enum для возврата результата функции и использую инструкцию switch для обновления пользовательского интерфейса в этом слое.Функции используют ранний возврат, который не оставляет времени для создания, настройки и сохранения аудита.

Мой вопрос заключается в том, какие подходы используют другие при аудите бизнес-функций, и какой подход вы бы использовали, если бы у вас была такая функция, как у меня (если вы скажете, что я ее слушаю, я выслушаю, но буду раздражительным).1007 *

Код выглядит примерно так:

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, но это кажется неправильным.

Неужели плохо вставлять все это в try catch с finally и использовать finally для создания объекта Audit и установки егоинформация там, таким образом, решая проблему раннего возвращения?У меня сложилось впечатление, что наконец-то для очистки, а не одитинга.

Меня зовут Дэвид, и я просто пытаюсь быть лучшим кодом.Спасибо.

Ответы [ 2 ]

2 голосов
/ 30 июня 2011

Не могу сказать, что использовал его, но это похоже на кандидата на Аспектно-ориентированное программирование . По сути, вы можете внедрить код в каждом вызове метода для таких вещей, как ведение журнала / аудит / и т.д., в автоматическом режиме.

Отдельно создание блока try / catch / finally не является идеальным, но я бы запустил оценку затрат / выгод, чтобы посмотреть, стоит ли это того. Если вы можете разумно дешевый рефакторинг кода, чтобы вам не пришлось его использовать, сделайте это. Если цена непомерна, я бы попробовал / наконец. Я думаю, что многие люди попадают в ловушку «лучшего решения», но время / деньги - это всегда ограничения, поэтому делайте то, что «имеет смысл».

1 голос
/ 30 июня 2011

Проблема с перечислением в том, что он не очень расширяемый.Если вы добавите новые компоненты позже, ваша структура аудита не сможет обрабатывать новые события.

В нашей последней системе, использующей 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;
    }
}

Икота здесь - внутренняя базовая задача - базовая задача МОЖЕТ быть публичной, чтобы ее могли использовать более поздние дополнения к пространству имен Задача - но в целом я думаю, что этоidea.

Когда дело доходит до реализации, другие наши задачи определяют, когда должно происходить ведение журнала, поэтому в вашем случае:

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();
...