Using CSLA PropertyInfo with a DAL

Using CSLA PropertyInfo with a DAL

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


BigPines2 posted on Wednesday, September 02, 2009

I recently moved from CSLA 2.1 to CSLA 3.5.3. One of the new features I am seeing is the ability to use PropertyInfo to let CSLA manage the values of the property. I really like the idea of this new approach because it takes care of all the authorization calls, etc. However, I am using a DAL (ActiveObjects) and this DAL works by using attributes on the property backing fields like this:

<DBRequired("EntityName")> _
Protected mEntityName As String = String.Empty

Public Property Name() As String
     <System.Runtime.CompilerServices.MethodImpl(Runtime.CompilerServices.MethodImplOptions.NoInlining)> _
     Get
          CanReadProperty(True)
          Return mEntityName
     End Get
     <System.Runtime.CompilerServices.MethodImpl(Runtime.CompilerServices.MethodImplOptions.NoInlining)> _
     Set(ByVal Value As String)
          CanWriteProperty(True)
          mEntityName = Value
          PropertyHasChanged()
     End Set
End Property


Obviously, if there is no backing field declared in the class, I can't assign this attribute to it so going with PropertyInfo breaks my DAL which I want to keep.

I have come up with a few "solutions" but I don't like any of them very well:
1) Don't use the new PropertyInfo feature.
2) Stop using the DAL.
3) Use PropertyInfo and keep the backing fields with the attributes and manually keep them in sync. Yuck!
3) Possibly modify the DAL somehow to work with PropertyInfo. Off the top of my head, I don't even have any ideas how I would do this.

Am I missing something? Has anyone else solved this problem? Am I the last hold-out still using the ActiveObjects DAL?

Mike

rsbaker0 replied on Wednesday, September 02, 2009

Why not tag the Property itself with your attribute instead of the backing field?

RockfordLhotka replied on Wednesday, September 02, 2009

As long as you aren't doing Silverlight work you can use PropertyInfo<T> and also use private backing fields. These are compatible concepts, though there are obviously some subtle coding differences.

This is the problem with ORM/DAL frameworks that assume they can set properties and/or fields. And really I can't fault these tools for setting fields - that seems perfectly reasonable in most scenarios. The ones that set properties are just broken imo though.

What would really be nice, is if the DAL framework allowed you to provide a delegate or lambda that it invoked for any get/set operations of a field value. That way you could implement a pair of functions that worked however you saw fit - in this case making calls to LoadProperty() and ReadProperty()...

BigPines2 replied on Thursday, September 03, 2009

RockfordLhotka:
As long as you aren't doing Silverlight work you can use PropertyInfo<T> and also use private backing fields. These are compatible concepts, though there are obviously some subtle coding differences.

This is the problem with ORM/DAL frameworks that assume they can set properties and/or fields. And really I can't fault these tools for setting fields - that seems perfectly reasonable in most scenarios. The ones that set properties are just broken imo though.

What would really be nice, is if the DAL framework allowed you to provide a delegate or lambda that it invoked for any get/set operations of a field value. That way you could implement a pair of functions that worked however you saw fit - in this case making calls to LoadProperty() and ReadProperty()...


Thanks Rocky. Yes, I decided to use both PropertyInfo and private backing fields for now because I want to get the value out of both. It is not optimum for sure because it requires me to keep the backing fields in sync with PropertyInfo which kind of defeats the purpose of the DAL automatically doing everything. However, it is the best compromise I could some up with right now. Eventually, I would like to dig into the DAL and modify it to work with PropertyInfo. I don't see why we couldn't use delegates in this situation. I'll have to look into that when I have some free time. Hehehehe - yeah right!

Out of curiosity, why does my temporary solution not work with Silverlight? Thank goodness I am not using it...yet.

Mike

BigPines2 replied on Thursday, September 03, 2009

OK, I think I have all this down now. Chapter 7: Property Descriptions (Page 235 and 236 of the VB 2008 book) explain it all very well. It won't be as manual as I originally thought to get them to play together. Plus, I will get a performance increase by doing the extra work myself.

I am still a little confused as to why this would not work well with Silverlight though.

Mike

BigPines2 replied on Thursday, September 03, 2009

