Silverlight: Correct method to create a child with default values from the db and a Criteria object.

Silverlight: Correct method to create a child with default values from the db and a Criteria object.

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


pgroenenstein posted on Wednesday, December 08, 2010

Hi

Currently creating a SL app in CSLA 3.8.4 and I need some help please.

What is the correct method to add a child to a "BusinessListBase " Type of object when that child needs certain "criteria" info from the UI and based upon that loads certain default values from the DB on creation.

I have a "AddNewItemFromMenu" Method on the list object which I call from the UI with certain info Like:

 

   Public Sub AddNewItemFromMenu(ByVal MenuID As Integer, ByVal MenuItemID As Integer, ByVal EntryTypeID As Enum_LineEntryTypes)

      Dim objCriteria As New OrderItem.Criteria

      ' Set item information
      With objCriteria
        .AddFromMenu = True
        .EntryTypeID = CType(EntryTypeID, Short)
        .MenuID = MenuID
        .MenuItemID = MenuItemID
      End With

      Dim vntItem As OrderItem = OrderItem.NewItem(objCriteria)

      ' Assigns item to the collection
      Add(vntItem)

 End Sub

 

My Child object Named "OrderItem"  "NewItem" function looks like this:

    Friend Shared Function NewItem(ByVal Criteria As Criteria) As OrderItem

      Return DataPortal.CreateChild(Of OrderItem)(Criteria)

    End Function

   Private Overloads Sub Child_Create(ByVal Criteria As Criteria)

       ' Do  DB stuff here

End sub

The above obviously work in a "Normal" windows app, but i'm in the dark as how to make it work in a SL app.

In a SL app it throws an exception "ChildDataPortal.Create failed on the server"

Here is the stack trace if it may help:

