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
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).
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.
Rocky,
Have you ever thought about a possibility to clear a lazy loaded value?
Stefan
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?
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
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.
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?
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) ).
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.
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.
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.
3.8.3
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)
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)
I'm curious as to why you are even trying to use this low-level API instead of ReadProperty?
Chalk it up to Csla noobness. 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