Csla Inheritance and RegisterProperty

Csla Inheritance and RegisterProperty

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


decius posted on Thursday, October 29, 2009

I'm trying to figure out what has caused this error to creep up on me.  I have a custom Csla.BusinessBase abstact class (MyBase<T> : Csla.BusinessBase <T>) and an implemented conrete class (MyClass : MyBase<MyClass>).  

I have protected static PropertyInfo's on both of these.  However, somehow, an error is being thrown upon constuction of MyClass.  I can't seem to recreate the problem in another project and am having trouble finding the problem.  The csla framework throws an error when executing the RegisterProperty method of the abstract class.  Somehow the IsLocked property is returning true here.  What could be causing this?

if (list.IsLocked)

throw new InvalidOperationException(string.Format(Resources.PropertyRegisterNotAllowed, info.Name, objectType.Name));

RockfordLhotka replied on Friday, October 30, 2009

The list of registered properties gets locked when a property is accessed, so something must be accessing a property on that type.

Or is it possible you are using the RegisterProperty() overload where you provide the containing type? That's one of the most common issues - due to copy-paste errors where that type is invalid after being pasted from another class.

decius replied on Friday, October 30, 2009

Could this have anything to do with the fact that the ctor I'm using uses the anonymous parameter ctor syntax?

MyClass tmp = new MyClass() { PropertyFromAbstract = "some value", PropertyFromMyClass = "some value"};

Could this somehow affect the order in which the static property info's are called?

decius replied on Friday, October 30, 2009

Nevermind, that doesn't seem to make a difference.  What I'm seeing is odd though:
  1. The RegisterProperty Method in the concrete class fires immediately upon construction of the object
  2. The Setter of a property in the abstract class gets fired next
  3. The SetProperty method in the setter seems to THEN cause the RegisterProperty method in the abstract class to fire.
That's odd to me, why isn't the static properties of the abstract classes getting fired upon construction of the object??? That's what I would expect, but isn't what's happening... Should 3 happen before 2?

So it seems that when the setter on the abstract class is invoked THATS what causes the static properties of the abstract class to initialize, which isn't what I'm used to happening.... I would think the ctor of the concrete class would kick this off, but it's not. 

decius replied on Friday, October 30, 2009

weird. Just as I thought.  If I add a LoadProperty() call in the ctor I'm using, it causes the static properties to initialize on the abstract class, thus when the SetProperty method gets called, the error isn't thrown.  The only thing now is.... why the heck is this happening??? I've never had this problem before, nor can I recreate it in other projects.  I'll just past my code here, maybe someone can take a look for me and tell me if something seems out of the norm???


public class Task : StickyBase<Task>
    {
        private static PropertyInfo<string> AssignedToProperty = RegisterProperty<string>(c => c.AssignedTo, "Assigned To", string.Empty);

        public string AssignedTo
        {
            get { return GetProperty(AssignedToProperty); }
            set { SetProperty(AssignedToProperty, value); }
        }

        public Task()
        {
            //Call this here so that when the Task is constructed, it also initializes the abstract class's static properties too
            LoadProperty(TitleProperty, string.Empty);
        }

    }






public abstract class StickyBase<T> :
        BusinessBase<T> where T : StickyBase<T>
    {

        #region Business Methods

        protected static PropertyInfo<string> TitleProperty = RegisterProperty<string>(c => c.Title, "Title", string.Empty);

        public string Title
        {
            get { return GetProperty(TitleProperty); }
            set
{
//When this gets called without making a LoadProperty call in the concrete ctor, the set property method causes the static properties to initialize while locked and throws an error
 SetProperty(TitleProperty, value);
}
        }

        #endregion


    }

RockfordLhotka replied on Friday, October 30, 2009

The initialization of static fields in .NET doesn’t work the way anyone (except presumably the CLR designers) would think.

 

Static fields are initialized when a static member of the specific type is accessed. The words here are specifically chosen.

 

In an inheritance scenario you have n types that combine to form the object you are using. Each type is initialized independently. And each type only initializes when a static member on that type is accessed.

 

The field manager (in some 3.6.x version and higher) includes a solution to this problem where it uses reflection to force initialization of a static field on each type in the inheritance hierarchy the first time any of the four helper methods are accessed (ReadProperty(), etc). In Silverlight this only works if the static fields are public in scope.

 

You can actually force this yourself if you’d like, by calling

Csla.Core.FieldManager.FieldDataManager.ForceStaticFieldInit(typeof(yourclass))

 

But normally you shouldn’t have to do this, because the standard data portal object creation model will have triggered this automatically.

 

Are you running 3.6.1 or higher? If so, it could have something to do with the fact that you are using the constructor to create the object rather than the data portal. CSLA is really designed around the idea that you’ll use the data portal to create object instances – which is why the templates and book all show the use of a non-public constructor. Using the constructor bypasses some of the normal object initialization processing, and could be part of the issue here.

decius replied on Monday, November 09, 2009

RockfordLhotka:

 In Silverlight this only works if the static fields are public in scope.

 

It seems I ran into this problem today. Everything works fine with the object model, until it gets to the Silverlight callback and BAM, same issue. What has been the recommended solution to this? Should I just make my property infos public? Is there possibly a better solution than that?

 

Perhaps in my BeginGet SL factory method, I can handle the callback and explicitly call Csla.Core.FieldManager.FieldDataManager.ForceStaticFieldInit(typeof(yourclass))?

Thanks again for your help

RockfordLhotka replied on Monday, November 09, 2009

ForceStaticFieldInit() will only work on the Silverlight side if your PropertyInfo<T> fields are public. Otherwise reflection can't interact with them, and so can't force them to initialize.

For my part, I've been making all my PropertyInfo<T> fields public. I find that I often want them in an ObjectFactory or even in the UI (to call CanReadProperty(), etc), so having them public is generally useful anyway.

Copyright (c) Marimer LLC