Csla.DataPortalException was unhandled by user code
  Message="ChildDataPortal.Create failed on the server"
  StackTrace:
       at Csla.DataPortalClient.ChildDataPortal`1.Create(Object[] parameters)
       at Csla.DataPortal.CreateChild[T](Object[] parameters)
       at SmtSelfOrder.Business.BusinessClasses.OrderItem.NewItem(Criteria Criteria)
       at SmtSelfOrder.Business.BusinessClasses.OrderItems.AddNewItemFromMenu(Int32 MenuID, Int32 MenuItemID, Enum_LineEntryTypes EntryTypeID)
       at SmtSelfOrder.MenuList.AddItemToOrder(Enum_LineEntryTypes LineEntryType, Int32 MenuItemID)
       at SmtSelfOrder.MenuList.btnMenuProd_Click(Object sender, EventArgs e)
       at System.Windows.Controls.Primitives.ButtonBase.OnClick()
       at System.Windows.Controls.Button.OnClick()
       at System.Windows.Controls.Primitives.ButtonBase.OnMouseLeftButtonUp(MouseButtonEventArgs e)
       at System.Windows.Controls.Control.OnMouseLeftButtonUp(Control ctrl, EventArgs e)
       at MS.Internal.JoltHelper.FireEvent(IntPtr unmanagedObj, IntPtr unmanagedObjArgs, Int32 argsTypeIndex, Int32 actualArgsTypeIndex, String eventName)
  InnerException:

What I'm lacking to find is a good sample on how to implement adding a child to a collection that MUST get certain info from the DB based upon some info in the criteria object.

I know for a fact I'm making a ridiculous stupid mistake here, but alas I just can't see it!

Thanks in advance for helping me out.

 

Kind Regards

Phillip

pgroenenstein replied on Wednesday, December 08, 2010

OK this is how far I got:

 

My  "AddNewItemFromMenu" Method on the "BusinessBaselist" object looks like this now:

 Public Sub AddNewItemFromMenu(ByVal MenuID As Integer, ByVal MenuItemID As Integer, ByVal EntryTypeID As Enum_LineEntryTypes)

      Dim objCriteria As New OrderItem.Criteria

      ' Get item information
      With objCriteria
        .AddFromMenu = True
        .EntryTypeID = CType(EntryTypeID, Short)
        .MenuID = MenuID
        .MenuItemID = MenuItemID
      End With

      OrderItem.NewItem(objCriteria, AddressOf Me.ItemFinCreated) 

End Sub

 

My"NewItem" Method on my child looks like this:

    Friend Shared Sub NewItem(ByVal Criteria As Criteria, ByVal handler As EventHandler(Of DataPortalResult(Of OrderItem)))

      Dim dp As DataPortal(Of OrderItem) = New DataPortal(Of OrderItem)()

      AddHandler dp.FetchCompleted, handler
      dp.BeginFetch(Criteria)

    End Sub

My Data Acces method looks like this on the Child:


    Private Overloads Sub Child_Create(ByVal Criteria As Criteria)

               ' Do  DB stuff here

 End Sub

 

Now I need to figure out how to work my "Callback" Method on the "BusinessListBase" object

Currently like this:

    Friend Sub ItemFinCreated(ByVal sender As Object, ByVal e As EventArgs)

        ' Figure out what to do here!!!!

    End Sub


Am I on the right track or not?

Thanks!

RockfordLhotka replied on Wednesday, December 08, 2010

I'm assuming the new child has to be initialized on the server with its new values.

In that case you have a couple options.

One is that you can implement an actual DataPortal_Create on the child and use the data portal to create the child - and at the bottom of the DataPortal_Create you'll need to call MarkAsChild, because that won't be automatic. You can't use Child_Create, because you actually need the data portal to go across the network.

Perhaps the more "pure" way to do this is with a command object. Build a command object that creates the child object with DataPortal.CreateChild - so your child class really has a Child_Create method that runs on the server. Then have your list class invoke the command to get the child.

In any case the call to get the child will be async, so you're list class will need to handle the callback and add the newly created child to the list when the result comes back from the server.

pgroenenstein replied on Wednesday, December 08, 2010

Hi Rocky Thanks for the reply.

Yes the child has to be initialize on the server. I took the DataPortal_create as shown in my 2nd post, But I have problems in my Callback in my list object.
It crashes on the "Add(CType(sender, OrderItem))" line. What is the correct syntax suppose to be like?

Here is what my call back looks like:

  Friend Sub ItemFinCreated(ByVal sender As Object, ByVal e As EventArgs)

      ' Assigns a item to the collection
      Add(CType(sender, OrderItem))

    End Sub

Obviously I must be doing something wrong here, but for the life of me I can't seem to see what.

This is the Error Stack it returns:

System.InvalidCastException was unhandled by user code
  Message="Unable to cast object of type 'Csla.DataPortal`1[SmtSelfOrder.Business.BusinessClasses.OrderItem]' to type 'SmtSelfOrder.Business.BusinessClasses.OrderItem'."
  StackTrace:
       at SmtSelfOrder.Business.BusinessClasses.OrderItems.ItemFinCreated(Object sender, EventArgs e)
       at Csla.DataPortal`1.OnFetchCompleted(DataPortalResult`1 e)
       at Csla.DataPortal`1.proxy_FetchCompleted(Object sender, DataPortalResult`1 e)
       at Csla.DataPortalClient.SynchronizedWcfProxy`1.OnCompleted(Object sender, DataPortalResult`1 e, EventHandler`1 method)
       at Csla.DataPortalClient.SynchronizedWcfProxy`1.OnFetchCompleted(Object sender, DataPortalResult`1 e)
       at Csla.DataPortalClient.SynchronizedWcfProxy`1.<.ctor>b__2(Object o, DataPortalResult`1 e)
       at Csla.DataPortalClient.WcfProxy`1.OnFetchCompleted(DataPortalResult`1 e)
       at Csla.DataPortalClient.WcfProxy`1.proxy_FetchCompleted(Object sender, FetchCompletedEventArgs e)
       at Csla.WcfPortal.WcfPortalClient.OnFetchCompleted(Object state)
  InnerException:

pgroenenstein replied on Wednesday, December 08, 2010

P.S. I did do the MarkAsChild thing to. I think everything is ok in the DB side of things. The problems seems to be where I assign the newly created to the list in the callback method of the list.

 

RockfordLhotka replied on Wednesday, December 08, 2010

You shouldn't use sender, you need e.Object, and only after you check e.Error to find out if any exception occurred during the asynchronous operation.

pgroenenstein replied on Thursday, December 09, 2010

Sorry i'm still stupid, but what should the Callback method in the list object declaration then look like?

 

pgroenenstein replied on Thursday, December 09, 2010

Am I not looking in the right places or what? But I cannot find ONE example of this in ANY of the sample projects.

 

pgroenenstein replied on Thursday, December 09, 2010

OK My declaration for my Callback Method looks like this now:

    Friend Sub ItemFinCreated(ByVal sender As Object, ByVal e As Csla.DataPortalResult(Of SmtSelfOrder.Business.BusinessClasses.OrderItem))

      ' Assign the Item to the collection
      If e.Error Is Nothing Then
        Add(CType(e.Object, OrderItem))
      Else
        ' Handle error here.
        Throw New System.Security.SecurityException(e.Error.Message)   ' Temp for now
      End If

    End Sub

 

The Callback works now but I get an error:

"DataPortal.Fetch failed (Invalid operation - fetch not allowed)"

I suspect that has to do with my "User Rules"...will look into that next.

 

RockfordLhotka replied on Thursday, December 09, 2010

I thought we were talking about create, not fetch?

If you call DataPortal.Create you need DataPortal_Create, if you call DataPortal.Fetch you need DataPortal_Fetch.

It sounds like you are calling DataPortal.Fetch, but have implemented DataPortal_Create.

pgroenenstein replied on Thursday, December 09, 2010

Normal 0 false false false EN-US X-NONE X-NONE MicrosoftInternetExplorer4

Hi Rocky

YES we are talking about "create"(sorry!) ...my code got mixed up a lot with all the fiddling. But I think I have it sorted now.

I posting my solution here for the sake of other CSLA programmers that might battle with this problem.

First things first:

Currently creating a SL 3 app in CSLA 3.8.4. (Will migrate later on but it works for me now.)

I'm not sure if this is the "Rocky Approved"  method but it works. Even though I have the a grid bound to the object in the SL App I do not make use of the "standard" way of adding child's to the collection i.e. by means of the user being presented with an "empty" line (read child) on the grid which he then populates as he goes. My approach is that a user click's on a "Menu Button (e.g. "Grilled Stake") " thereby triggering the process that leads to me adding a item (read child) to the order (read collection,  i.e. add a line to the clients order). Therefore in my implementation, I have no need for the "AddNewCore" option. What I'm trying to say is that if you need that kind of behavior you need to make some adjustments to my code, but I'm not sure exactly what. Maybe someone else can shed some light here.

Ok I have a "Order" object (Inherits from BusinessBase) which contains a collection object (Name "OrderItems" and Inherits from BusinessListBase) which in turn contains child objects (Named "OrderItem" which Inherits from BusinessBase). Thats the back ground.

Here is my code:

In OrderItems object. (The collection Object):

My  "AddNewItemFromMenu" Method on the "BusinessBaselist" object looks like this now ( This is the method the UI will call with certain parameters):

     Public Sub AddNewItemFromMenu(ByVal MenuID As Integer, ByVal MenuItemID As Integer, ByVal EntryTypeID As Enum_LineEntryTypes)

      Dim objCriteria As New OrderItem.Criteria

      ' Get item information

      With objCriteria

        .AddFromMenu = True

        .EntryTypeID = CType(EntryTypeID, Short)

        .MenuID = MenuID

        .MenuItemID = MenuItemID

      End With

       OrderItem.NewItem(objCriteria, AddressOf Me.ChildCreated_CallBck)        ' Callback sub located the collection object

End Sub


 My Callback Sub, named " ChildCreated_CallBck" located in the collection object looks like this:

    Friend Sub ChildCreated_CallBck(ByVal sender As Object, ByVal e As Csla.DataPortalResult(Of SmtSelfOrder.Business.BusinessClasses.OrderItem))

       ' Assign the Item to the collection

      If e.Error Is Nothing Then

        Add(CType(e.Object, OrderItem))

      Else

        ' Handle error here.

        Throw New System.Security.SecurityException(e.Error.Message)   ' Temp for now replace with what ever you want to do when encountering an error.

      End If

    End Sub

 

My "NewItem" Method on my child looks like this:

     Friend Shared Sub NewItem(ByVal Criteria As Criteria, ByVal handler As EventHandler(Of DataPortalResult(Of OrderItem)))

       Dim dp As DataPortal(Of OrderItem) = New DataPortal(Of OrderItem)()

       AddHandler dp.CreateCompleted, handler

      dp.BeginCreate(Criteria)

     End Sub

 

My Data Acces method looks like this on the Child:

    Private Overloads Sub DataPortal_Create(ByVal Criteria As Criteria)

      ' Remember to mark as child.

      MarkAsChild()

      ' Open Connection here and do your DB thing.

      '

      '      

      ' Force Rules

      ValidationRules.CheckRules()

     End Sub

Copyright (c) Marimer LLC