When my private backing field SetProperty is called...even though it is ByRef, the value in mDTO.EvidNum does not change until AFTER the PropertyHasChanged runs.
This means that a simple rule like StringRequired runs and sees an empty string and the rule stays broken. But the next step after rules running is that the mDTO.EvidNum property setter is called. So you look at the business object and the rule is broken but there is a value there.
Any ideas? I assume the CSLA code assumes a private backing field is just a string not a Class.StringProperty.
I have the following code:
Private Shared EvidNumPropertyInfo As PropertyInfo(Of String) = RegisterProperty(Of String)(Function(c) c.EvidNum, RelationshipTypes.PrivateField)
Public Property EvidNum() As String
Get
Return GetProperty(EvidNumPropertyInfo, mDTO.EvidNum)
End Get
Set(ByVal value As String)
SetProperty(EvidNumPropertyInfo, mDTO.EvidNum, value)
End Set
Normal 0 false false false EN-US JA X-NONE
End Property
Hi,
Which version of CSLA do you use?
I wouldn't be surprised if this has to do with the "mDTO.EvidNum" property.
What is sent in as a ref value to the SetProperty method when the wrapped object expose a property or a field.?
When it is a property I suspect that what is sent in as ref parameter is not the property method but the "backing field value" of the property.
Does it work correct if you change mDTO.EvidNum to a private member field in the business object class?
Does it work correct if you change mDTO.EvidNum to a string Field rather than a property?
Using the latest Beta version 4.5.8
I just did a test and it works as expected if it is a plain string private member backing field in my business object. ex. mTesting as string
Here is the property in my DTO
Public Overridable Property [EvidNum]() As System.String
Get
Return CType(GetValue(CInt(EvidenceFieldIndex.EvidNum), True), System.String)
End Get
Set
SetValue(CInt(EvidenceFieldIndex.EvidNum), value)
End Set
End Property
Hi,
The challenge about properties is that they are actually compiled into 2 methods in the object
get_EvidNum and set_EvidNum - it is not represented as a single field/value.
See also https://msmvps.com/blogs/luisabreu/archive/2007/09/17/properties-on-c.aspx
I'm sure there is a good reason why this is not supported out of the box as a private backing field in CSLA - tho' I haven't spent any time digging into it.
You _might_ get away with creating your own intermediate base class and create your own SetProperty2 method without a ref parameter and use f.ex MethodCaller.CallPropertyGetter / MethodCaller.CallPropertySetter to get/set the property values of the DTO. (Or use plain reflection in .NET or look at FasterFlect)
Hi,
Remember, the compiler creates 2 methods in compiled IL code when you create a property in your code: get_<propertyname> and set_<propertyname>
So when you _try_ to pass a property as ref parameter you actually pass the returned value.
You might want to try and create your own implementation of SetProperty in your own intermediate base class that accepts a property and use reflection to get/set the property value (or Csla.Reflection.MethodCaller or FasterFlect).
I'm sorry Jonny, I don't understand what you are saying about my own implementation in a base class.
Your saying by passing in a property, it is being passed ByVal and not ByRef...so it ends up getting Set after the rules run. You are saying somehow I can use reflection to overcome that?
By the way here is a description of what is going on from a different forum:
When you execute Method(Object.PropertyValue), and PropertyValue is passed ByRef, the method first executes the PropertyValue GET statement to populate the variable which will be used in the method. If you do not update the object pointer, but only change a member of the parameter variable inside the method, the change to that member's value would be immediately apparent outside the executing method. But if the variable is reassigned to another pointer, the pointer internal to the original property will not be updated until the SET statement is executed, so you won't see the object change immediately.
Maybe some example code would help:
Public Class Form1
Dim f As New Fiddle
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Foo(f.Faddle, f.Button)
End Sub
Private Sub Foo(ByRef obj As String, ByRef btn As Button)
obj &= " Changed"
'btn = New Button
btn.Text &= " Changed"
End Sub
End Class
Public Class Fiddle
Public Sub New()
mButton = New Button
mButton.Text = "First"
End Sub
Private mFaddle As String = "Fiddle Faddle"
Public Property Faddle() As String
Get
Return mFaddle
End Get
Set(ByVal value As String)
mFaddle = value
End Set
End Property
Private mButton As New Button
Public Property Button() As Button
Get
Return mButton
End Get
Set(ByVal value As Button)
mButton = value
End Set
End Property
End Class
Place a breakpoint on Sub Foo() then run the form. First, note the values of Faddle and Button as listed on the 'f' variable declaration. Now step through sub Foo() and stop at 'End Sub'. Notice that 'f.Button.Text' has changed, however 'f.Faddle' has not yet changed. Continue to step through the code. Note how execution returns to the executing method, then to each property SET statement, then back to the method. Immediately after the Faddle SET statement and execution returns to the method, you can check the value of f.Faddle and see that it has changed.
Now uncomment the line of code in Foo(). Repeat the exercise, but this time note that the Button.Text does not change on 'f' until the SET statement executes for 'Button'. Since we change the variable pointer for the button parameter inside of Foo, the changes we make to that button are not part of 'f' until the SET statement updates the property's internal variable.
It is foggy but I kind of get why it is happening. Are you saying I should change the implementation of the property set in the DTO or the BO?
Sean
From what I can figure out you have 2 options:
In case of 2)
I always recommend that you have your own intermediate base class so that you can alter/add behavior in the base classes.
Ex inheritance hierarchy:
Csla.BusinessBase<T>
- YourApp.MyBusinessBase<T>
- YourBusinessObject : YourApp.MyBusinessBase<YourBusinessObject>
This structure allows you to add new bhavior or overrides in YourApp.MyBusinessBase class.
What you need to get - is the System.Reflection.MethodInfo of the underlying property as this will allow you to call the property get/set methods on the given object. I would first try to use another name - ex SetProperty2 - and work out how the code and parameters must be.
OK I already have an intermediate base class MyBusinessBase, so I can try both your suggestions. I will try both. #1 would be editing the code generator for the DTOs.
Re #2, I feel like an idiot but I can't picture what you are saying in my mind. Do you have a simple example to help me see what you are saying. Just a simple string property example of some kind?
Thanks a million!
Explanation of properties: https://msmvps.com/blogs/luisabreu/archive/2007/09/17/properties-on-c.aspx
For the other part google PropertyInfo or look at Csla.Reflection.MethodCaller or search and look at FasterFlect.
The part I am not getting is how would the set line change? I just need a hint to get me going.
Set(ByVal value As String)
SetProperty(EvidNumPropertyInfo, mDALObject.EvidNum, value)
End Set
Semantically - in C#:
public class MyBO { MyDTO _myDto = new MyDTO(); private PropertyInfo StringVal1Property; public string StringVal1 { get { return _myDto.S1; } set { SetProperty2(StringVal1Property, value, _myDto, "S1"); } } void SetProperty2<T>(PropertyInfo property, T newValue, object obj, string propertyName) where T:IComparable { var method = obj.GetType().GetProperties().First(p => p.Name == propertyName); T orgValue = (T)method.GetValue(obj); if (orgValue.CompareTo(newValue) != 0) { method.SetValue(obj, newValue); PropertyHasChanged(property); } } protected void PropertyHasChanged(PropertyInfo property) { } }
AHA! OK I see what you are saying now. And I see where you would put that in the intermediate base class.
Am I correct that I would also have to add the bits that do this in the framework since I am bypassing SetProperty:
if (_bypassPropertyChecks || CanWriteProperty(propertyInfo, noAccess == Security.NoAccessBehavior.ThrowException))
Yes.
You know about the Csla.Data.DataMapper class - right?
This class can be used to map (copy) values between 2 objects and creates a default map provided the properties have the same name in both objects!
I believe what you are experiencing is specific to VB.NET and "Copy Back ByRef" functionality
See: http://blogs.msdn.com/b/vbteam/archive/2010/01/26/the-many-cases-of-byref.aspx
You are right...C# would have thrown a compiler error trying to pass in a property ByRef. VB.NET allows it and I see why that is kind of a problem. How would it know the property is not ReadOnly etc.
So, thanks a gazillion to you, I know I can do one of the following:
Re DataMapper...it is a little too all or nothing for me. I do use it here and there...but there are downsides I struggle with:
My way, if the DTO gets updated in the Property Setter then everything is taken care of and values will get persisted. And as a bonus all the relevant logic for setting code is in the setter...rather than both in the Setter and the TransferTo/FromDB methods.
Thanks again Jonny!
What if I change it to a tandem approach...I could change from this:
Private Shared EvidNumPropertyInfo As PropertyInfo(Of String) = RegisterProperty(Of String)(Function(c) c.EvidNum, RelationshipTypes.PrivateField)
Public Property EvidNum() As String
Get
Return GetProperty(EvidNumPropertyInfo, mDTO.EvidNum)
End Get
Set(ByVal value As String)
SetProperty(EvidNumPropertyInfo, mDTO.EvidNum, value)
End Set
End Property
To This:
Private Shared EvidNumPropertyInfo As PropertyInfo(Of String) = RegisterProperty(Of String)(Function(c) c.EvidNum)
Public Property EvidNum() As String
Get
Return GetProperty(EvidNumPropertyInfo, mDTO.EvidNum)
End Get
Set(ByVal value As String)
SetProperty(EvidNumPropertyInfo, value)
If mDTO.EvidNum <> GetProperty(EvidNumPropertyInfo) Then mDTO.EvidNum = value
End Set
End Property
OK this is starting to make sense. I think I get the problem.
Others have explained that you can't do this in C#. In VB.NET, you can do this, even with option strict/explicit on:
Option Strict On
Option Explicit On
Imports System.Text
Module Test
Sub Main()
Dim sb as new StringBuilder
Foo (sb.Length)
End Sub
Sub Foo(ByRef x as Integer)
End Sub
End Module
The above code is equivalent to this C# code:
using System.Text;
class Test
{
static void Main()
{
StringBuilder sb = new StringBuilder();
int tmp = sb.Length;
Foo(ref tmp);
sb.Length = tmp;
}
static void Foo(ref int x)
{
}
}
Personally I'm glad that C# doesn't have this - it's muddying the waters quite a lot, particularly in terms of the value of the property if the parameter is set within the method but then an exception is thrown.
But Jonny, I still do not understand your suggesting for using reflection in an intermediate base class.
Copyright (c) Marimer LLC