Postsharp Newbie - Why is args.Instance null? - aop

New to PostSharp --- I'm trying out the NuGet version now and I'm trying to understand wny in the AuthoriseAttribute OnEntry method that the agrs.Instance value is null. I'm trying to implement authorsation that depends on the values of the object e.g. A customer who's been archived can't have a credit limit raised. I'm implementing the rules within other classes specific to the rules.
public class Program
{
static void Main(string[] args)
{
var c = new Customer();
c.RaiseCreditLimit(100000);
c.Error(00);
}
}
public class Customer
{
[AuthorizeActivity]
public void RaiseCreditLimit(int newValue)
{
}
[AuthorizeActivity]
public void Error(int newValue)
{
}
}
[Serializable]
public class AuthorizeActivityAttribute : OnMethodBoundaryAspect
{
public override void OnEntry(MethodExecutionArgs args)
{
//
//Why is args.Instance null???????????
//
if (args.Method.Name == "RaiseCreditLimit")
{
Debug.WriteLine(args.Method.Name + " started");
}
else
{
throw new Exception("Crap");
}
}
public override void OnExit(MethodExecutionArgs args)
{
Debug.WriteLine(args.Method.Name + " finished");
}
}

The answer is because you're not using it in your aspect. It's an optimization. If you use it in the aspect then it will be set. Change your aspect to consume instance and it will be there.
public override void OnEntry(MethodExecutionArgs args)
{
//
//Why is args.Instance null???????????
//
if (args.Method.Name == "RaiseCreditLimit")
{
Debug.WriteLine(args.Instance.GetType().Name);
Debug.WriteLine(args.Method.Name + " started");
}
else
{
throw new Exception("Crap");
}
}
For more info check out this article to see what else PostSharp does to optimize code http://programmersunlimited.wordpress.com/2011/03/23/postsharp-weaving-community-vs-professional-reasons-to-get-a-professional-license/

Related

Revit Synchronization event

Starting with this...
https://github.com/jeremytammik/RevitLookup/blob/master/CS/EventTrack/Events/ApplicationEvents.cs
I'm trying to add an event listener for a synchronization event. the code below throws an error stating that the m_app is null. Can i not subscribe to this event while Revit is starting up?
I was able to do this before with application.ViewActivated += ..... Im wondering if this has something to do with DB vs UI driven events and when they are allowed to be subscribed to? I just don't know.
namespace RevitModelHealth
{
public class checkHealth : IExternalApplication
{
// Document doc;
static public Autodesk.Revit.ApplicationServices.Application m_app = null;
public Result OnShutdown(UIControlledApplication application)
{
return Result.Succeeded;
}
public Result OnStartup(UIControlledApplication application)
{
m_app.DocumentSynchronizingWithCentral += new EventHandler<DocumentSynchronizingWithCentralEventArgs>(m_app_DocumentSavingToCentral);
return Result.Succeeded;
}
void m_app_DocumentSavingToCentral(object sender, Autodesk.Revit.DB.Events.DocumentSynchronizingWithCentralEventArgs e)
{
MessageBox.Show("asd","asd");
}
}
}
Here is updated code reflecting my response to the first answer. The message box opens when the document is loaded. No errors are thrown when I try to initialize the synchronization event handlers, however, neither of the message boxes open before or after a synchronization event.
public class checkHealth : IExternalApplication
{
// Document doc;
static public Autodesk.Revit.ApplicationServices.Application m_app;
public Result OnShutdown(UIControlledApplication application)
{
return Result.Succeeded;
}
public Result OnStartup(UIControlledApplication application)
{
application.ControlledApplication.DocumentOpened += new EventHandler<DocumentOpenedEventArgs>(app_DocOpened);
return Result.Succeeded;
}
public void app_DocOpened(object sender, DocumentOpenedEventArgs args)
{
MessageBox.Show("asd","asd");
m_app.DocumentSynchronizingWithCentral += new EventHandler<DocumentSynchronizingWithCentralEventArgs>(m_app_DocumentSavingToCentral);
m_app.DocumentSynchronizedWithCentral += new EventHandler<Autodesk.Revit.DB.Events.DocumentSynchronizedWithCentralEventArgs>(m_app_DocumentSavedToCentral);
}
void m_app_DocumentSavingToCentral(object sender, Autodesk.Revit.DB.Events.DocumentSynchronizingWithCentralEventArgs e)
{
MessageBox.Show("sync", "sync");
}
void m_app_DocumentSavedToCentral(object sender, Autodesk.Revit.DB.Events.DocumentSynchronizedWithCentralEventArgs e)
{
MessageBox.Show("Doone", "Done");
}
}
this worked.... Thanks largely in part to the SDK sample project EventsMonitor
namespace RevitModelHealth
{
public class checkHealth : IExternalApplication
{
public Result OnShutdown(UIControlledApplication application)
{
return Result.Succeeded;
}
public Result OnStartup(UIControlledApplication application)
{
application.ControlledApplication.DocumentSynchronizingWithCentral += new EventHandler<DocumentSynchronizingWithCentralEventArgs>(app_syncStart);
application.ControlledApplication.DocumentSynchronizedWithCentral += new EventHandler<DocumentSynchronizedWithCentralEventArgs>(app_syncOver);
return Result.Succeeded;
}
public void app_syncStart(object o ,DocumentSynchronizingWithCentralEventArgs args)
{
MessageBox.Show("","Stasrting");
}
public void app_syncOver(object o,DocumentSynchronizedWithCentralEventArgs args)
{
MessageBox.Show("", "Over");
}
}
}
Try
application.ControlledApplication.DocumentSynchronizingWithCentral += new EventHandler<DocumentSynchronizingWithCentralEventArgs>(m_app_DocumentSavingToCentral)
in your OnStartup() method.
The call is failing because instance member m_app is initialized to null.
The UIApplication.ControlledApplication object that raises the DocumentSynchronizingWithCentralEventArgs is being accessible from the parameter to OnStartup.
You can try this:
public void app_DocOpened(object sender, DocumentOpenedEventArgs args)
{
MessageBox.Show("asd","asd");
Autodesk.Revit.ApplicationServices.Application m_app = args.Document.Application;
m_app.DocumentSynchronizingWithCentral += new EventHandler<DocumentSynchronizingWithCentralEventArgs>(m_app_DocumentSavingToCentral);
m_app.DocumentSynchronizedWithCentral += new EventHandler<Autodesk.Revit.DB.Events.DocumentSynchronizedWithCentralEventArgs>(m_app_DocumentSavedToCentral);
}

