Parent Child objects

Parent Child objects

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


Brendt posted on Thursday, July 04, 2013

Good day.

i would like to know if what i am doing is the correct way to do this. i have a parent child class: Customer(parent) and Address(children of parent) i would like users to be able to create as many addresses for the customer as needed. i have my parent classes all set up and my address classes. In My customer class i have the following:

 public static PropertyInfo<AddressList> AddressListProperty = RegisterProperty(new PropertyInfo<AddressList>("AddressList", "Address items"));
        public AddressList AddressList
        {
            get
            {
                if (!FieldManager.FieldExists(AddressListProperty))
                    LoadProperty(AddressListProperty, AddressList.NewList());
                return GetProperty(AddressListProperty);
            }
        }

 

i have my address class "Address : BusinessBase<Address>" and my AddressList class ''AddressList : BusinessBindingListBase<AddressList, Address>"

from my UI 'Customer form' i had new address like the following: 

var add = cust.AddressList.AddNew();

and pass the 'add' object to the address form to work with it. once i have finished adding all the fields i do a

add.applyedit()  <--is that the correct way to do it?

and then i load the list into a list view by doing a foreach address in this.cust.AddressList.

i also have this function in my AddressList to remove an address if the user chooses to do so:

public void Remove(Guid id)
        {
            foreach (Address item in this)
            {
                if (item.Id == id)
                {
                    Remove(item);
                    break;
                }
            }
        }

i see that, that removes the item from my addresslist and adds it to the DeletedList, so is that the correct way to remove child objects from the list?

another thing, how would i update the child addresses? in my Customer class i have the following

  public static Customer Update(Customer obj)
        {
            return DataPortal.Update<Customer>(obj);
        }

which calls a method in my customerfactory class, where and how would i call to update my addresslist of children. also how would i go go about doing an ApplyEdit, CancelEdit, on all the children objects?

sorry for all the questions, just trying to wrap my head around it all and do it the correct way the first time.

 

thank you in advance.

JonnyBee replied on Thursday, July 04, 2013

Hi Brendt, 

I highly recommend the Using CSLA 4 ebooks from Rocky. They provide a good knowledge of how to use CSLA 

I'd recommend the 3 first books 

They are available for purchase in the CSLA Store and at 79.99$ for the entire series (or 45$ for the first three books) it is a real bargain for knowledge. 

FWIW: I have purchased the entire series myself.

Brendt replied on Friday, July 05, 2013

Hi JonneyBee.

 

Thanks for your reply. i will have to make a plan to buy the ebooks, just wont be able to manage that right now.

my code seems to be working 100%, but i am not sure on how to go about saving my children objects from the parent.

i have tried placing a obj.AddressList.Save() method in my customerfactory but i get an error saying "Cannot save child objects directly" i have ready that when calling the save method on the root/parent object it should call save on all its children, but it does not seem to be doing this. where do i need to make changes to get this to work? i have an update_child in my address class:

 private void Child_Update()
        {
        // update child data here
            MessageBox.Show("Calling child update");
        }

and i have the following in my AddressList class:

        protected override void DataPortal_Update()
        {
            DataPortal.UpdateChild(this);
        }

        protected virtual void Child_Update()
        {
            var oldRLCE = this.RaiseListChangedEvents;
            this.RaiseListChangedEvents = false;
            try
            {
                foreach (var child in DeletedList)
                    DataPortal.UpdateChild(child);
                DeletedList.Clear();

                foreach (var child in this)
                    if (child.IsDirty) DataPortal.UpdateChild(child);
            }
            finally
            {
                this.RaiseListChangedEvents = oldRLCE;
            }
        }

        protected override void DataPortal_Update()
        {
            MessageBox.Show("calling dataportal update from addresslist");
        }

i have tried to follow all the samples that i downloaded when i downloaded CSLA, but they seem to be incomplete, it does not clearly show how to perform the save on children.

please could you help me, and point me in the right direction.

cfarren replied on Monday, July 08, 2013

You need to go into the AddressList's parent object and call DataPortal.UpdateChild(AddressList).

You can also remove all the DataPortal_Update and Child_Update methods in the AddressList class and overload the Child_Update, Child_Insert and Child_Delete methods in the child object of the AddressList. Well, only add the ones you need anyway.

