Skip to content

IInterceptor not refreshing inject #46

@RtypeStudios

Description

@RtypeStudios

Hi There,

We recently updated to the latest Ninject and Ninject extensions library. We have noticed we are now getting disposed exceptions when using the following generic transaction interceptor.

It appears even though we are running the below in request scope it is still holding onto a previous instance of the EF context and using it to try and start a new transaction. Where previously we were getting a new context instance for each request scope.

/// <summary>
/// Used to mark a method that requires being wrapped in a transaction.
/// </summary>
public class UseTransactionAttribute : InterceptAttribute
{
    private readonly IsolationLevel _isolationLevel;

    /// <summary>
    /// Initializes a new instance of the <see cref="UseTransactionAttribute"/> class.
    /// </summary>
    public UseTransactionAttribute(IsolationLevel isolationLevel = IsolationLevel.ReadCommitted)
    {
        _isolationLevel = isolationLevel;
    }

    /// <summary>
    /// Create the interceptor attached to this attribute.
    /// </summary>
    /// <param name="request">The proxy request being made.</param>
    /// <returns>A <see cref="IInterceptor"/> to wrap the method in.</returns>
    public override IInterceptor CreateInterceptor(IProxyRequest request)
    {
        var isolationLevel = new ConstructorArgument("isolationLevel", _isolationLevel);
        return request.Kernel.Get<UseTransactionInterceptor>(isolationLevel);
    }
}

/// <summary>
/// Transaction method is responsible for wrapping a method in a transaction in a consistent way.
/// </summary>
public class UseTransactionInterceptor : IInterceptor
{
    private readonly IObjectContextAdapter _context;
    private readonly IsolationLevel _isolationLevel;

    /// <summary>
    /// Initializes a new instance of the <see cref="UseTransactionInterceptor"/> class.
    /// </summary>
    public UseTransactionInterceptor(IObjectContextAdapter context, IsolationLevel isolationLevel = IsolationLevel.ReadCommitted)
    {
        _context = context;
        _isolationLevel = isolationLevel;
    }

    /// <summary>
    /// Intercept the method call so it can be wrapped in a transaction. Check if a transaction is active, if it isn't start a new one otherwise do nothing as we are already in one.
    /// </summary>
    /// <param name="invocation">The invocation we are wrapping a transaction around.</param>
    public void Intercept(IInvocation invocation)
    {
        var database = ((DbContext)_context).Database;

        // Don't start another transaction if we are already in one.
        if (database.CurrentTransaction != null)
        {
            invocation.Proceed();
        }
        else
        {
            using (var transaction = database.BeginTransaction(_isolationLevel))
            {
                try
                {
                    invocation.Proceed();
                    transaction.Commit();
                }
                catch
                {
                    transaction.Rollback();
                    throw;
                }
            }
        }
    }
}

If we replace the line

var database = ((DbContext)_context).Database;

with:

var t = invocation.Request.Kernel.Get<IObjectContextAdapter>();
var database = ((DbContext)t).Database;

We get the behavior we were getting before. So it appears the interceptor is being cached perhaps? Or is this an expected behavior change?

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions