Skip to main content

Command Palette

Search for a command to run...

CSLA : Step-by-Step, Complex Data Types & some gotchas

Updated
4 min read
CSLA : Step-by-Step, Complex Data Types & some gotchas

Welcome to the third instalment of CSLA: Step-by-Step. In the previous 2 parts we looked at which tools to use to get started and creating business objects with parent-child relationships.

In this part we’ll do some pretty cool stuff with CSLA as well as tackling one or two gotchas that pops up every now and again.
All right, let’s get cracking. Open our NorthwindTraders solution, we created in part 1. If you followed the previous two tutorials you should already have the following classes in your project:

  • Customer (BusinessBase/EditableRoot)
  • Database (Module)
  • Order (BusinessBase/EditableRoot)
  • OrderDetail (BusinessBase/EditableChild)
  • OrderDetails (BusinessListBase/EditableChildList)

The Gotchas

One gotcha that had me scratching my had every now and again is when you try to create a new object i.e. Order.NewOrder(), you get the following error:

Csla.DataPortalException : DataPortal.Create failed (System.NotSupportedException: Invalid operation – create not allowed

You can test this by adding the following code to your OrderUnitTests class in your Northwind solution’s Order.vb :

_
PublicSubCreateOrder()
DimobjOrderAsOrder\=Order.NewOrder
objOrder.CustomerID\="ALFKI"
objOrder.EmployeeID\=1
objOrder.ShipVia\=1
objOrder.Save()
EndSub

Generated using PrettyCode.Encoder

At first it seems like an extremely weird error, but the solution and explanation is actually very easy. You’ll notice that your object’s DataPortal_Create, expect a criteria parameter of type object. Remove the parameter from and test your code again.Viola! it works.

You’ll notice this error only happens when you have an object for a table that has a primary key that also has an Identity Specification. The code generated expects you pass the primary key/identifier for the object, but when it is assigned by the database, you obviously wouldn’t need to. The Customer object in our Northwind Solution, will work without any hiccups, because you have to specify the CustomerID when creating a new Customer.

Another gotcha is when you manually add a child property to your business object. You have to change the IsValid and IsDirty property of your object to include the new child object. Also make sure you change your Data Access code to include the new child object. The easiest way this can be overcome by just re-generating your business object and including both child object properties.

The Cool Stuff

Right, let’s get to the nifty bit.

Create a new BusinessBase/EditableRoot object called Product. Use CodeSmith to generate it from the Northwind Products table and MyGeneration to generate the sql stored procedures. See part 1, if you need to know how.
Your CodeSmith property grid should look something like this:

![Product](http://www.mythicalmanmoth.com/wp-content/uploads/2009/10/product-thumb.png "Product")

We are going to change the ProductID property on you OrderDetail object to be of type Product and not Integer. This will give us some pretty cool and powerful flexibility later on.

First open your OrderDetail class. Change the ProductID variable and property to look like this:

Private_productAsProduct\=Nothing

PublicPropertyProduct()AsProduct
Get
CanReadProperty("Product",True)
Return_product
EndGet
Set(ByValvalueAsProduct)
_product\=value
EndSet
EndProperty

Generated using PrettyCode.Encoder

Change FetchObject, AddInsertParameters and AddUpDateParameters to look like this:

PrivateSubFetchObject(ByValdrAsSafeDataReader)
_orderID\=dr.GetInt32("OrderID")
_product\=Product.GetProduct(dr.GetInt32("ProductID"))
_unitPrice\=dr.GetDecimal("UnitPrice")
_quantity\=dr.GetInt16("Quantity")
_discount\=dr.Item("Discount")
EndSub

PrivateSubAddInsertParameters(ByValcmAsSqlCommand,ByValparentAsOrder)
cm.Parameters.AddWithValue("@OrderID",parent.OrderID)
cm.Parameters.AddWithValue("@ProductID",_product.ProductID)
cm.Parameters.AddWithValue("@UnitPrice",_unitPrice)
cm.Parameters.AddWithValue("@Quantity",_quantity)
cm.Parameters.AddWithValue("@Discount",_discount)
EndSub

PrivateSubAddUpdateParameters(ByValcmAsSqlCommand,ByValparentAsOrder)
cm.Parameters.AddWithValue("@OrderID",_orderID)
cm.Parameters.AddWithValue("@ProductID",_product.ProductID)
cm.Parameters.AddWithValue("@UnitPrice",_unitPrice)
cm.Parameters.AddWithValue("@Quantity",_quantity)
cm.Parameters.AddWithValue("@Discount",_discount)
EndSub

Generated using PrettyCode.Encoder

Instead of passing the ProductID variable, we’re passing the Product object’s ProductID property and we’re fetching a Product object rather than an integer value.

In theory we can now also change our UnitPrice property to a readonly property and return the Product.UnitPrice, like so:

PublicReadOnlyPropertyUnitPrice()AsDecimal
Get
CanReadProperty("UnitPrice",True)
Return_product.UnitPrice
EndGet
EndProperty

Generated using PrettyCode.Encoder

Note: You can then also change you data access code, practically you would no longer need to save the unit price on the OrderDetail level. This would all depend on the design of your application. For now, we’ll just assume, we no longer require the unit price on the order detail, since we have it on the product level.

Now we need to see if the changes we’ve made actually works. Let’s create our Order with Details Unit test, it can look something like this:

_
Public Sub CreateOrder()
‘We Get the Product we want to add to this order
‘our clientisafanofGrandma’sBoysenberrySpreadsohe’sordereda100
Dim objProduct As Product\=Product.GetProduct(6)

‘Wecreatetheorder
Dim objOrderAs Order\=Order.NewOrder
objOrder.CustomerID\="CHOPS"
objOrder.EmployeeID\=1
objOrder.ShipVia\=1

‘WecreatetheOrderDetail
Dim objDetail As OrderDetail\=OrderDetail.NewOrderDetail
objDetail.Product\=objProduct
objDetail.Quantity\=100

‘AddtheOrderDetailtotheorder
objOrder.Orderdetails.Add(objDetail)

‘SavetheOrder
objOrder.Save()

‘DisplayourneworderID
Console.Write(objOrder.OrderID.ToString)
EndSub

Generated using PrettyCode.Encoder

Note: I’ve add a constructor to the OrderDetail object to make things easier. Also when you compile there will be a few errors complaining about the _productID, remember it’s now the _product.ProductID.Here’s the code for the constructor:

Friend Shared Function NewOrderDetail()As OrderDetail
Return New OrderDetail()
EndFunction

Private Sub New()
ValidationRules.CheckRules()
MarkAsChild()
EndSub

Generated using PrettyCode.Encoder

I’ve made a couple of changes to the code, that I did not mention here, you can download the files here.

I hope part 3 was of use, tune in next time for some Data Binding Fun with CSLA.

Happy Coding!

More from this blog

M

Mythical Man Moth

80 posts

Hi, I’m Pieter van der Westhuizen. I'm a professional freelance web & mobile developer from South Africa that has been code slinging for more than 23 years. https://youtube.com/shorts/aCCKAnDNrzM