1

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

谢谢。

资源:

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

1 回答 1

0

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

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

  • 注入IOrchardServices和使用.WorkContext.CurrentUser财产,或
  • 或用于contextAccessor.GetContext()获取上下文,然后调用CurrentUser它。

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

于 2014-08-18T19:06:00.877 回答