When you call DataPortal.UpdateChild(AddressList) in the AddressList's parent class it should flow down to each individual list item in the AddressList.

Brendt replied on Tuesday, July 09, 2013

Good day cfarren.

thank you so much for your reply. i'm so glad that i actually am on the right track, i had changed the code to what you have stated above just before you replied and its all working great now. however the next challenge i am facing is to fetch the children back to its parent. im not sure what i am missing tho because i can see the data being fetched from the database and added to the addresslist but i cannot see it from my parent object. Currently this is the flow of fetching the children:

Parent Factory - > Fetch Child (Csla.DataPortal.FetchChild<AddressList>(obj.Id);) -> AddressList (DataPortal.FetchChild<Address>(id);) ->Address (  DataPortal.Fetch<Address>(id);) -> AddressFactory (does all the database calls)

so basically it flows (ParentFactory -> AddressList -> Address -> AddressFactory)

CustomerFactory (Parent):

Csla.DataPortal.FetchChild<AddressList>(obj.Id);

AddressList

 public void Child_Fetch(Guid id)
        {

            RaiseListChangedEvents = false;

            //return DataPortal.Fetch<Address>(id);

            var add = DataPortal.FetchChild<Address>(id);
            this.Add(add);

            RaiseListChangedEvents = true;

            //return DataPortal.FetchChild<AddressList>(id);
        }

Address

  private void Child_Fetch(Guid id)
        {
            // load child data here
            DataPortal.Fetch<Address>(id);
        }

i see that when fetching or creatign new child objects its comes out to this code in the parent object, which i assume is where it is loading it to the parent

public static PropertyInfo<AddressList> AddressListProperty = RegisterProperty(new PropertyInfo<AddressList>("AddressList", "Address items"));
        public AddressList AddressList
        {
            get
            {
                if (!FieldManager.FieldExists(AddressListProperty))
                    LoadProperty(AddressListProperty, AddressList.NewList());
                return GetProperty(AddressListProperty);
            }
           
        }

am i missing something here? i see that 'FieldManager.FieldExists' does have fields but i think i may be missing something here.