New problem/concern. The book illustrates SetProperty using private backing fields like this:

Private Shared NameProperty As PropertyInfo(Of String) = RegisterProperty(New PropertyInfo(Of String)("Name"))

<DBRequired("EntityName")> _
Protected mEntityName As String = NameProperty.DefaultValue
Public Property Name() As String
      Get
          Return GetProperty(NameProperty, mEntityName)
     End Get
     Set(ByVal Value As String)
          SetProperty(NameProperty, mEntityName, Value.Trim)
     End Set
End Property


of course, you can see my DAL attribute in there too. The problem is I get a compile error with the above red line of code indicating the "Overload resolution failed because no accessible 'SetProperty' is most specific for these arguments." If I change the code as follows, it works fine:

Private Shared NameProperty As PropertyInfo(Of String) = RegisterProperty(New PropertyInfo(Of String)("Name"))

<DBRequired("EntityName")> _
Protected mEntityName As String = NameProperty.DefaultValue
Public Property Name() As String
      Get
          Return GetProperty(NameProperty, mEntityName)
     End Get
     Set(ByVal Value As String)
          SetProperty(Of String) (NameProperty, mEntityName, Value.Trim)
     End Set
End Property


Is this normal? Has something changed since the 2008 book was released? Is something else going on?

EDIT: I just found this doesn't work either: ReadProperty(NameProperty, mEntityName). Should I simply call: ReadProperty(NameProperty)? Will that work correctly? LoadProperty has a similar problem. I guess I could just access the private backing fields directly if I don't want the authorization, etc.

Mike

RockfordLhotka replied on Thursday, September 03, 2009

>I am still a little confused as to why this would not work well with Silverlight though.

 

The problem with Silverlight is that the MobileFormatter (nor any other formatter) can serialize non-public field values. This is due to reflection limitations on Silverlight.

 

The primary reason for introducing managed backing fields was so that the MobileFormatter could transparently get/set your field values automatically – by just getting them from the field manager.

 

If you have private backing fields this can’t be automatic. There’s still a solution – you must override OnGetState() and OnSetState() in your object and manually get/set the field values to/from the serialization stream. That is not hard code – it is trivial really – but it is tedious code, and it is easy to forget to add the two lines there when you add a new field and then that field doesn’t serialize.

 

So if you go down that road I strongly suggest using code-gen to create your business classes, because then you can just code-gen these two methods and never have to worry about forgetting to add a new field.

 

BigPines2 replied on Thursday, September 03, 2009

RockfordLhotka:
The problem with Silverlight is that the MobileFormatter (nor any other formatter) can serialize non-public field values. This is due to reflection limitations on Silverlight.

The primary reason for introducing managed backing fields was so that the MobileFormatter could transparently get/set your field values automatically – by just getting them from the field manager.


If you have private backing fields this can’t be automatic. There’s still a solution – you must override OnGetState() and OnSetState() in your object and manually get/set the field values to/from the serialization stream. That is not hard code – it is trivial really – but it is tedious code, and it is easy to forget to add the two lines there when you add a new field and then that field doesn’t serialize.


So if you go down that road I strongly suggest using code-gen to create your business classes, because then you can just code-gen these two methods and never have to worry about forgetting to add a new field.

Thanks for the heads up! Even though I currently have no plans to use Silverlight, I think I want to protect for this now just in case. Is there an example of this code posted somewhere? I checked the 2008 book and the 3.0 eBok with no luck.


Thanks again.


Mike

RockfordLhotka replied on Thursday, September 03, 2009

CSLA .NET for Silverlight is later than the 2008 book, and the 2008 book is later than the 3.0 ebook.

 

The primary source of information on CSLA .NET for Silverlight is the video series at http://store.lhotka.net, but even that doesn’t cover manual serialization of private fields, as it is an advanced topic, not a mainstream topic (or that should be the case anyway).

 

There are unit tests in the Silverlight test suite that do manual serialization – check the tests for MobileFormatter. And I think we might have a sample or other test app that does some manual serialization as well.

 

It isn’t difficult though – the idea was to make it roughly comparable to the old VB5/6 serialization model that used a PropertyBag, or to what you do when you manually implement ISerializable in .NET, and we achieved that goal for fields.

 

