Problems with Begin- / Cancel- / Apply-Edit operations

Problems with Begin- / Cancel- / Apply-Edit operations

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


DeepCore posted on Friday, December 10, 2010

Hello community!

I'm lost with the undo capabilities of CSLA. I have found several questions/answers dealing with the strange behavior of data binding under windows forms.

But alas, I'm not using data binding at all in my test application. I'm using CSLA 3.8.4, C#, .NET 3.5SP1, VS2010, NHibernate with Fluent-NHibernate.

I have built the following business objects:

User object with 4 properties: UserID, FirstName, LastName and Role
UserRole object with 2 properties: ID and Description.

So far so good, I am able to persist these objects in the SQLite-DB. Smile

 

Then I reload one user object from the DB, call .BeginEdit on it and assign another UserRole to the Role property ...

Afterwards I call .CancelEdit, expecting my user object's role property would revert to it's state before the .BeginEdit call ...

But that's not the case Crying

What am I doing wrong ?

 

Seasons greetings

Philippe

DeepCore replied on Friday, December 10, 2010

As I've spent almost 2 days on this issue, I thought I may be lost in my code … so I reverted back to a simple test …

But even this doesn't work to my expectations … the MyChild property doesn't revert back to 'InitialChild' after the call to root.CancelEdit …

 

    [Serializable]
    public class Root : BusinessBase<Root>
    {
        private static PropertyInfo<int> IdProperty = RegisterProperty<int>(typeof(Root), new PropertyInfo<int>("Id", "Id"));
        public int Id
        {
            get { return GetProperty<int>(IdProperty); }
            set { SetProperty<int>(IdProperty, value); }
        }

        private static PropertyInfo<string> NameProperty = RegisterProperty<string>(typeof(Root), new PropertyInfo<string>("Name", "Name"));
        public string Name
        {
            get { return GetProperty<string>(NameProperty); }
            set { SetProperty<string>(NameProperty, value); }
        }

        private static PropertyInfo<Child> ChildProperty = RegisterProperty<Child>(typeof(Root), new PropertyInfo<Child>("Children", "Children"));
        public Child MyChild
        {
            get
            {
                if (!FieldManager.FieldExists(ChildProperty))
                    LoadProperty<Child>(ChildProperty, new Child());
                return GetProperty<Child>(ChildProperty);
            }
            set { SetProperty(ChildProperty, value); }
        }

        protected override object GetIdValue()
        {
            return ReadProperty<int>(IdProperty);
        }
    }

 

    [Serializable]
    public class Child : BusinessBase<Child>
    {
        private static PropertyInfo<int> IdProperty = RegisterProperty<int>(typeof(Child), new PropertyInfo<int>("Id", "Id"));
        public int Id
        {
            get { return GetProperty<int>(IdProperty); }
            set { SetProperty<int>(IdProperty, value); }
        }

        private static PropertyInfo<string> NameProperty = RegisterProperty<string>(typeof(Child), new PropertyInfo<string>("Name", "Name"));
        public string Name
        {
            get { return GetProperty<string>(NameProperty); }
            set { SetProperty<string>(NameProperty, value); }
        }

      

        protected override object GetIdValue()
        {
            return ReadProperty<int>(IdProperty);
        }

        private static int _lastId;

        public Child()
        {
            LoadProperty<int>(IdProperty, System.Threading.Interlocked.Increment(ref _lastId));
            MarkAsChild();
        }      
    }

 

    class Program
    {
        static void Main(string[] args)
        {

            var root = new Root {Id = 1, Name = "TestRoot"};
            root.MyChild = new Child() { Name = "InitialChild" };

            root.BeginEdit();

            root.MyChild = new Child(){Name = "TestChild"};

            root.CancelEdit();
        }
    }

 

JonnyBee replied on Friday, December 10, 2010

The problem here is that you cannot change the object references.

You may change the properties of parent and child -  and add/remove items in lists but you cannot change the object intance. The undo mechanism just will not be able to handle this properly.

DeepCore replied on Monday, December 13, 2010

Takk JonnyBee Wink

With your suggestion the undo works as I want it to work. But I've got another problem…

The foreign key from the root to the child object in the DB doesn't get updated afterwards I assign new values to the child's properties.

 

Philippe

JonnyBee replied on Monday, December 13, 2010

Hi,

Is that after Save is called or when you assign values to the child's properties?

Can you show some code sample?

 

DeepCore replied on Monday, December 13, 2010

Hi

this is what I'm trying to do:

        static void Main(string[] args)
        {
            var userRoles = UserRoles.NewList();

            var roleOne = UserRole.NewRole();
            roleOne.Name = "RoleOne";
            roleOne.IsActive = false;

            var roleTwo = UserRole.NewRole();
            roleTwo.Name = "RoleTwo";
            roleTwo.IsActive = true;

            userRoles.Add(roleOne);
            userRoles.Add(roleTwo);
            userRoles.Save();

            //

            var testUser = User.NewUser();
            testUser.FirstName = "User";
            testUser.LastName = "Test";
            testUser.UserID = "testUser";
            testUser.Role = UserRole.GetByID(1);

            testUser = testUser.Save();

 

            // change role assigned to user
            var testUser2 = User.GetByID(1);
            testUser2.Role = UserRole.GetByID(2);
            testUser2 = testUser2.Save();
        }

After 'testUser2.Save()' my user's record in the DB has the correct foreign key. The FK points now to role# 2.

