Overriding "DataPortal_Create" to override the RunLocal Attribute

Overriding "DataPortal_Create" to override the RunLocal Attribute

Old forum URL: forums.lhotka.net/forums/t/1033.aspx


ward0093 posted on Saturday, August 26, 2006

I have two class:

TransactionBase (my base class) and ProductTransaction (my derived class, that derives from TransactionBase.

In TransactionBase, I set the "DataPortal_Create" method with a RunLocal attribute because I do not need to get anything from the database

However, when I use the ProductTransaction, I want to go out to the database on the Create method (i.e. ProductTransaction.NewRoot()).  So, if I override the "DataPortal_Create" method in my derived class, will that RunLocal Attribute disappear and the NewRoot method call WILL go out to the server?

ward0093

xal replied on Saturday, August 26, 2006

Yes, it will go to the remote dp. The RunLocal() attribute is not inherited by a method that overrides the one in the base class.


Andrés

ward0093 replied on Saturday, August 26, 2006

Great, Thanks...

I kind of figured that (and had not testing mechanism setup)... but wanted to make sure

ward0093

JoeFallon1 replied on Monday, August 28, 2006

I have tested this scenario.

 The RunLocal() attribute seems to be inherited by a method that overrides the one in the base class. (Or at least it is determined to exist by Rocky's code, so the effect is the same.)

I just stepped through the code and found that it returns True when the Base class has the atrribute on it even though the methodInfo is from the final type.

The methodInfo is found here: Note the specific use of oneLevelFlags. The hierarchy is not supposed to be flattened with this call. ====================================================================

Public Function FindMethod(ByVal objType As Type, ByVal method As String, ByVal types As Type()) As MethodInfo

Dim info As MethodInfo = Nothing

Do

' find for a strongly typed match

info = objType.GetMethod(method, oneLevelFlags, Nothing, types, Nothing)

If info IsNot Nothing Then

Exit Do ' match found

End If

objType = objType.BaseType

Loop While objType IsNot Nothing

Return info

End Function

====================================================================

Private Function RunLocal(ByVal method As MethodInfo) As Boolean

  Return Attribute.IsDefined(method, GetType(RunLocalAttribute))

End Function

The Base type is part of the methodInfo too and maybe the function above *does* flatten the hierarchy so that it it returns True when it should be False. The documentation says is should not do so. A third Boolean parameter can be used to "determine whether any custom attributes are applied to a member of a type. Parameters specify the member, the type of the custom attribute to search for, and whether to search ancestors of the member."

Now I am not sure why it works this way, but it does. I guess it would help if someone else could weigh in on this and let us know why it is acting this way.

This is why I decided to omit the RunLocal attribute from my Base class but add it to my developer level class which is the final type. This way I can codegen both classes and have RunLocal as a default. But then as a developer I can remove the attribute on the final type if I need to hit the DB.

===============================================================

Update:

here is the code from the command window using the 3rd Boolean parameter:

>? Attribute.IsDefined(method, GetType(RunLocalAttribute), True)

True

>? Attribute.IsDefined(method, GetType(RunLocalAttribute), False)

False

Apparently the 3rd parameter defaults to True and it was omitted by Rocky who probably assumed it was False. So it seems there is a bug in the CSLA code for the RunLocal function. It should read:

Return Attribute.IsDefined(method, GetType(RunLocalAttribute), False)

Joe

 

 

 

ward0093 replied on Monday, August 28, 2006

shoootttt!!!!.... this is no good!!!

I have something like 100 objects.., I am going to have to add "RunLocal" to all those classes.

There must be a better way!... can someone way in on this?  Rocky?

ward0093

ajj3085 replied on Monday, August 28, 2006

Joe,

If you don't mind modifying Csla, you could define RunLocal on the base class and still allow bus. devs. to not have it bubble up..  Change the RunLocal attribute so that it is not inheritable.  You do this by specifying an attribute on the RunLocalAttribute class.

Andy

ward0093 replied on Monday, August 28, 2006

thanks Andy, but do you have an example of how to do that?

also... does this mean that none of our Bis Classes have to have the <Serializable()> Attribute applied?  Because it is already applied to the base classes?

ward0093

ward0093 replied on Monday, August 28, 2006

wait... Andy... that will not work either, because then the attribute will not be applied to any of our other classes correct... I mean, we can not use a BaseClass in our BO Library with the RunLocal Attribute applied if it is not inherited to all the other derived classes... correct?

ajj3085 replied on Monday, August 28, 2006

ward0093:
wait... Andy... that will not work either, because then the attribute will not be applied to any of our other classes correct... I mean, we can not use a BaseClass in our BO Library with the RunLocal Attribute applied if it is not inherited to all the other derived classes... correct?


To get the behavior your want you'd have to put that attribute on the RunLocalAttribute class.  If I read Joe's post correctly the behavior Xal describes isn't correct.  To make the behavior as Xal describes, one way would be to make that attribute non-inheritable...  Is that not the behavior you wanted?

JoeFallon1 replied on Monday, August 28, 2006

The simplest fix would probably be to change this one line of code in the CSLA 2.1 framework.

Then it would act "correctly". You could codegen the <RunLocal> attribute for all your base classes. All derived classes would use it. If you need to Override DP_Create in a derived class you could omit the attribute and talk to the DB because you would have gone through the Data Portal.

But it would be nice if Rocky or someone else could confirm the behavior first. And even nicer if Rocky were to make the change. (I am trying to not modify the framework in 2.x if I can help it. I want to extend it using my own code where possible.)

Return Attribute.IsDefined(method, GetType(RunLocalAttribute), False)

Joe

ajj3085 replied on Monday, August 28, 2006

Joe, that would work as well, but I think the attribute on RunLocalAttribute is better because it more clearly identifies the behavior; i.e. you are utilizing self documenting code because the fact that RunLocal won't be inheritied can be seen when you look at the type declaration.

Now whether or not the attribute SHOULD be inherited is another matter...

xal replied on Monday, August 28, 2006

Sorry about the wrong post... I really thought it wasn't considered if it wasn't specified in the override...
The reason was that I'm pretty sure I read that was the intended behaviour.
We'll see what rocky has to say about it.
I agree with Andy, the attribute should be marked as not inherited. But what Joe says is also correct, and both changes could be made.
The first one because of "declarativeness". The second, so that the check doesn't have to walk up the inheritance tree to find that out....


Andrés

JoeFallon1 replied on Monday, August 28, 2006

Andres,

No need to apologize for the "wrong" post. I had known about this behavior for a while and had even built it into my Codesmith templates. So I was surprised to see your comment. That is what lead to some deeper digging and exposed the possible bug. In other words, without the bug, your comment would have been spot on.

Joe

 

ajj3085 replied on Tuesday, August 29, 2006

Technically I'm not sure if its a bug or by design.. we'll have to wait to see what Rocky says.

JoeFallon1 replied on Tuesday, August 29, 2006

ajj3085:
Technically I'm not sure if its a bug or by design.. we'll have to wait to see what Rocky says.

 

I got a message from Rocky.

He said he would change it in 2.1 because:

"I certainly _expected_ false to be the default."

Joe

 

ajj3085 replied on Tuesday, August 29, 2006

Well I'd say that confirms its a bug. Smile [:)]

ward0093 replied on Wednesday, August 30, 2006

Now that we are here... I am confused on what the solution is going to be

Will I have to mark all my BO Classes - DB_Create methods with the RunLocalAttribute because it is not Inheritable?  Not sure I like that... becuase I have to touch over 100 code files.

Then again... that might be the only way to get the functionality we need.

is that how it is going to work?

ward0093

ajj3085 replied on Wednesday, August 30, 2006

You could modify Csla so that it keeps the behavior you want.  You just have to decide if its worth your time to change 100 files once, or to continually modify the relevent files in Csla as each new version comes out.

JoeFallon1 replied on Wednesday, August 30, 2006

Well, let's define where "here" is first. <g>

The original problem is:

==========================================================

TransactionBase (my base class) and ProductTransaction (my derived class, that derives from TransactionBase.

In TransactionBase, I set the "DataPortal_Create" method with a RunLocal attribute because I do not need to get anything from the database

However, when I use the ProductTransaction, I want to go out to the database on the Create method (i.e. ProductTransaction.NewRoot()).  So, if I override the "DataPortal_Create" method in my derived class, will that RunLocal Attribute disappear and the NewRoot method call WILL go out to the server?

ward0093

==========================================================

This response would be correct if there was no bug:

Yes, it will go to the remote dp. The RunLocal() attribute is not inherited by a method that overrides the one in the base class.

==========================================================

Once the bug is fixed, then your Base classes will all have the RunLocal attribute in them.

Unless you Override DataPortal_Create then all of your final types will use the base method and run locally.

For those Types where you do override DataPortal_Create, if you omit the attribute then it is not inherited so you can run remotely to the database.

The only files you have to "touch" are those where you override DataPortal_Create.

Joe

 

Copyright (c) Marimer LLC