3

データベース接続のライフサイクルを処理するために、(Castle.DynamicProxyを使用して)単純なインターセプターを作成しました。つまり、すべてのサービスには、最初の使用時に新しいサービスを開くConnectionプロパティがあります。各メソッド呼び出しの後、接続は自動的に閉じられます。

class CloseConnectionInterceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        try
        {
            invocation.Proceed();
        }
        finally
        {
            var hasConnection = invocation.InvocationTarget as IHasConnection;
            if (hasConnection.IsConnectionOpen)
            {
                hasConnection.Connection.Dispose();
                hasConnection.Connection = null;
            }
        }
    }
}

interface IHasConnection
{
    bool IsConnectionOpen { get; }
    Connection Connection { get; set; }
}

イテレータを返すメソッドを除いて、うまく機能します。

[Test]
public void ConnectionClosedByProxy()
{
    // arrange
    var service = new MyService();
    var proxy = new ProxyGenerator()
        .CreateInterfaceProxyWithTarget<IMyService>(
            service, new CloseConnectionInterceptor());

    // act
    proxy.GetList();

    // assert: works!!!
    Assert.IsFalse(service.IsConnectionOpen);
}

[Test]
public void IteratorLeavesOpenConnection()
{
    // arrange
    var service = new MyService();
    var proxy = new ProxyGenerator()
        .CreateInterfaceProxyWithTarget<IMyService>(
            service, new CloseConnectionInterceptor());

    // act
    proxy.GetEnumerable().ToList();

    // assert: bad, bad, bad!
    Assert.IsTrue(service.IsConnectionOpen);
}

ここで完全な例を参照してください:https ://gist.github.com/4087483

GetEnumerableメソッド内に"using(new Connection())"ステートメントがある場合、それは期待どおりに機能します-イテレーターへの最後のアクセス後に接続が閉じられます。この瞬間を迎撃機で捕まえることは可能ですか?または、メソッドだけでなく、結果のIEnumerableもプロキシする必要がありますか?

4

1 に答える 1

0

私は私の質問に答えの種を入れました:)。IEnumerableもプロキシする変更されたインターセプターは次のとおりです。

class CloseConnectionInterceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        Type genericType = invocation.Method.ReturnType.IsGenericType
            ? invocation.Method.ReturnType.GetGenericTypeDefinition()
            : null;

        if (genericType == typeof(IEnumerable<>))
        {
            invocation.Proceed();

            var method = GetType()
                .GetMethod("HandleIteratorInvocation")
                .MakeGenericMethod(
                    invocation.Method.ReturnType.GetGenericArguments()[0]);

            invocation.ReturnValue = method.Invoke(
                null,
                new[] { invocation.InvocationTarget, invocation.ReturnValue });
        }
        else
        {
            HandleNonIteratorInvocation(invocation);
        }
    }

    public static IEnumerable<T> HandleIteratorInvocation<T>(
        IHasConnection hasConnection, IEnumerable enumerable)
    {
        try
        {
            foreach (var element in enumerable)
                yield return (T)element;
        }
        finally
        {
            CloseOpenConnection(hasConnection);
        }
    }

    private static void HandleNonIteratorInvocation(IInvocation invocation)
    {
        try
        {
            invocation.Proceed();
        }
        finally
        {
            CloseOpenConnection(invocation.InvocationTarget as IHasConnection);
        }
    }

    private static void CloseOpenConnection(IHasConnection hasConnection)
    {
        if (hasConnection.IsConnectionOpen)
        {
            hasConnection.Connection.Dispose();
            hasConnection.Connection = null;
        }
    }
}
于 2012-11-16T15:20:37.603 に答える