Now let's imagine that there is some UI to modify the assignement of the user role. And this is where I can't have the undo function.

var testUser2 = User.GetByID(1);

testUser2.BeginEdit();

testUser2.Role = UserRole.GetByID(2);  // simulate an assignement from the UI

testUser2.CancelEdit();

This doesn't work.

 

I suspect that I'm better off to fetch the record being edited again when a user wants to undo his modifications, don't I ?

 

Philippe

DeepCore replied on Monday, December 13, 2010

    [Serializable]
    public class User : NHibernateBusinessBase<User>
    {
        #region Business Methods

        private int _iD;

        private static readonly PropertyInfo<int> IDProperty = RegisterProperty<int>(p => p.ID);

        public virtual int ID
        {
            get { return GetProperty(IDProperty, _iD); }
            set { SetProperty(IDProperty, ref _iD, value); }
        }

        private string _userID;

        private static readonly PropertyInfo<string> UserIDProperty = RegisterProperty<string>(p => p.UserID, "User ID");

        public virtual string UserID
        {
            get { return GetProperty(UserIDProperty, _userID); }
            set { SetProperty(UserIDProperty, ref _userID, value); }
        }

        private string _firstName;

        private static readonly PropertyInfo<string> FirstNameProperty = RegisterProperty<string>(p => p.FirstName, "First name");

        public virtual string FirstName
        {
            get { return GetProperty(FirstNameProperty, _firstName); }
            set { SetProperty(FirstNameProperty, ref _firstName, value); }
        }

        private string _lastName;

        private static readonly PropertyInfo<string> LastNameProperty = RegisterProperty<string>(p => p.LastName, "Last name");

        public virtual string LastName
        {
            get { return GetProperty(LastNameProperty, _lastName); }
            set { SetProperty(LastNameProperty, ref _lastName, value); }
        }

        private UserRole _role;

        private static readonly PropertyInfo<UserRole> UserRoleProperty = RegisterProperty<UserRole>(p => p.Role,
                                                                                                     "User role",
                                                                                                     UserRole.NewRole(),
                                                                                                     RelationshipTypes.
                                                                                                         Child);

        public virtual UserRole Role
        {
            get { return GetProperty(UserRoleProperty, _role); }
            set { SetProperty(UserRoleProperty, ref _role, value); }
        }

        #endregion

        #region Business Rules

        protected override void AddBusinessRules()
        {
            ValidationRules.AddRule(CommonRules.StringRequired, new RuleArgs(UserIDProperty));
            ValidationRules.AddRule(CommonRules.StringRequired, new RuleArgs(FirstNameProperty));
            ValidationRules.AddRule(CommonRules.StringRequired, new RuleArgs(LastNameProperty));
        }
        #endregion


        #region Authorization Rules

        #endregion


        #region Factory Methods

        protected User()
        {

        }

        public static User GetByID(int id)
        {
            return DataPortal.Fetch<User>(new SingleCriteria<User, int>(id));
        }

        public static User NewUser()
        {
            return DataPortal.Create<User>();
        }
        #endregion


        #region Data Access

        #endregion


        #region Overrides

        public override string ToString()
        {
            return string.Format("{0} {1}", FirstName, LastName);
        }


        protected override object GetUniqueIdentifier(object businessCriteria)
        {
            return ((SingleCriteria<User, int>)businessCriteria).Value;
        }
        #endregion

    }

DeepCore replied on Monday, December 13, 2010

    [Serializable]
    public class UserRole : NHibernateBusinessBase<UserRole>
    {
        #region Business Methods

        private int _iD;

        private static readonly PropertyInfo<int> IDProperty = RegisterProperty<int>(p => p.ID);

        public virtual int ID
        {
            get { return GetProperty(IDProperty, _iD); }
            set { SetProperty(IDProperty, ref _iD, value); }
        }

        private string _name;

        private static readonly PropertyInfo<string> NameProperty = RegisterProperty<string>(p => p.Name, "Role name", String.Empty);

        public virtual string Name
        {
            get { return GetProperty(NameProperty, _name); }
            set { SetProperty(NameProperty, ref _name, value); }
        }

        private bool _isActive;

        private static readonly PropertyInfo<bool> IsActiveProperty = RegisterProperty<bool>(p => p.IsActive, "State", false);

        public virtual bool IsActive
        {
            get { return GetProperty(IsActiveProperty, _isActive); }
            set { SetProperty(IsActiveProperty, ref _isActive, value); }
        }

        #endregion


        #region Business Rules

        protected override void AddBusinessRules()
        {
            ValidationRules.AddRule(CommonRules.StringRequired, NameProperty, 0);
        }
        #endregion


        #region Authorization Rules

        #endregion


        #region Factory Methods


        public static UserRole GetByID(int temp)
        {
            return DataPortal.Fetch<UserRole>(new SingleCriteria<UserRole, int>(temp));
        }

        public static UserRole NewRole()
        {
            return DataPortal.CreateChild<UserRole>();
        }

        protected UserRole()
        {
        }
        #endregion


        #region Data Access

        #endregion


        #region Overrides

        public override string ToString()
        {
            return Name;
        }

        protected internal override void Init()
        {
            base.Init();

            MarkAsChild();
        }

        protected override object GetUniqueIdentifier(object businessCriteria)
        {
            return ((SingleCriteria<UserRole, int>)businessCriteria).Value;
        }
        #endregion

    }

Copyright (c) Marimer LLC