【问题标题】:Logging entities change using SessionInterceptor in orchard使用果园中的 SessionInterceptor 记录实体更改
【发布时间】:2014-08-11 07:22:44
【问题描述】:

我想记录某些实体的更改(标有属性),所以我创建了AbstractSessionInterceptor 的后代来访问实体更改。另外我想知道谁做了这个改变,所以我需要访问当前用户,所以通过IWorkContextAccessor我正在创建IWorkContextScope,获取WorkContext并尝试获取用户,当现有实体正在被编辑时我能够访问当前用户,当使用 contentmanager 创建新实体时,我得到超时异常。然后我通过IWorkContextAccessor.GetContext() 得到WorkContext,我得到无限循环(拦截器被一次又一次地调用)。任何想法和建议将不胜感激。

谢谢。

来源:

public class AccountInterceptor : AbstractSessionInterceptor
{
    private IProtocolLogger _logger;
    private readonly Type _mainAttrType = typeof(ProtocolAttribute);
    private readonly Type _fieldAttrType = typeof(ProtocolFieldAttribute);
    private readonly IWorkContextAccessor _contextAccessor;
    ISessionFactoryHolder _sessionFactoryHolder;

    public AccountInterceptor(IWorkContextAccessor contextAccessor, ISessionFactoryHolder sessionFactoryHolder)
    {
        _contextAccessor = contextAccessor;
        _sessionFactoryHolder = sessionFactoryHolder;
    }

    public override bool OnFlushDirty(object entity, object id, object[] currentState, object[] previousState, string[] propertyNames, NHibernate.Type.IType[] types)
    {
        var t = entity.GetType();
        var attributes = t.GetCustomAttributes(_mainAttrType, true);

        if (attributes.Length != 0)
        {
            IWorkContextScope scope = _contextAccessor.CreateWorkContextScope();

            WorkContext context = scope.WorkContext;

            if (context != null)
            {
                var attr = (ProtocolAttribute)attributes.FirstOrDefault();
                var currentDic = currentState.Select((s, i) => new { S = s, Index = i }).ToDictionary(x => x.Index, x => x.S);

                var prvDic = previousState.Select((s, i) => new { S = s, Index = i }).ToDictionary(x => x.Index, x => x.S);

                var diff = compare(currentDic, prvDic);

                if (!attr.LogAllData)
                {
                    List<string> properties = new List<string>();
                    foreach (var propety in t.GetProperties())
                    {
                        var propertyAttributes = propety.GetCustomAttributes(_fieldAttrType, true);
                        if (propertyAttributes.Length != 0)
                            properties.Add(propety.Name);
                    }
                    if (properties.Count != 0)
                    {
                        var necesseryProps = propertyNames.Select((s, i) => new { S = s, Index = i }).Where(p => properties.Contains(p.S)).ToDictionary(x => x.Index, x => x.S);
                        TupleList<int, object, object> ToRemove = new TupleList<int, object, object>();
                        foreach (var tuple in diff)
                        {
                            if (!necesseryProps.Keys.Contains(tuple.Item1))
                            {
                                ToRemove.Add(tuple);
                            }
                        }
                        ToRemove.ForEach(d => diff.Remove(d));
                    }
                }

                if (diff.Count != 0)
                {
                    _logger = ProtocolLogger.GetInstance();
                    var sessionFactory = _sessionFactoryHolder.GetSessionFactory();
                    var session = sessionFactory.OpenSession();

                    var user = GetCurrentUser(session, context.HttpContext);

                    string propertiesFormat = GetPropertiesStringFormat(diff, propertyNames);
                    object[] param = new object[] { DateTime.Now, entity, propertyNames };

                    string entityId = string.Empty;
                    try
                    {
                        if (entity is IAuditable)
                        {
                            entityId = ((IAuditable)entity).Id.ToString();
                        }
                    }
                    catch (Exception)
                    {
                        entityId = entity.ToString();
                    }
                    foreach (var pair in diff)
                    {
                        ProtocolPropertyInfo info = new ProtocolPropertyInfo(propertyNames[pair.Item1], Convert.ToString(pair.Item2), Convert.ToString(pair.Item3));

                        _logger.Log(user, entity, entityId, session, context, Operation.Write, info);
                    }

                    session.Flush();
                    session.Close();
                }

            }
        }

        return base.OnFlushDirty(entity, id, currentState, previousState, propertyNames, types);
    }

    private object GetCurrentUser(ISession session, HttpContextBase httpContext)
    {
        if (httpContext == null || !httpContext.Request.IsAuthenticated || !(httpContext.User.Identity is FormsIdentity))
        {
            return null;
        }

        var formsIdentity = (FormsIdentity)httpContext.User.Identity;
        var userData = formsIdentity.Ticket.UserData ?? "";

        // the cookie user data is {userId};{tenant}
        var userDataSegments = userData.Split(';');

        if (userDataSegments.Length != 2)
        {
            return null;
        }

        var userDataId = userDataSegments[0];
        var userDataTenant = userDataSegments[1];

        int userId;
        if (!int.TryParse(userDataId, out userId))
        {
            return null;
        }

        Type regType = Assembly.Load("Orchard.Users").GetTypes().First(t => t.Name == "UserPartRecord");
        var user = session.Get(regType, userId);

        return user;
    }

    private string GetPropertiesStringFormat(TupleList<int, object, object> diffDic, string[] propertyNames)
    {
        StringBuilder result = new StringBuilder();

        foreach (var pair in diffDic)
        {
            result.AppendFormat("Property name {0}, New value {1}, Old value {2}", propertyNames[pair.Item1], pair.Item2, pair.Item3);
        }

        return result.ToString();
    }

    private TupleList<int, object, object> compare(Dictionary<int, object> dic1, Dictionary<int, object> dic2)
    {
        var diff = new TupleList<int, object, object>();

        foreach (KeyValuePair<int, object> pair in dic1)
        {
            if (!Equals(pair.Value, dic2[pair.Key]))
            {
                diff.Add(pair.Key, pair.Value, dic2[pair.Key]);
            }
        }

        return diff;
    }
}

【问题讨论】:

  • 您看过新的审计追踪功能了吗?
  • 我会试试的,谢谢@Bertrand Le Roy

标签: c# asp.net-mvc orchardcms orchardcms-1.8


【解决方案1】:

永远不要在你的拦截器中启动新的工作上下文 - 保证无限循环。事实上,你不必这样做。每个拦截器已经在每个工作上下文中实例化,因此您可以像往常一样通过 ctor 注入依赖项。

要访问当前用户,您可以:

  • 注入IOrchardServices并使用.WorkContext.CurrentUser属性,或者
  • 或使用contextAccessor.GetContext() 获取上下文,然后调用CurrentUser

另外,从拦截器内部执行数据库操作时要小心,因为这些操作很可能导致无限循环并引发堆栈溢出异常。

【讨论】:

  • 谢谢,但事实上,如果注入 IOrchardServices,并且使用 contextAccessor.GetContext(),当内容管理器创建新项目实际上会导致无限循环时,我会得到循环依赖...我决定听根据 Bertrand Le Roy 的建议,根据我的要求修改 AuditTrail 模块。
  • 你能提交一个关于这个的错误吗?它不应该发生。
  • 我创建了一个错误报告。
  • 我正在运行 1.8.1 版,似乎无法注入 IOrchardServices,但使用 contextAccessor.GetContext() 没有任何问题。 (没有无限循环)
猜你喜欢
  • 2011-06-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多