从ConnectionFilter访问ServiceStack会话

时间:2015-01-29 16:45:07

标签: servicestack ormlite-servicestack

我正在使用SQL Server和数据库触发器来对系统的所有更改进行数据级审计。此审核包括发起更改的任何人的用户ID /名称。理想情况下,我想在我的AppHost.Configure方法中执行类似的操作:

SqlServerDialect.Provider.UseUnicode = true;
var dbFactory = new OrmLiteConnectionFactory(ConnectionString, SqlServerDialect.Provider)
        {
            ConnectionFilter = (db =>
            {
                IAuthSession session = this.Request.GetSession();
                if (session != null && !session.UserName.IsNullOrEmpty())
                {
                    System.Data.IDbCommand cmd = db.CreateCommand();
                    cmd.CommandText = "declare @ci varbinary(128); select @ci = CAST(@Username as varbinary(128)); set context_info @ci";
                    System.Data.IDbDataParameter param = cmd.CreateParameter();
                    param.ParameterName = "Username";
                    param.DbType = System.Data.DbType.String;
                    //param.Value = session.UserName;
                    param.Value = session.UserAuthId;
                    cmd.Parameters.Add(param);
                    cmd.ExecuteNonQuery();
                }

                return new ProfiledDbConnection(db, Profiler.Current);
            }),
            AutoDisposeConnection = true
        };
        container.Register<IDbConnectionFactory>(dbFactory);

当然,这不起作用,因为this.Request不存在。有没有办法在OrmLite连接上从ConnectionFilter或ExecFilter访问当前会话?

我开始的另一种方法是覆盖Db的{​​{1}}属性,不再起作用了,因为我已经将一些活动抽象到他们自己的接口实现中以允许模拟在测试期间。其中每个都传递一个预期返回DB连接的函数。例如:

Service

那么,我如何确保执行的任何DML都具有我的数据库审计触发器所需的(特别是数据库特定的)上下文信息?

3 个答案:

答案 0 :(得分:2)

早期multi tenant ServiceStack example显示了如何使用请求上下文存储每个请求项,例如您可以从全局请求过滤器填充请求上下文:

GlobalRequestFilters.Add((req, res, dto) =>
{
    var session = req.GetSession();
    if (session != null)
        RequestContext.Instance.Items.Add(
            "UserName", session.UserName);
});

并在连接过滤器中访问它:

ConnectionFilter = (db =>
{
    var userName = RequestContext.Instance.Items["UserName"] as string;
    if (!userName.IsNullOrEmpty()) {
       //...
    }
}),

答案 1 :(得分:1)

另一种方法是使用工厂模式,类似于ServiceStack首先创建OrmLite数据库连接的方式。由于所有与用户相关的调用都是通过ServiceRunner进行的,因此我捎带了由ServiceStack管理的会话。

public class TransactionProcessorFactory : ITransactionProcessorFactory
{
    public ITransactionProcessor CreateTransactionProcessor(IDbConnection Db)
    {
        return new TransactionProcessor(Db);
    }
}

public abstract MyBaseService : Service
{
    private IDbConnection db;

    public override System.Data.IDbConnection Db
    {
        get
        {
            if (this.db != null) return db;

            this.db = this.TryResolve<IDbConnectionFactory>().OpenDbConnection();

            IAuthSession session = this.Request.GetSession();

            if (session != null && !session.UserName.IsNullOrEmpty())
            {
                IDbCommand cmd = db.CreateCommand();
                cmd.CommandText = "declare @ci varbinary(128); select @ci = CAST(@Username as varbinary(128)); set context_info @ci";
                IDbDataParameter param = cmd.CreateParameter();
                param.ParameterName = "Username";
                param.DbType = DbType.String;
                //param.Value = session.UserName;
                param.Value = session.UserAuthId;
                cmd.Parameters.Add(param);
                cmd.ExecuteNonQuery();
            }
            return db;
        }
    }

    private ITransactionProcessor tp = null;
    public virtual ITransactionProcessor TransactionProcessor
    {
        get
        {
            if (this.tp != null) return tp;
            var factory = this.TryResolve<ITransactionProcessorFactory>();
            this.tp = factory.CreateTransactionProcessor(this.Db);

            return tp;
        }
    }
}

答案 2 :(得分:1)

为了潜在的未来ServiceStack用户,另一种方法是使用OrmLite的Global Insert/Update filters结合Mythz's approach above仅在进行DML操作时注入必要的SQL。它不是100%,因为可能存在存储过程或手动SQL,但可能通过IDbConnection扩展方法处理,以手动设置所需的审计信息。