in my form this is how i load the addresslist to a listview (it works when creating new child objects, but is empty when fetching existing child objects.

 

private void LoadList()
        {
            this.lvAdd.Items.Clear();

            foreach (Address item in this.cust.AddressList)
            {
                //if (item.IsDeleted != true)
                //{
                ListViewItem newList = new ListViewItem(this.cust.AddressList.IndexOf(item).ToString());
                newList.SubItems.Add(item.L1);
                newList.SubItems.Add("IsDeleted = " + item.IsDeleted);

                this.lvAdd.Items.Add(newList);
                //}
            }
        }

when i put a breaker on the foreach i see that 'cust.AddressList' count is 0 (only when fetching exisitng children.)

please could you explain what i am doing wrong, or what i am missing here.

 

cfarren replied on Tuesday, July 09, 2013

In your Address class you shouldn't be calling DataPortal.Fetch. This is where you need to put your data (either from the database or hard coding) into the Address class. I'm not sure what the Address Factory class is all about but if it is in fact a child object of Address than I feel you have an extra layer of complexity that you don't need.

Address

private void Child_Fetch(Guid id)

(

   LoadProperty(GuidProperty,id)

   LoadProperty(StreetNumberProperty,2)

   LoadProperty(StreetProperty,"Test Street")

   LoadProperty(SuburbProperty, "Some Suburb")

}

etc etc.

Brendt replied on Wednesday, July 10, 2013

thank you for your reply.

i'm still abit stumped i see that in the address class i need what you posted above. but i still need to get the data into that class some how.

i have a list of addresses (users can add as many as desired) and i can save all the addresses to the database. so i have my AddressList class that is holding a collection of Address in it. so i assume that i need to return a list back to AddressList class and then add a new address child object.

My Address Factory is where i have all my Data Access code so that i keep by business objects and Data Access separate. i have the following classes:

Address (BO)

AddressList (BO)

AddressFactory (In DAL)

so i assume that in my  AddressList i would make a call to the AddressFactory(to fetch all matching addresses - public void Child_Fetch(Guid id)) which would call 'public Address Fetch(Guid id)' in AddressFactory. once it has returned with its data i would add each one to the collection and create child(address) objects from it.

So how should the flow of Data go? at Present this is how i have it.

1. CustomerFactory after loading Customer Data i call : obj.AddressList.Child_Fetch(obj.Id)

2. In AddressList the above calls : public void Child_Fetch(Guid id)

3. The above goes to my AddressFactory and calls: public Address Fetch(Guid id)

Once the data is fetched it should then create the address children. and add it to the addresslist collection. this is where i'm stumped, i don't know how to get the flow of data back to create my address child objects

Brendt replied on Wednesday, July 10, 2013

ok, i think i have it working. i have done the following:

public static PropertyInfo<AddressList> AddressListProperty = RegisterProperty(new PropertyInfo<AddressList>("AddressList", "Address items"));
        public AddressList AddressList
        {
            get
            {
                if (!FieldManager.FieldExists(AddressListProperty))
                    if (base.IsNew)  <----Added this line
                    {
                        LoadProperty(AddressListProperty, AddressList.NewList());
                    }
                    else
                    {
                        LoadProperty<AddressList>(AddressListProperty, AddressList.GetList(this.Id));    <-----Added this line
                    }

                    //LoadProperty(AddressListProperty, AddressList.NewList());
                //LoadProperty<AddressList>(AddressListProperty, AddressList.GetList(this.Id));
                return GetProperty(AddressListProperty);
            }
           
        }

is that the correct way to fetch children objects, it does seem to fetch them correctly.

Brendt replied on Wednesday, July 10, 2013

ok, well my previous post seems to work, but my child objects are not being mark as old, even tho when i fetch them from my AddressFactory i call MarkOld(obj) and MarkAsChild(obj) what am i doing wrong here?

cfarren replied on Wednesday, July 10, 2013

Call base.Child_Fetch() at the end of your Fetch method

Brendt replied on Wednesday, July 10, 2013

ok, so i am now totally lost and a bit frustrated.

i am going to post my complete class code, im not sure what im doing wrong here, i think it might be in my Fetch method in AddressFactory.

This is my Parent Class (Customer):

using System;
using Csla;
using Csla.Rules.CommonRules;
using Csla.Rules;


namespace BusinessLibrary
{
    [Serializable]
    [Csla.Server.ObjectFactory("DataAccess.CustomerFactory, DataAccess")]
    public class Customer : BusinessBase<Customer>
    {

        public static PropertyInfo<Guid> IdProperty = RegisterProperty<Guid>(p => p.Id);
        public Guid Id
        {
            get { return GetProperty(IdProperty); }
            set { SetProperty(IdProperty, value); }
        }

        public static PropertyInfo<string> NameProperty = RegisterProperty<string>(c => c.Name);
        public string Name
        {
            get { return GetProperty(NameProperty); }
            set { SetProperty(NameProperty, value); }
        }

        public static PropertyInfo<string> LastNameProperty = RegisterProperty<string>(c => c.LastName);
        public string LastName
        {
            get { return GetProperty(LastNameProperty); }
            set { SetProperty(LastNameProperty, value); }
           
        }

        public static PropertyInfo<string> AccNumProperty = RegisterProperty<string>(c => c.AccNum);
        public string AccNum
        {
            get { return GetProperty(AccNumProperty); }
            set { SetProperty(AccNumProperty, value); }
           
        }

        public static PropertyInfo<int> ConsultantIDProperty = RegisterProperty<int>(c => c.ConsultantID);
        public int ConsultantID
        {
            get { return GetProperty(ConsultantIDProperty); }
            set { SetProperty(ConsultantIDProperty, value); }

        }

        public static PropertyInfo<string> ConsultantNameProperty = RegisterProperty<string>(c => c.ConsultantName);
        public string ConsultantName
        {
            get { return GetProperty(ConsultantNameProperty); }
            set { SetProperty(ConsultantNameProperty, value); }

        }

        public static PropertyInfo<AddressList> AddressListProperty = RegisterProperty(new PropertyInfo<AddressList>("AddressList", "Address items"));
        public AddressList AddressList
        {
            get
            {
                if (!FieldManager.FieldExists(AddressListProperty))
                    if (base.IsNew)
                    {
                        LoadProperty(AddressListProperty, AddressList.NewList());
                    }
                    else
                    {
                        LoadProperty<AddressList>(AddressListProperty, AddressList.GetList(this.Id));
                    }

                return GetProperty(AddressListProperty);
            }
           
        }

    
        public static Customer NewCustomer()
        {
            return DataPortal.Create<Customer>();
           
        }

        public static Customer GetCustomer(Guid id)
        {
           return DataPortal.Fetch<Customer>(id);
           //LoadProperty<AddressList>(AddressListProperty, AddressList.GetList(Id));
          
        }

        public static Customer Update(Customer obj)
        {
            return DataPortal.Update<Customer>(obj);
        }
       

        
        #region business rules
        protected override void AddBusinessRules()
        {
            base.AddBusinessRules();
            BusinessRules.AddRule(new Required(NameProperty) { MessageText = "First Name Required" });
            BusinessRules.AddRule(new Required(AccNumProperty) { MessageText = "Acc No. Required" });
        }

        #endregion //business rules




        public BrokenRulesCollection GetAllBrokenRules()
        {
            BrokenRulesCollection list = new BrokenRulesCollection();

            if (FieldManager.HasFields)
            {

                // add broken rules at the root object level

                list.AddRange(this.BrokenRulesCollection);
            }
            return list;
           
        }

       
    }
}

This is my Address Class:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using Csla;
using Csla.Rules.CommonRules;
using Csla.Serialization;
using System.Windows.Forms;
using Csla.Rules;
using Csla.Core;


namespace BusinessLibrary
{
    [Serializable]
    [Csla.Server.ObjectFactory("DataAccess.AddressFactory, DataAccess")]
    public class Address : BusinessBase<Address>
    {
        public static PropertyInfo<Guid> IdProperty = RegisterProperty<Guid>(p => p.Id);
        public Guid Id
        {
            get { return GetProperty(IdProperty); }
            private set { SetProperty(IdProperty, value); }
        }

        public static PropertyInfo<Guid> ParentIdProperty = RegisterProperty<Guid>(p => p.ParentId);
        public Guid ParentId
        {
            get { return GetProperty(ParentIdProperty); }
            private set { SetProperty(ParentIdProperty, value); }
        }

        public static PropertyInfo<string> AddresseeProperty = RegisterProperty<string>(c => c.Addressee);
        public string Addressee
        {
            get { return GetProperty(AddresseeProperty); }
            set { SetProperty(AddresseeProperty, value); }
        }

        public static PropertyInfo<string> L1Property = RegisterProperty<string>(c => c.L1);
        public string L1
        {
            get { return GetProperty(L1Property); }
            set { SetProperty(L1Property, value); }
        }

        public static PropertyInfo<string> L2Property = RegisterProperty<string>(c => c.L2);
        public string L2
        {
            get { return GetProperty(L2Property); }
            set { SetProperty(L2Property, value); }
        }

        public static PropertyInfo<string> L3Property = RegisterProperty<string>(c => c.L3);
        public string L3
        {
            get { return GetProperty(L3Property); }
            set { SetProperty(L3Property, value); }
        }

        public static PropertyInfo<string> L4Property = RegisterProperty<string>(c => c.L4);
        public string L4
        {
            get { return GetProperty(L4Property); }
            set { SetProperty(L4Property, value); }
        }

        public static PropertyInfo<string> TownProperty = RegisterProperty<string>(c => c.Town);
        public string Town
        {
            get { return GetProperty(TownProperty); }
            set { SetProperty(TownProperty, value); }
        }

        public static PropertyInfo<string> ProvinceProperty = RegisterProperty<string>(c => c.Province);
        public string Province
        {
            get { return GetProperty(ProvinceProperty); }
            set { SetProperty(ProvinceProperty, value); }
        }

        public static PropertyInfo<int> CountryIDProperty = RegisterProperty<int>(c => c.CountryID);
        public int CountryID
        {
            get { return GetProperty(CountryIDProperty); }
            set { SetProperty(CountryIDProperty, value); }
        }

        public static PropertyInfo<string> CountryNameProperty = RegisterProperty<string>(c => c.CountryName);
        public string CountryName
        {
            get { return GetProperty(CountryNameProperty); }
            set { SetProperty(CountryNameProperty, value); }
        }

      


        //private Address()
        //{ /* require use of factory methods */ }

        internal static Address NewAddress()
        {
            return DataPortal.Create<Address>();
        }

        private void Child_Insert(Customer obj)
        {
            // insert child data here
            //MessageBox.Show("Calling child insert");
            this.ParentId = obj.Id;
            //MessageBox.Show("Parent ID = " + obj.Id);
            DataPortal.Update<Address>(this);
           
        }


        private void Child_Create(Customer obj)
        {
            // update child data here
            MessageBox.Show("Calling child create");
            //foreach (Address Item in obj.AddressList)
            //{
            //    LoadProperty(L1Property, Item.L1);
            //}
        }

        private void Child_Update()
        {
            // update child data here
            MessageBox.Show("Calling child update");
        }

        private void Child_DeleteSelf()
        {
            // delete child data here
            MessageBox.Show("Calling child delete self");
        }

        private void Child_Fetch(Guid id)
        {
            //AddressList.GetList(id);


        }


        public Address()
        {
            MarkAsChild();
        }

  
    }
}

This is my AddressList Class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Csla;
using Csla.Serialization;
using System.Windows.Forms;

namespace BusinessLibrary
{
   [Serializable]
   [Csla.Server.ObjectFactory("DataAccess.AddressFactory, DataAccess", "Fetch")]
    public class AddressList : BusinessBindingListBase<AddressList, Address>
    {
        private AddressList()
        {
            AllowNew = true;
        }

        protected override object AddNewCore()
        {
            var Addrs = Address.NewAddress();
            Add(Addrs);
            return Addrs;
        }

        internal static AddressList NewList()
        {
            return DataPortal.CreateChild<AddressList>();
        }

        public void Remove(Guid id)
        {
            foreach (Address item in this)
            {
                if (item.Id == id)
                {
                    Remove(item);
                    break;
                }
            }
        }

        public int CountDeletedItems()
        {
            int iCount = 0;
            for (int i = 0; i < this.DeletedList.Count; i++)
            {
                iCount++;
            }
            return iCount;
        }

        protected override void DataPortal_Update()
        {
            MessageBox.Show("calling dataportal update from addresslist");
           
        }

        //protected void DataPortal_Fetch()
        //{
        //    MessageBox.Show("Calling FETCH from Address List");
        //}
        //private void DataPortal_Fetch(Guid id)
        //{
        //    MessageBox.Show("Calling FETCH from Address List");
        //    //DataPortal.Fetch<Address>(id);
        //}

        //public AddressList GetList()
        //{
        //    return this;
        //}

       public static AddressList GetList(Guid id)
       {
           return DataPortal.Fetch<AddressList>(id);
       }

        public void Child_Fetch(Guid id)
        {

            MessageBox.Show("Calling FETCH from Address List STEP1");

           

            RaiseListChangedEvents = false;


            RaiseListChangedEvents = true;

            //return DataPortal.FetchChild<AddressList>(id);
        }

      
    }
}

 

This is my AddressFactory class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Csla;
using Csla.Reflection;
using Csla.Server;
using BusinessLibrary;
using System.Data.SqlClient;
using TestApp.DataAccessLayer;
using System.Data;
using Csla.Data;
using System.Windows.Forms;
using Csla.Rules;

namespace DataAccess
{
    public class AddressFactory : ObjectFactory
    {
        public static List<Address> address;


        [RunLocal]
        public Address Create()
        {
            var obj = (Address)MethodCaller.CreateInstance(typeof(Address));

            //CheckRules(obj);
            MarkNew(obj);
            MarkAsChild(obj);
            return obj;
        }


        public Address Update(Address obj)
        {
            MessageBox.Show("Calling update address in Factory");

            if (obj.IsDeleted)
            {
                if (!obj.IsNew)
                {
                    //ExecuteDelete(obj.Id);
                }
                MarkNew(obj);
            }
            else
            {
                if (obj.IsNew)
                {
                    //Insert New data
                    ExecuteInsert(obj);
                }
                else
                {
                    //Update existing data
                    //ExecuteUpdate(obj);
                }
            }
            return obj;
        }

        private void ExecuteInsert(Address obj)
        {
            ConnSingleton cn = ConnSingleton.getDbInstance();
            cn.GetDBConnection();

            using (SqlCommand cm = cn.CreateCommand())
            {
                cm.CommandType = CommandType.Text;
                cm.CommandText = "INSERT INTO tAdd (ADD_L1, ADD_L2, ADD_L3, ADD_L4, ADD_GUID,ADD_TP_GUID) VALUES (@ADD_L1, @ADD_L2, @ADD_L3, @ADD_L4, @ADD_GUID,@ADD_TP_GUID) ;";

                AddInsertParameters(cm, obj);

                cm.ExecuteNonQuery();

                cn.CloseConn();
            }
        }

        protected void AddInsertParameters(SqlCommand cm, Address obj)
        {
           
            cm.Parameters.AddWithValue("@ADD_L1", obj.L1);
            cm.Parameters.AddWithValue("@ADD_L2", obj.L2);
            cm.Parameters.AddWithValue("@ADD_L3", obj.L3);
            cm.Parameters.AddWithValue("@ADD_L4", obj.L4);
            cm.Parameters.AddWithValue("@ADD_GUID", Guid.NewGuid());
            cm.Parameters.AddWithValue("ADD_TP_GUID", obj.ParentId);
       
        }
        public AddressList Fetch(Guid id)
        {

            try
            {

                var obj = (AddressList)MethodCaller.CreateInstance(typeof(AddressList));
                address = new List<Address>();

                ConnSingleton cn = ConnSingleton.getDbInstance();
                cn.GetDBConnection();

               obj.RaiseListChangedEvents = false;

               using (SqlCommand cm = cn.CreateCommand())
               {

                   cm.CommandType = CommandType.Text;
                   cm.CommandText = "SELECT * FROM tADD where ADD_TP_GUID =" + "'" + id + "'";
                  

               

                       using (SafeDataReader dr = new SafeDataReader(cm.ExecuteReader()))
                       {
                           while (dr.Read())
                           {
                            
                               //customers.Add(new CustomerInfo { Id = Guid.Parse(dr["TP_ClientID"].ToString()), AccNo = dr["TP_AcNo"].ToString(), CustomerName = dr["TP_Name"].ToString(), LastName = dr["TP_LastName"].ToString() });
                               address.Add(new Address { L1 = dr["ADD_L1"].ToString(), L2 = dr["ADD_L2"].ToString(), L3 = dr["ADD_L3"].ToString(), L4 = dr["ADD_L4"].ToString() });

                           }
                           cn.CloseConn();
                       }
                        obj.AddRange(address);
                        MarkOld(obj);
                        MarkAsChild(obj);


                   }
                   obj.RaiseListChangedEvents = true;
              
                return obj;
               


            }
            catch (Exception ex)
            {
                MessageBox.Show("Error: " + ex.Message);
                return null;
                // throw ex;
            }

        }
     

    }
}

i am now really lost and im not sure how to actaully proceed with this. any help would be much appreciated.

JonnyBee replied on Wednesday, July 10, 2013

Hi Brendt,

This is quite a mess, right!

The majority if your challenge is to understand Data Access and the difference between encapsulated invocation and factory invocation

You are trying to use a mix that is not possible.

You are targeting factory invocation and some easy rule of thumb is: 

  1. You only use ObjectFactory on "root" objects (or objects that use the "root" dataportal for lazy loading -ie Create/Fetch)
  2. Do NOT use Child_XYZ and DataPortal.XYZChild methods. These are only valid for encapsulated invocation.
    This also means that you cannot use FieldManager.UpdateChildren or DataPortal.UpdateChild.

So to your code:

  1. Use ObjectFactory attribute on Customer and AddressList only
  2. In your AddressListFactory only Create and Fetch methods wiull be called by the DataPortal (as you use Lazy Loading for that class)
  3. In all other aspects you must view AddressList / Address as a child object
  4. I do recommend that your DAL has a AddressFactory class to create the Address object but no ObjectFactory attribute on Address class. AddrressListFactory must know by itself to create and call the AddressFactory to get each address object in the DAL. 
  5. In CustomerFactory.Update method your code must know to call AddressListFactory to do the updates of both DeletedList and actual list content. 
  6. In AddressList.Get and AddressList.Fetch you should call the DataPortal.Create or DataPortal.Fetch method so that execution may go across the wire to the server 

THIS IS ALL WELL DOCUMENTED IN USING CSLA 4 - DATA ACCESS EBOOK THAT I HIGHLY RECOMMEND:  

Brendt replied on Friday, July 12, 2013

thanks for your reply JonnyBee.

i know i really should get those ebooks. yes i want to target Factory Invocation as so to keep my Business object and Data Access separate.

so let me see if i have this straight in my head.

  1. AddressList is a child of my Customer class and Address is a child of my AddressList Class?
  2. my Address class should not call my addressFactory class to create itself at all, the AddressList should create address children? (because i see when i call new address from my addresslist it calls address to create a new address, but my address class in turn goes to AddressFactory to create and return the new address) is that incorrect?
  3. so when calling .Save() on the parent (Customer Class) it will know to call save on all its children? so does it then call Save() from Parent, parent calls Save() on AddressList and it in turn will call Save() on each address child in AddressList? (so there is no need to call FieldManager.UpdateChildren or DataPortal.UpdateChild.)
  4. So does my Address class have absolutely no interaction with the addressfactory? 
  5. So in my Address class should i remove all (Child_Update, Child_Create, Child_Fetch, etc) methods? if so how would my address child objects update themselves?
  6. And in my AddressList i should have  "DataPortal_Update" and " DataPortal_Fetch"

 

JonnyBee replied on Friday, July 12, 2013

Hi,

  1. yes
  2. AddressList class is designed for LazyLoad and will use an ObjectFactory for Create/Fetch
  3. Customer.Save will call into the CustomerFactory.Update method. All logic to save children must be handled from within this method. 
  4. Yes - tho you will typically have an AddressFactory that will create/load values/read values from the Address object. 
  5. No Child_XYZ methods.
  6. No DataPortal_XYZ methods.

When you call Customer.Save (using ObjectFactry) will call into CustomerFactory.Update method. Code execution is now transferred to the factory class that must:

So - when you enter the ObjectFactory all logic for DataAccess is with the ObjectFactoryAssembly and the business object is primarily containers of data with BusinessRules.  

 

Brendt replied on Tuesday, July 16, 2013

Hi.

OK so is my CustomerFactory supposed to talk directly to my AddressFactory? so as i have it now this is the flow of data.

Customer.Save() calls into CustomerFactory.Update() at the end of all my update/insert/delete on my customer object i have create an instance of my addressFactory and called .Update like the following.

AddressFactory af = new AddressFactory();

             foreach (var item in obj.AddressList)
             {
                 af.Update(item);
             }

which then goes into my AddressFactory and processes update/insert/delete as follows

public Address Update(Address obj)
        {
          
            if (obj.IsDeleted)
            {
                if (!obj.IsNew)
                {
                    MessageBox.Show("Execute Delete");
                }
                MarkNew(obj);
            }
            else
            {
                if (obj.IsNew)
                {
                    //Insert New data
                    MessageBox.Show("Execute Insert");
                }
                else
                {
                    //Update existing data
                    MessageBox.Show("Execute Update");
                }
            }

        
            return obj;
        }

 

is this now the correct way of processing children objects?

JonnyBee replied on Tuesday, July 16, 2013

Yes, '

with the addition that your AddressListFactory.Update must first process the DeletedList before processing the actual list items. 

Brendt replied on Tuesday, July 16, 2013

ok, i have now added a method that accepts my list as follows:

 public void UpdateChildren(AddressList obj)
        {
            foreach (Address item in obj.DeletedList) <---Error
            {
                this.Update(item);
            }

            foreach (Address item in obj)
            {
                this.Update(item);
            }
        }

but now i get an error trying to access DeletedList

  1. 'Csla.BusinessListBase<BusinessLibrary.AddressList,BusinessLibrary.Address>.DeletedList' is inaccessible due to its protection

JonnyBee replied on Tuesday, July 16, 2013

To get the inner protected properties in a business object look at the methods implemented in base class ObjectFactory:

    protected Csla.Core.MobileList<C> GetDeletedList<C>(object obj)

Brendt replied on Tuesday, July 16, 2013

i have tried a couple of things but i cannot loop through the deleted list. i have tried

foreach (Address item in GetDeletedList<AddressList>(obj))
 {
     this.Update(item);
 }

Error: Cannot convert type 'BusinessLibrary.AddressList' to 'BusinessLibrary.Address'

foreach (var item in GetDeletedList<AddressList>(obj))
{
   this.Update(item);
 }

So how do i actually process the deleted list?

JonnyBee replied on Tuesday, July 16, 2013

foreach (var address in GetDeletedList<Address>(obj))

Brendt replied on Wednesday, July 17, 2013

ok, so i am now able to save my child to the DB, but i need to be able to set the parent ID to my children, where and how do i go about doing that? should i pass a reference of my parent to my AddressFactory.Update and then grab it from there or should i just pass the ID itself?

these would be new ID's that get created when the parent gets saved for the first time, so i save my parent first and then return the ID.

Brendt replied on Wednesday, July 17, 2013

alright. i have managed to pass the customer ID and Set the children.

my next step is to fetch the Customer and its children by the customer ID, what route do i take to fetch all of its children? does it go from

CustomerFactory ->AddressList ->AddressFactory?

also will fetched children be marked as IsNew = False and Ischild = True?

Brendt replied on Thursday, July 18, 2013

good day.

i am now still completely lost in how i am supposed to fetch my children objects from the database.

i have seen the following but am not sure how or where i am supposed to use it.

  1. LoadProperty
  2. DataPortal.FetchChild

in my parent object i have the following code:

 public static PropertyInfo<AddressList> AddressListProperty = RegisterProperty(new PropertyInfo<AddressList>("AddressList", "Address items"));
        public AddressList AddressList
        {
            get
            {
                if (!FieldManager.FieldExists(AddressListProperty))
                        LoadProperty(AddressListProperty, AddressList.NewList());
                return GetProperty(AddressListProperty);
            }

but is that only for when create new child objects, am i supposed to be adding and calling something in there?

any help would be much appreciated, i am frustrating myself to no end at the moment.

Brendt replied on Wednesday, July 24, 2013

Good day.

i am really hoping that somebody can help me with this.

i am still struggling to fetch my child (address) objects. please could somebody help me with this problem.

These are the classes i have:

  1. Customer (parent)
  2. CustomerFactory (DAL)
  3. Address
  4. AddressList
  5. AddressFactory (DAL)

i am trying to fetch my child objects (address) for my Parent (customer). In my CustomerFactory at the end of my Fetch method i am calling this function

 AddressList al = AddressList.GetChildren(obj.Id); <-- is this right, is my addressList a child of my parent?

The above 'GetChildren' calls into my AddressList which does the following

public static AddressList GetChildren(Guid id)
        {
           
            return DataPortal.Fetch<AddressList>(id); <--should i be calling DataPortal.Fetch or DataPortal.FetchChild ( when i call fetch child i get error 'ChildDataPortal.Fetch failed on the server') when i call fetch it works and goes through to my AddressFactory 'Fetch' method
         
        }

In my AddressFactory it calls the 'Fetch' Method with the following:

public AddressList Fetch(Guid id)
        {
            try
            {

                var obj = (AddressList)MethodCaller.CreateInstance(typeof(AddressList));

                ConnSingleton cn = ConnSingleton.getDbInstance();
                cn.GetDBConnection();

                using (SqlCommand cm = cn.CreateCommand())
                {

                    cm.CommandType = CommandType.Text;
                    cm.CommandText = "SELECT * FROM tAdd where ADD_TP_GUID =" + "'" + id + "'";
                    //cm.Parameters.AddWithValue("@ACCOUNTNUM", custID);

                    //MessageBox.Show(cm.CommandText.ToString());

                    using (var reader = new SafeDataReader(cm.ExecuteReader()))
                    {
                        if (reader.Read())
                        {
                            do
                            {
                               
                                obj.Add(Address.GetAddress(reader)); <--this then calls into the Address class
                               
                            } while (reader.Read());
                        }
                    

                        return obj;
                    }
                   
                }

               


            }
            catch (Exception ex)
            {
                MessageBox.Show("Error In: AddressFactory " + ex.Message);
                return null;
                // throw ex;
            }

        }

 

In My Address class the 'obj.Add(Address.GetAddress(reader))' calls the follwoing method

 public static Address GetAddress(SafeDataReader reader)
        {
            return DataPortal.Fetch<Address>(reader);
        }

which calls the following to do the loading:

private void DataPortal_Fetch(SafeDataReader reader)
        {
           
            LoadProperty(_addL1Property, reader.GetString("ADD_L1"));
            //MessageBox.Show(reader.GetString("ADD_L1"));

            MarkAsChild();
        }

but i see that my property is not getting set, i put a break in place but its not getting called. why would it not be loading the property?

please could somebody be so kind as to help me.

 

Copyright (c) Marimer LLC