Unexpected FieldManager.GetFieldData() Results

Unexpected FieldManager.GetFieldData() Results

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


comp1mp posted on Thursday, October 27, 2011

I have the following code which loops and populates the parameters for a sql command

foreach (var p in FieldManager.GetRegisteredProperties())

    if (((IOrionPropertyInfo)p).IsSqlColumn)

        cm.Parameters.AddWithValue("@" + ((IOrionPropertyInfo)p).SqlColumnName, FieldManager.GetFieldData(p).Value));

Unfortunately, FieldManager.GetFieldData(p) is returning null. To be clear, Value is not null, rather the FieldManager is returning null when trying to get the FieldData for the IPropertyInfo p.

Setting a break point, i can inspect the business object with no problem, so the property get is working for all of the properties.

How is this possible if the BO is properly instantiated?

Thanks,

Matthew 

StefanCop replied on Thursday, October 27, 2011

As far as I know, getting null from FieldManager.GetFieldData(p) is the case when having lazy loading properties. That means you have not yet LoadProperty(p, aValue) nor SetProperty(p, aValue).

For lazy loading you can test with FieldManager.FieldExists(p) if it already has been set (to a value including null).

 

RockfordLhotka replied on Thursday, October 27, 2011

Stefan is correct. The field data object is lazy loaded - as lazy as we can make it actually, because that helps reduce the amount of data that must be serialized in some cases.

StefanCop replied on Thursday, October 27, 2011

Rocky,

Have you ever thought about a possibility to clear a lazy loaded value? 

Stefan

RockfordLhotka replied on Thursday, October 27, 2011

stefan cop

Have you ever thought about a possibility to clear a lazy loaded value? 

This has come up before, and I recall having some good reason why that isn't supported. Unfortunately I don't recall the reason itself, just that there was one :)

If you'd like to explore this, would you start a new thread so we don't hijaak this one with a whole other discussion?

StefanCop replied on Thursday, October 27, 2011

To close this side-topic, I've found this post with a statement below:  Is there a way to undefine a field in the FieldManager

JonnyBee

Allowing the field value to be reset has been in the wishlist for some time but unfortunately is not trivial to implement due to the N-level edit and how CancelEdit requires the original instance to restore (rollback) to state.

comp1mp replied on Thursday, October 27, 2011

That makes sense.

Upon further investigation, I have discovered something quite remarkable!  

My BO is the datasource for a BindingSource which is bound to a DataGridView [WinForms]. None of the properties are required, so in my initial testing i just entered data in the first couple of columns. Several of the columns i did not edit, but their FieldData had nevertheless been initialized. So I would get halfway through my properties in the above code and BAM, the FieldData would be null. 

It turns out several of the columns were not visible. If I simply scroll to view all columns all of my Properties are initalized and the code above runs without issue!

Is this a possible bug with CSLA, or does the WinForm binding system not call a PropertyGet until the control bound to the property is actually drawn?

StefanCop replied on Thursday, October 27, 2011

Your observation sound familiar.

Not until the "rows" of a control bound to a business object is actually drawn, the properties' getters are invoked and thus, the values loaded.
Same is true for a horizontal scrollbar, where certain columns are initially not visible and their values not loaded.

But was is your cause to BAM!? If your code accesses values through the Property, it will be loaded. If you access it directly (i.e. ReadProperty(p) ), then you have to take into account, that it can be "undefined" ( =! FieldManager.FieldExists(p) ).

 

StefanCop replied on Thursday, October 27, 2011

Sorry about this, I forgot your initial post.

But was is your cause to BAM!?

I think in your case the values shall be set, or you need to define what an absence of a value means to your app. You could also take p.DefaultValue (if you have defined those in RegisterProperty) , or choose a DBNull?

In my application only non-scalar properties - properties referencing other BOs - are defined lazy. All scalar values are always set.

comp1mp replied on Friday, October 28, 2011

stefan cop

I think in your case the values shall be set, or you need to define what an absence of a value means to your app. You could also take p.DefaultValue (if you have defined those in RegisterProperty) , or choose a DBNull?

 

Considering the above, I tried an experiment. None of my property declarations used the default value overload, so I added a default for a string property which was bound to a column which was not initially visible in the grid. The IPropertyInfo was initialized without scrolling the column into view.

I assume this is because properties are set to the default value when a new BO is created.

Unfortunately, this behavior does not seem to apply to a nullable property with a default property of null.

        private static PropertyInfo<int?> StateProperty = RegisterProperty<int?>(c => c.State, "State", null);

        [SqlColumn("State")]

        public int? State

        {

            get { return GetProperty(StateProperty); }

            set { SetProperty(StateProperty, value); }

        }

 For the property above, the IPropertyInfo is not initialized (IE the BO fails to initialize it upon creation).

Is this possibly a bug? Or are there underlying issues with initializing properites with a null value, and the above behavior is by design?

In any case, I can certainly take Stefan's advice and use the DefaultValue if the field does not exist.

StefanCop replied on Friday, October 28, 2011

Are you using CSLA 4.0 or 4.1 ?

I think in 4.0 some overloads are "missing" (on ReadOnlyBase<T>).  A workaround is to use this instead:

public static readonly PropertyInfo<DateTime?> EndDateProperty =
    RegisterProperty<
DateTime?>(new PropertyInfo<DateTime?>("EndDate", "Ending date", default(DateTime?));

Unfortunatelly, this doesn't provide the better lambda expression. But 4.1 has an overload with lambda.

comp1mp replied on Friday, October 28, 2011

3.8.3

 

StefanCop replied on Friday, October 28, 2011

I don't know details about 3.8.3. 

Are you sure that FieldManager.GetFieldData(p) is null, and not FieldManager.GetFieldData(p).Value?

(and null is not DBNull)

RockfordLhotka replied on Friday, October 28, 2011

I'm curious as to why you are even trying to use this low-level API instead of ReadProperty?

We never really intended for people to use the field manager directly in this manner - the intent is that people would use ReadProperty and LoadProperty for DAL implementations. That's why there are non-generic overloads of these methods (though using the generic overloads is better if at all possible).

It seems like you are making your life harder than it should be.

(and assuming more risk - because lower-level APIs change over time faster than higher-level APIs, so you are at more risk of hitting breaking changes in future versions of CSLA)

comp1mp replied on Friday, October 28, 2011

RockfordLhotka

I'm curious as to why you are even trying to use this low-level API instead of ReadProperty?

Chalk it up to Csla noobnessEmbarrassed. For some reason I was thinking that ReadProperty only took PropertyInfo<T> as an argument. I now see it has an IPropertyInfo overload.

 When I was writing the code in the original post, the first line of code led me  to explore the FieldManager members, and lo and behold GetFieldData().Value seemed to fit the bill.

Thank both of you!

Copyright (c) Marimer LLC