For child references things are a whole lot more complex, but you should generally always use managed fields for child references, because there’s a whole lot of other work you need to do for child objects if you use a private backing field.

BigPines2 replied on Thursday, September 03, 2009

Thanks Rocky. You have been helpful.

Sounds like it is time for a new book to consolidate all this "new" stuff. Expert 2010 Business Objects?

Since I don't use the DAL for children objects, I shouldn't have a problem using the managed backing fields there.

Mike

RockfordLhotka replied on Thursday, September 03, 2009

My approach is a new video series for CSLA .NET for Windows 3.8 – filming starts Sept 13.

 

BigPines2 replied on Thursday, September 03, 2009

Unfortunately the attribute is not compatible with properties. It only works on the backing fields. I would have to re-write the DAL to use the property somehow and frankly I have no idea where to start on that.

Mike

rsbaker0 replied on Thursday, September 03, 2009

BigPines2:
Unfortunately the attribute is not compatible with properties. It only works on the backing fields. I would have to re-write the DAL to use the property somehow and frankly I have no idea where to start on that.

Mike

Yes, sorry about that clunker of an idea. Rocky is also quite correct in that loading data via the Property setter is in general a (very) bad plan as it incurs much unnecessary overhead and likely has unintended side-effects.

BigPines2 replied on Thursday, September 03, 2009

rsbaker0:
Yes, sorry about that clunker of an idea. Rocky is also quite correct in that loading data via the Property setter is in general a (very) bad plan as it incurs much unnecessary overhead and likely has unintended side-effects.

No worries. Thanks for attempting to help! I also started thinking that having the DAL set the properties directly may get a little confusing on the timing of the updates. :)

You don't have any ideas for me about the above "problems" do you?

Mike

BigPines2 replied on Thursday, September 03, 2009

OK, new problem. *sigh*

Now using more more code directly out of the book (page 236) is not working:

GetPropertyConvert(Of SmartDate, String)(PoliciesAndProceduresReceivedProperty, mPoliciesAndProceduresReceived)

SetPropertyConvert(Of SmartDate, String)(PoliciesAndProceduresReceivedProperty, mPoliciesAndProceduresReceived, Value)


I am getting the compile error "Name 'GetPropertyConvert' is not declared."

Huh? This is part of BusinessBase right? I don't need to import another namespace or anything do I? I am scratching my head here. I'm sure I am just doing something stupid but I sure can't see it.

Mike

rsbaker0 replied on Thursday, September 03, 2009

BigPines2:
.
...


You don't have any ideas for me about the above "problems" do you?

Mike

If you are referring to the issue of having to include the type qualifier in your SetProperty() call, I have a similar issue, but I'm using C# and the message is slightly different.

Even with managed properties and no backing fields. This does not work:

SetProperty(StringProperty, value);

But this does...

SetProperty<string>(StringProperty, value);

RockfordLhotka replied on Thursday, September 03, 2009

It is true that some of the overloads for these methods changed since the book was published. This was due to various feature requests and their side-effects on overload resolution.

BigPines2 replied on Thursday, September 03, 2009

RockfordLhotka:

It is true that some of the overloads for these methods changed since the book was published. This was due to various feature requests and their side-effects on overload resolution.


No problem. If I could just figure out what the replacement for GetPropertyConvert and SetPropertyConvert are, I think I would be set. I have checked the 3.0 eBook but have not found anything there. I checked the CSLA BusinessBase source code and these methods simply don't exist in 3.5.3. *shrug*

Thanks Rocky.

Mike

EDIT: It looks like they just got renamed to GetProperty and SetProperty and you just have to pass the extra type in the call.

RockfordLhotka replied on Thursday, September 03, 2009

I think in 3.5 they were still overloads of SetProperty(). That was the issue – the overloads became ambiguous in certain cases and to solve the issue I had to rename the methods to SetPropertyConvert(), etc. I think that happened in 3.6?

 

 

RockfordLhotka replied on Thursday, September 03, 2009

I should be more helpful.

 

The old overloads were SetProperty<P, V> where P is the type of the backing field and V is the type of the value – so SetProperty<SmartDate, string>().

Copyright (c) Marimer LLC