how to intercept a method with specific parameters with bytebuddy

I want to intercept method named methodA with one arg which's type is String as blow, what should i do. How to use hasParameters() api?
public class Demo {
public static void main(String[] args) {
new ByteBuddy()
.subclass(A.class)
.method(named("methodA").and(hasParameters(?)))
}
static class A {
public void methodA() {
System.out.println("methodA() invoked.");
}
public void methodA(String arg) {
System.out.println("methodA(" + arg + ") invoked.");
}
}
}
For this you want the ElementMatchers.takesArguments(String.class) matcher.
So something like that:
Class<? extends A> loaded = new ByteBuddy().subclass(A.class)
.method(ElementMatchers.named("methodA").and(ElementMatchers.takesArguments(String.class)))
.intercept(MethodDelegation.to(Demo.class))
.make().load(Demo.class.getClassLoader(), ClassLoadingStrategy.Default.INJECTION).getLoaded();
A instance = loaded.getConstructor().newInstance();
instance.methodA("abc");
instance.methodA();
public class Demo {
static void intercept(String arg){
System.out.println("intercepted");
}
}
To clarify, you need to define a matcher (similar to a filter) to apply to methods. Create some constraint in the matcher so it will only match to some parameter structure you specify:
ElementMatcher<Iterable<? extends ParameterDescription>> matcher = parameterDescriptions -> {
for (ParameterDescription p : parameterDescriptions) {
if (p.getType().equals(TypeDescription.STRING.asGenericType()) && p.getIndex() == 0) return true;
}
return false;
};
ByteBuddyAgent.install();
new ByteBuddy()
.redefine(SomeType.class)
.method(ElementMatchers.hasParameters(matcher))
.intercept(FixedValue.value("Hello World"))
.make()
.load(SomeType.class.getClassLoader(),
ClassReloadingStrategy.fromInstalledAgent());

MethodDecorator.Fody and getting parameter values

