Два IPreInsertEventListeners, HttpContext.Current равен нулю, когда выполняется второе - PullRequest
0 голосов
/ 23 января 2012

Все эти выходные царапал мне голову.

Справочная информация: Проект представляет собой MVC3, C # 4, FluentNHibernate (1.2.0.712), nHibernate (3.1.0.4) и StructureMap (2.6.3.0), все они установлены через NuGet. Это большая кодовая база, которую я не создавал, я просто пытаюсь добавить некоторые функциональные возможности для клиента ...

Существует существующий IPreInsertEventListener, который устанавливает информацию об аудите (CreatedDate, CreatedBy и т. Д.), Где CreatedBy - это пользовательская сущность приложения, которая извлекается из HttpSessionState через некоторый IOC voodoo. Это уже существующее и работающее ...

public class AuditEventListener : IPreInsertEventListener
{
    public bool OnPreInsert(PreInsertEvent eventItem)
    {
         if (eventItem.Entity is IAudit)
         {
            IAudit auditObject = (IAudit)eventItem.Entity;
            Store(eventItem.Persister, eventItem.State, "CreatedDate", DateTime.Now);
            Store(eventItem.Persister, eventItem.State, "CreatedBy", GetCurrentUser());
            Store(eventItem.Persister, eventItem.State, "LastModifiedDate", DateTime.Now);
            Store(eventItem.Persister, eventItem.State, "LastModifiedBy", GetCurrentUser());

            auditObject.CreatedBy = GetCurrentUser();
            auditObject.CreatedDate = DateTime.Now;
            auditObject.LastModifiedBy = GetCurrentUser();
            auditObject.LastModifiedDate = DateTime.Now;
          }

          return false;
    }

    private Model.WebUser GetCurrentUser()
    {
        return ObjectFactory.GetInstance<ICurrentUser>().Get();
    }
}    

Функция GetCurrentUser () - это оболочка для вызова StructureMap ObjectFactory службы, которая ищет в HttpSessionState пользователя, вошедшего в систему. По сути, это делает:

if(HttpContext.Current != null)
    return WebUser.GetBy(wu => wu.EmailAddress == HttpContext.Current.Session["Email"].ToString();
else
    return null;

Функция Store - это шаблонный код, который устанавливает значения в массиве eventItem.State по их именам из Persister. Опущено, потому что я не думаю, что это является частью проблемы.

Я добавил второй IPreInsertEventListener, предназначенный для создания журнала «История» с полной таблицей, основанного на том, что делает nhibernate.evers (который я не могу использовать здесь из-за проблем с версией). Он в основном проверяет наличие атрибута, отмечающего сохраняемый объект, и, если он его находит, копирует текущие и старые значения в связанный «HistoryModel» и сохраняет его.

    public class HistoryEventListener : IPreInsertEventListener
    {
        public bool OnPreInsert(PreInsertEvent eventItem)
        {
            var model = TryGetHistoryModel(eventItem.Entity);
            if (model != null)
            {
                MapToHistory(eventItem.Entity, model);

                session.Save(model);

            }
            return false;
        }

        private static IHistoryModel TryGetHistoryModel(object entity)
        {
            var att = entity.GetAttribute<HistoryLogAttribute>();
            if (att != null)
                return ObjectFactory.GetInstance(att.HistoryModelType) as IHistoryModel;
            else
                return null;
        }
    }

Функция MapToHistory в основном выполняет наивное отражение на основе отражения в модели, которая передается ей, используя имена, которые она получает из eventItem.Persister, для поиска имен свойств. Например, свойство «ProcessingStatusId» из eventItem.State будет сопоставлено с model.NewProcessingStatusId. Я опускаю это для краткости. Просто предположим, что мелкая копия создается и возвращается.

Слушатели подключены через StructureMap.Configuration.DSL.Registry как часть Fluently.Configure.BuildSessionFactory:

        return Fluently.Configure()
            .Database(
                MsSqlConfiguration.MsSql2008.ShowSql().ConnectionString(
                    connection => connection.FromConnectionStringWithKey("DefaultConnectionString")))
            .Mappings(mapping => mapping.FluentMappings.AddFromAssemblyOf<SiteUser>())
            .ExposeConfiguration(cfg =>
            {
                new SchemaUpdate(cfg).Execute(false, false);
                cfg.EventListeners.PreInsertEventListeners = new NHibernate.Event.IPreInsertEventListener[] { new AuditEventListener(), new HistoryEventListener() };

            })
            .BuildSessionFactory();

Теперь, когда вся преамбула удалена, проблема:

Если бы я должен был создать и сохранить объект, который не помечен HistoryAttribute, HistoryEventListener пропустит его обработку как следует, и все будет работать правильно. Однако если сохраняется объект, помеченный атрибутом, происходит этот процесс:

  1. AuditEventListener запускает для помеченного объекта, устанавливает CreatedBy, CreatedDate и т. Д. Правильно, потому что HttpContext.Current не является нулевым.

  2. HistoryEventListener срабатывает, создает историю объекта из отмеченного сущность и вызывает Session.Save.

  3. AuditEventListener запускает для объекта истории, устанавливает CreatedDate, но для CreatedBy устанавливается значение NULL, , поскольку HttpContext.Current равен NULL.

Я должен четко указать, что я ХОЧУ, чтобы шаг # 3 происходил здесь, поэтому таблица истории собирает информацию CreatedDate и CreatedBy. Я мог бы добавить это явно в HistoryEventListener, но тогда я повторюсь.

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

Вызывает ли действие сохранения формы нового объекта сущности внутри слушателя события какое-то сумасшедшее переключение контекста, которое убивает текущий HttpContext? Это ошибка?

Спасибо.

...