Problem with the order of fields in derived classes.

Problem with the order of fields in derived classes.

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


Entophysalis Conferta posted on Monday, December 29, 2008

I have following inheritance structure of business objects:

1. public abstract class BusinessObject<T> : BusinessBase<T> where T : BusinessObject<T>
2. public abstract class AuthorizedUser : BusinessObject<AuthorizedUser>

+ Code
+ Email
+ Kind
+ Login
+ Name
+ Password
+ Phone
+ PreferredContact (enum ContactMethods { Email, Phone })

All properties is managed (using RegisterProperty, GetProperty, SetProperty).

3. public class AdminUser : AuthorizedUser
4. public class ClinicUser : AuthorizedUser

+ City
+ Comment
+ ConvenientTime
+ Fax
+ PracticeName
+ PracticeWebsite
+ State
+ Zip

All properties is managed.

With fresh database, I create admin user. I have AdminUser's field list as:

Field index : Field name
-------------------------
0 : Code
1 : Email
2 : Kind
3 : Login
4 : Name
5 : Password
6 : Phone
7 : PreferredContact

Then I load user list, create clinic user. I have ClinicUser's field list as:

Field index : Field name
-------------------------
0 : City
0 : Code
1 : Comment
2 : ConvenientTime
1 : Email
2 : Fax
2 : Kind
3 : Login
4 : Name
5 : Password
6 : Phone
7 : PracticeName
8 : PracticeWebsite
7 : PreferredContact
8 : State
9 : Zip

I got an exception from validation rule for [PracticeName]:
System.InvalidCastException: Unable to cast object of type 'ContactMethods' to type 'System.String'.

I temporarily fix that by:
+ Add inheritance level to field friendly name
+ Call SyncFieldIndex procedure before every action of create, insert.

        protected virtual void SyncFieldIndex()
        {
            List<Csla.Core.IPropertyInfo> list = FieldManager.GetRegisteredProperties();
            list.Sort(ComparePropertyInfo);
            for (int idx = 0; idx < list.Count; idx++)
            {
                list[idx].Index = idx;
            }
        }

        private int ComparePropertyInfo(Csla.Core.IPropertyInfo x, Csla.Core.IPropertyInfo y)
        {
            int result = string.Compare(x.FriendlyName, y.FriendlyName);
            if (result != 0) return result;
            return string.Compare(x.Name, y.Name);
        }

I would like to confirm that this is a bug or my wrong use of framework. Which is best way to overcome this problem.

Thanks for your help.

ajj3085 replied on Tuesday, December 30, 2008

Hi,

I hit this problem too.  It has to do with the multiple levels of inheritence.  Search this forum for _dummy or dummy to find the solution, or check out the C# 2008 Business Objects book, page 248, RegisterProperty and Inheritance.

Entophysalis Conferta replied on Tuesday, December 30, 2008

ajj3085:
Hi,

I hit this problem too.  It has to do with the multiple levels of inheritence.  Search this forum for _dummy or dummy to find the solution, or check out the C# 2008 Business Objects book, page 248, RegisterProperty and Inheritance.


Thanks for your help. I have read _dummy related threads. I have tried _dummy trick but it did not help.

        #region Dummy Workaround

        private static int _dummy;

        protected BusinessObject()
        {
            _dummy = 0;
        }

        protected override void OnDeserialized(System.Runtime.Serialization.StreamingContext context)
        {
            base.OnDeserialized(context);
            _dummy = 0;
        }

        #endregion


        #region Dummy Workaround

        private static int _dummy;

        protected AuthorizedUser()
        {
            _dummy = 0;
        }

        protected override void OnDeserialized(System.Runtime.Serialization.StreamingContext context)
        {
            base.OnDeserialized(context);
            _dummy = 0;
        }

        #endregion


        #region Dummy Workaround

        private static int _dummy;

        protected AdminUser()
        {
            _dummy = 0;
        }

        protected override void OnDeserialized(System.Runtime.Serialization.StreamingContext context)
        {
            base.OnDeserialized(context);
            _dummy = 0;
        }

        #endregion


        #region Dummy Workaround

        private static int _dummy;

        protected ClinicUser()
        {
            _dummy = 0;
        }

        protected override void OnDeserialized(System.Runtime.Serialization.StreamingContext context)
        {
            base.OnDeserialized(context);
            _dummy = 0;
        }

        #endregion

Entophysalis Conferta replied on Wednesday, December 31, 2008

I have read "C# 2008 Business Objects" book, page 248. I have tried both ways: adding static constructor and initializing dummy static field. But it still did not help.

        #region Dummy Workaround

        private static int _forceInit;

        static BusinessObject() { }

        protected BusinessObject()
            : base()
        {
            _forceInit = 1;
        }

        protected override void OnDeserialized(System.Runtime.Serialization.StreamingContext context)
        {
            _forceInit = 1;
        }

        #endregion


        #region Dummy Workaround

        private static int _forceInit;

        static AuthorizedUser() { }

        protected AuthorizedUser()
            : base()
        {
            _forceInit = 1;
        }

        protected override void OnDeserialized(System.Runtime.Serialization.StreamingContext context)
        {
            _forceInit = 1;
        }

        #endregion


        #region Dummy Workaround

        private static int _forceInit;

        static AdminUser() { }

        protected AdminUser()
            : base()
        {
            _forceInit = 1;
        }

        protected override void OnDeserialized(System.Runtime.Serialization.StreamingContext context)
        {
            _forceInit = 1;
        }

        #endregion


        #region Dummy Workaround

        private static int _forceInit;

        static ClinicUser() { }

        protected ClinicUser()
            : base()
        {
            _forceInit = 1;
        }

        protected override void OnDeserialized(System.Runtime.Serialization.StreamingContext context)
        {
            _forceInit = 1;
        }

        #endregion

JoeFallon1 replied on Wednesday, December 31, 2008

Did you add the _dummy code in every single level of your inheritance chain? You have to do that.

Joe

 

rfcdejong replied on Tuesday, October 13, 2009

Sorry for replying on an old post, but i just ran into this problem.

We removed the _dummy or _forceInit from our inheritance classes and indeed the order of the fields are wrong between the FieldManager and the PropertyInfo.Index

I thought it would had been "fixed" in a new version.

I guess i'm mistaken about it being "fixed"

RockfordLhotka replied on Tuesday, October 13, 2009

It should be fixed in current versions - there's even a unit test that confirms the fix.

The fix was to use reflection to walk the inheritance hierarchy, accessing a static field in each class, thus triggering the static field initialization in each class.

The method is called ForceStaticFieldInit() in the FieldDataManager class. You could put a breakpoint in that method to make sure it is being hit on the first use of each type - on the client and on the server.

RockfordLhotka replied on Tuesday, October 13, 2009

Oh, one other thing.

If you are using Silverlight the rules are a little different because private reflection isn't possible. So the fix is in CSLA .NET for Silverlight, but it can only work if your PropertyInfo<T> fields are public in scope, not private. If they aren't public, then the reflection code can't see the fields and thus can't trigger the field initialization.

Copyright (c) Marimer LLC