I was wondering if with MethodDecorator it's possible to have the passed parameter during the OnException... that would be great since if I can catch an exception I can also have the passed parameter values
Consider this piece of code
static void Main(string[] args)
{
Worker worker = new Worker();
worker.DoWork(6);
}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Assembly | AttributeTargets.Module)]
public class LoggableAttribute : Attribute, IMethodDecorator
{
public void OnEntry(System.Reflection.MethodBase method)
{
var args = method.GetParameters();
var arguments = method.GetGenericArguments();
}
public void OnExit(System.Reflection.MethodBase method)
{
}
public void OnException(System.Reflection.MethodBase method, Exception exception)
{
}
}
and
public class Worker
{
[Loggable]
public void DoWork(int i )
{
}
}
I wish to have 6 on the OnEntry/Nor OnException
Thanks
I know this is an old question, but in case someone stumbles upon this like I did, you can add an Init method and capture the argument values there.
e.g:
public class LoggableAttribute : Attribute, IMethodDecorator
{
private object[] arguments;
public void Init(object instance, MethodBase method, object[] args) {
this.arguments = args;
}
public void OnEntry()
{
// this.arguments[0] would be 6 when calling worker.DoWork(6);
}
}
Check out the example on https://github.com/Fody/MethodDecorator

NServiceBus lost messages

I have some messages that are disappearing only in Production.
In the logs it shows the method HandleBeginMessage() being called immediately followed by HandleEndMessage() without any handlers being called in the middle (this only happens sometimes!!!!)
public class OracleMessageModule : IMessageModule
{
public OracleMessageModule()
{
Factory = new OracleSagaSessionFactory();
}
public OracleSagaSessionFactory Factory { get; set; }
public void HandleBeginMessage()
{
Factory.Begin();
}
public void HandleEndMessage()
{
Factory.Complete();
}
public void HandleError()
{
Factory.Complete();
}
}
So I upgraded NServiceBus from 3.2.8 to 4.6.1 (latest version at the moment) and refactored the code:
public class OracleMessageModule : UnitOfWork.IManageUnitsOfWork
{
public OracleMessageModule()
{
Factory = new OracleSagaSessionFactory();
}
public OracleSagaSessionFactory Factory { get; set; }
public void Begin()
{
Factory.Begin();
}
public void End(System.Exception ex = null)
{
Factory.Complete();
}
}
There's the code for OracleSagaSessionFactory:
public class OracleSagaSessionFactory
{
public string ConnectionString { get; set; }
[ThreadStatic]
private static OracleSagaSession current;
public OracleSagaSession Begin()
{
if (current != null)
throw new InvalidOperationException("Current session already exists.");
var session = new OracleSagaSession(ConnectionString);
current = session;
return current;
}
public static OracleSagaSession Current
{
get
{
return current;
}
}
public void Complete()
{
var c = current;
current = null;
if (c != null)
c.Dispose();
}
}
Looking at NServiceBus code looks like in the new version it cleans the Pipeline, so that's why I believe this is the solution, and I don't believe this is my code problem...
Anyone agrees or disagrees?
Edit1: It only happens on my master node with multiple threads and just sometimes. It doesn't happen on my slave nodes that use just one thread.

Extend DropDownList to add ListSearchExtender

I want to extend DropDownList to add ListSearchExtender.
Using the code below, the control works well in runtime but in design time it give me this error:
SearchDropDownList - DdlTest There was an error rendering the
control. Page cannot be null. Please ensure that this operation is
being performed in the context of an ASP.NET request.
I'd like to understand the cause of this error.
[ToolboxData("<{0}:SearchDropDownList runat=\"server\"></{0}:SearchDropDownList>")]
public class SearchDropDownList : DropDownList
{
private ListSearchExtender listSearchExt = new ListSearchExtender();
protected override void OnInit(EventArgs e)
{
ReloadSettings();
}
protected override void Render(HtmlTextWriter w)
{
base.Render(w);
listSearchExt.RenderControl(w);
}
public void ReloadSettings()
{
listSearchExt.TargetControlID = this.ID;
listSearchExt.ID = this.ID + "_CalendarExtender";
if (Controls.Count > 0)
{
foreach (Control item in Controls)
{
if (item.ID == listSearchExt.ID)
{
Controls.Remove(item);
}
}
}
Controls.Add(listSearchExt);
}
}
i got it by simple way i am not sure if it will make problem in future but for now it work well
protected override void Render(HtmlTextWriter w)
{
base.Render(w);
if (!this.DesignMode)
{
listSearchExt.RenderControl(w);
}
}