c# - Create an exception-converting dynamic proxy that works with tasks -
problem
i'm somehow running in circles... try create interface proxy target using castle dynamic proxy. proxy should
- return return value of invocation if no exception thrown (i.e. nothing).
- throw new
interceptedexceptionif invocation throwsinvalidoperationexception. - throw
eif invocation throws exceptione.
in other words, interceptor should catch , convert specific exception type, , not intercept in other cases.
i got working synchronous methods. however, need same behavior async methods return task.
what tried
i tried adding continuation returned task , inspect isfaulted , exception (similar this answer. works methods return task, not methods return task<t> since continuation of type task (and don't know t in interceptor).
tests covers 3 cases described above async methods (xunit.net)
public class convertnotfoundinterceptortest { [fact] public void non_throwing_func_returns_a_result() { assert.equal(43, runtest(i => + 1)); } [fact] public void invalidoperationexceptions_are_converted_to_indexoutofrangeexceptions() { var exception = assert.throws<aggregateexception>(() => runtest(i => { throw new invalidoperationexception("ugh"); })); assert.true(exception.innerexception indexoutofrangeexception); } [fact] public void other_exceptions_are_preserved() { var exception = assert.throws<aggregateexception>(() => runtest(i => { throw new argumentexception("ugh"); })); assert.true(exception.innerexception argumentexception); } private static int runtest(func<int, int> func) { var generator = new proxygenerator(); var proxiedsubject = generator.createinterfaceproxywithtarget<isubject>(new subject(func), new convertnotfoundinterceptor()); return proxiedsubject.doasync(42).result; } public interface isubject { task<int> doasync(int input); } public class subject : isubject { private readonly func<int, int> _func; public subject(func<int, int> func) { _func = func; } public async task<int> doasync(int input) { return await task.run(() => _func(input)); } } } interceptor
public class convertnotfoundinterceptor : iinterceptor { public void intercept(iinvocation invocation) { invocation.proceed(); var task = invocation.returnvalue task; if (task != null) { var continuation = task.continuewith( t => { if (t.exception != null && t.exception.innerexception invalidoperationexception) { throw new indexoutofrangeexception(); } }, taskcontinuationoptions.onlyonfaulted); // following line fails (invalidcastexception: unable cast object // of type 'system.threading.tasks.continuationtaskfromtask' // type 'system.threading.tasks.task`1[system.int32]'.) invocation.returnvalue = continuation; } } } note implementation shown here not consider synchronous cases. left part out intentionally.
question
what correct way add above interception logic asynchronous methods?
ok, doesn't work task<dynamic> because castle dynamic proxy requires returnvalue exact matching type. however, can accomplish elegantly using dynamic dispatch:
public class convertnotfoundinterceptor : iinterceptor { public void intercept(iinvocation invocation) { invocation.proceed(); var task = invocation.returnvalue task; if (task != null) invocation.returnvalue = convertnotfoundasync((dynamic)task); } private static async task convertnotfoundasync(task source) { try { await source.configureawait(false); } catch (invalidoperationexception) { throw new indexoutofrangeexception(); } } private static async task<t> convertnotfoundasync<t>(task<t> source) { try { return await source.configureawait(false); } catch (invalidoperationexception) { throw new indexoutofrangeexception(); } } } i prefer async/await syntax because handle edge cases tricky using continuewith.
Comments
Post a Comment