我编写了简单的拦截器(使用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?
答案 0 :(得分: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;
}
}
}