I am trying to use ContextBoundObject and message sinks to inject some aspects into my code.
My problem is that my aspect is being called only once –
when I make the call: myFacadee.GetValue("Tie")
I would expect to see my caching aspect be called twice
- Once for the ‘
GetValue‘ method - Secondly for the ‘
GetValues‘ which is called internally in the
‘GetValue‘ method
However, it’s being called only once for the first ‘GetValue‘ method call.
How can change/fix the following code to make sure that all the methods on my ‘MyFacade‘ object cause the caching aspect to be invoked. Even if they are being called by other methods in the same ‘MyFacde‘ object?
Here is a simplified example of what my code looks like:
Test Application:
class Program
{
static void Main(string[] args)
{
var myFacadee = new MyFacade();
System.Console.WriteLine("Value:\t" + myFacadee.GetValue("Tie"));
System.Console.ReadLine();
}
}
Facade:
[Cache]
public class MyFacade : ContextBoundObject
{
public string GetValue(string name)
{
return GetValues().FirstOrDefault(x => x.EndsWith(name));
}
public List<string> GetValues()
{
return new List<string>
{
"You asked for a Shirt",
"You asked for a Pants",
"You asked for a Tie"
};
}
}
CacheAttribute:
[AttributeUsage(AttributeTargets.Class)]
public class CacheAttribute : ContextAttribute
{
public CacheAttribute() : base("Security") { }
public override void GetPropertiesForNewContext(IConstructionCallMessage ctorMsg)
{
ctorMsg.ContextProperties.Add(new CacheProperty());
}
}
CacheProperty:
public class CacheProperty : IContextProperty, IContributeObjectSink
{
public IMessageSink GetObjectSink(MarshalByRefObject o, IMessageSink next)
{
return new CacheAspect(next);
}
public void Freeze(Context newContext)
{
// no op
}
public bool IsNewContextOK(Context ctx)
{
var newContextLogProperty = ctx.GetProperty("CacheProperty") as CacheProperty;
if (newContextLogProperty == null)
{
Debug.Assert(false);
return false;
}
return (true);
}
public string Name { get { return "CacheProperty"; } }
}
CacheAspect:
internal class CacheAspect : IMessageSink
{
internal CacheAspect(IMessageSink next)
{
_next = next;
}
private readonly IMessageSink _next;
public IMessageSink NextSink
{
get { return _next; }
}
public IMessage SyncProcessMessage(IMessage msg)
{
Preprocess(msg);
var returnMethod = _next.SyncProcessMessage(msg);
return returnMethod;
}
public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
{
throw new InvalidOperationException();
}
private void Preprocess(IMessage msg)
{
// We only want to process method calls
if (!(msg is IMethodMessage)) return;
var call = msg as IMethodMessage;
var type = Type.GetType(call.TypeName);
var callStr = type.Name + "." + call.MethodName;
var argsString = call.Args.Aggregate((current, next) => current + ", " + next);
Console.WriteLine("Try to get value form cache : {0} for {1}({2})", callStr, call.MethodName, argsString);
}
}
It is only getting called once because the context boundry is only crossed once. A call to the same instance from within the instance does not require any marshalling into the context, hence your sink is not used to intercept a message.