In this tutorial, you will learn how to:
In the previous tutorial, we defined and used data objects in the same project. This is not how DataObjects for .NET is supposed to be used. A better way to use DataObjects for .NET is to define your business objects (data objects) in a separate assembly, data library, so it can be used by multiple applications. Although this is not mandatory, your enterprise can assign a special team of "data-oriented" developers to the task of creating business object projects (data libraries) and another team of "GUI-oriented" developers to creating client applications using data libraries. This is how DataObjects for .NET enables you to benefit from the right design and development technology. However, DataObjects for .NET fits equally well into smaller development projects, where business objects and client applications are developed by the same team. The main architectural benefit of using DataObjects for .NET is in clear separation between business logic and presentation layer (GUI). Encapsulating your data model and logic in a centralized place (data library) that can be reused by multiple clients makes your applications robust, scalable and fun to develop. This is not to mention numerous tools and enhancements DataObjects for .NET adds to your data access toolbox, including such a powerful and extremely important one as virtual mode (cached access to large recordsets), see Tutorial 4: Virtual Mode: Dealing with Large Datasets.
A data library is an assembly (DLL) containing DataObjects for .NET schema and business logic components and code that defines your data objects. These data objects can then be used in any project by simply referencing the library in the project and using a DataObjects for .NET C1DataSet component to connect to the library. All database access and business logic code is encapsulated in the library, so it can be created and maintained independently of client applications.
To define business logic:
With the schema now in place, we can specify business logic. You already saw some elements of business logic, namely constraint expressions and calculation expressions, in Tutorial 1: Creating a Data Schema. Expressions are an easy and straightforward way of defining business logic. However, when expressions are not enough, some code must be written. In this tutorial, we will show how to write business logic code.
Every schema object can be represented by a special business logic component. Business logic components (components C1.Data.C1TableLogic and C1.Data.C1DataSetLogic) have events where you can write code responding to various occurrences in data objects.
To write code in Visual Basic
Visual Basic |
Copy Code
|
---|---|
Private Sub table_Customers_BeforeFieldChange(ByVal sender As Object, ByVal e As C1.Data.FieldChangeEventArgs) Handles table_Customers.BeforeFieldChange If e.Field.Name = "CustomerID" Then e.NewValue = CStr(e.NewValue).ToUpper() End If End Sub |
To write code in C#
C# |
Copy Code
|
---|---|
private void table_Customers_BeforeFieldChange(object sender, C1.Data.FieldChangeEventArgs e) { if (e.Field.Name == "CustomerID") e.NewValue = ((string)e.NewValue).ToUpper(); } |
To write code in Visual Basic
Visual Basic |
Copy Code
|
---|---|
Private Sub table_Order_Details_BeforeFieldChange(ByVal sender As Object, ByVal e As C1.Data.FieldChangeEventArgs) Handles table_Order_Details.BeforeFieldChange If e.Field.Name = "Quantity" Then If (CShort(e.NewValue) < 1) Then e.NewValue = CShort(1) End If ElseIf e.Field.Name = "Discount" Then If CSng(e.NewValue) < 0 Or CSng(e.NewValue) > 1 Then Throw New ApplicationException("Discount must be between 0 and 1") End If End If End Sub |
To write code in C#
C# |
Copy Code
|
---|---|
private void table_Order_Details_BeforeFieldChange(object sender, C1.Data.FieldChangeEventArgs e) { if (e.Field.Name == "Quantity") { if ((short)e.NewValue < 1) e.NewValue = (short)1; } else if (e.Field.Name == "Discount") { if ((float)e.NewValue < 0 || (float)e.NewValue > 1) throw new ApplicationException("Discount must be between 0 and 1"); } } |
To write code in Visual Basic
Visual Basic |
Copy Code
|
---|---|
Private Sub table_Order_Details_AfterFieldChange(ByVal sender As Object, ByVal e As C1.Data.FieldChangeEventArgs) Handles table_Order_Details.AfterFieldChange If e.Field.Name = "Quantity" Then Dim orderDetail As Order_DetailsRow, product As ProductsRow Dim oldValue, unitsOrdered As Short orderDetail = Order_DetailsRow.Obj(e.Row) product = orderDetail.GetProductsRow() If Not (product Is Nothing) Then If e.OldValue Is Convert.DBNull Then oldValue = CShort(0) Else oldValue = CShort(e.OldValue) End If unitsOrdered = CShort(CShort(e.NewValue) - CShort(oldValue)) End If product.UnitsInStock = CShort(product.UnitsInStock - unitsOrdered) If product.UnitsInStock < 0 Then product.UnitsInStock = 0 End If product.UnitsOnOrder = product.UnitsOnOrder + unitsOrdered End If End Sub |
To write code in C#
C# |
Copy Code
|
---|---|
private void table_Order_Details_AfterFieldChange(object sender, C1.Data.FieldChangeEventArgs e) { if (e.Field.Name == "Quantity") { Order_DetailsRow orderDetail = Order_DetailsRow.Obj(e.Row); ProductsRow product = orderDetail.GetProductsRow(); if (product != null) { short unitsOrdered = (short)((short)e.NewValue - (e.OldValue == Convert.DBNull ? (short)0 : (short)e.OldValue)); product.UnitsInStock = (short)(product.UnitsInStock - unitsOrdered); if (product.UnitsInStock < 0) product.UnitsInStock = 0; product.UnitsOnOrder += unitsOrdered; } } } |
Click here for an explanation of some important features in this code.
There are two data object classes generated by DataObjects for .NET in this code: Order_DetailsRow and ProductsRow. Class Order_DetailsRow represents an Order Details row, and ProductsRow represents a Products row. Such classes are automatically generated and maintained for each table and table view in the schema. For example, ProductsRow is an object (business object, data object) where each field has a corresponding property (ProductsRow.UnitPrice, ProductsRow.UnitsInStock, and so on), and each relation has a corresponding method (Products.GetOrder_DetailsRows, and so on), allowing you to obtain child rows and the parent row.
Using these classes, you can write business logic code in a convenient, type-safe way, and benefit from Visual Studio code-completion features giving you the lists of properties and methods to choose from.
The data object classes belong to the Northwind.DataObjects namespace (substitute your data library name instead of Northwind, if different). They are hosted in the assembly Northwind.DataObjects.dll. This data objects assembly is generated by DataObjects for .NET each time you change the schema and save it in the C1SchemaDef component. A reference to this assembly is added to your data library project References.
To write code in Visual Basic
Visual Basic |
Copy Code
|
---|---|
Private Sub dataset_ProductsOrders_AfterEndAddNew(ByVal sender As Object, ByVal e As C1.Data.RowChangeEventArgs) Handles dataset_ProductsOrders.AfterEndAddNew If e.DataTable.SchemaTable.Name = "CustOrdersDetails" Then Dim order As CustOrdersDetailsRow_tableView order = CustOrdersDetailsRow_tableView.Obj(e.Row) If order.IsShipNameNull() Then order.ShipName = order.CompanyName If order.IsShipAddressNull() Then order.ShipAddress = order.Address If order.IsShipCityNull() Then order.ShipCity = order.City If order.IsShipRegionNull() Then order.ShipRegion = order.Region If order.IsShipPostalCodeNull() Then order.ShipPostalCode = order.PostalCode If order.IsShipCountryNull() Then order.ShipCountry = order.Country End If End Sub |
To write code in C#
C# |
Copy Code
|
---|---|
private void dataset_ProductsOrders_AfterAddNew(object sender, C1.Data.RowChangeEventArgs e) { if (e.DataTable.SchemaTable.Name == "CustOrdersDetails") { CustOrdersDetailsRow_tableView order = CustOrdersDetailsRow_tableView.Obj(e.Row); if (order.IsShipNameNull()) order.ShipName = order.CompanyName; if (order.IsShipAddressNull()) order.ShipAddress = order.Address; if (order.IsShipCityNull()) order.ShipCity = order.City; if (order.IsShipRegionNull()) order.ShipRegion = order.Region; if (order.IsShipPostalCodeNull()) order.ShipPostalCode = order.PostalCode; if (order.IsShipCountryNull()) order.ShipCountry = order.Country; } } |
Click here for a list of business logic events.
This tutorial demonstrates only a limited number of business logic events. Here is a brief list of business logic events available in DataObjects for .NET (we omit prefixes Before and After pertaining to most events, retaining the prefix only if an event occurs only Before or only After):
Event | Description |
---|---|
AddNew | Fired when a new (empty) row is added. |
AfterChanges | Fired when all changes initiated by a field change are done and handled by the business logic code, see the FieldChange event. |
BeginEdit | Fired when the user starts editing a row (data-bound controls start editing a row immediately after they position on it, even though no changes have been made yet). |
CancelEdit | Fired when the user cancels editing a row reverting the changes made to it. |
Delete | Fired when a row is deleted. |
EndAddNew | Fired when a newly added row becomes a regular row in the rowset. When a row is added, it is added empty, its primary key is unknown. A row with unknown primary key is in special transitory state, it is not a regular rowset row. Only after its primary key is set it becomes a regular (added) row, which is signaled by this event. |
EndEdit | Fired when the user finishes editing a row (data-bound controls finish editing a row when they leave that row, even if no changes have been made). |
FieldChange | Fired when a field value is set. Inside this event, your code can set other fields triggering recursive FieldChange events, DataObjects for .NET handles this situation correctly. Only after all changes are done and handled, AfterChanges event is triggered. |
FirstChange | Fired when a first change is made to the row (a field value changed) after BeginEdit. |
UpdateRow | This event is not fired in a client application, unless it is a direct client, that is a 2-tier application updating the database directly from client, see Tutorial 3: Creating Distributed 3-Tier Applications. In a 3-tier deployment, it is fired only on the server, when a modified row is committed to the database. |
Number of Components | Name | Namespace |
---|---|---|
1 C1DataSet | C1DataSet1 | C1.Data.C1DataSet |
2 C1TrueDBGrid | C1TrueDBGrid1 C1TrueDBGrid2 |
C1.Win.C1TrueDBGrid.C1TrueDBGrid |
1 command Button | Button1 | System.Windows.Forms.Button |
To write code in Visual Basic
Visual Basic |
Copy Code
|
---|---|
C1DataSet1.Update() |
To write code in C#
C# |
Copy Code
|
---|---|
c1DataSet1.Update(); |
To write code in Visual Basic
Visual Basic |
Copy Code
|
---|---|
Private Sub C1DataSet1_BeforeFill(ByVal sender As Object, ByVal e As C1.Data.FillEventArgs) Handles C1DataSet1.BeforeFill Dim dataSetDef As C1.Data.SchemaObjects.DataSetDef dataSetDef = e.DataSet.Schema.DataSetDefs("ProductsOrders") e.Filter.Add(New C1.Data.FilterCondition(dataSetDef.TableViews("ProductsOrderDetailsCust"), "[CategoryID] = 1")) e.Filter.Add(New C1.Data.FilterCondition(dataSetDef.TableViews("CustOrdersDetails"), "[CategoryID] = 1")) End Sub |
To write code in C#
C# |
Copy Code
|
---|---|
private void c1DataSet1_BeforeFill(object sender, C1.Data.FillEventArgs e) { C1.Data.SchemaObjects.DataSetDef dataSetDef = e.DataSet.Schema.DataSetDefs["ProductsOrders"]; e.Filter.Add(new C1.Data.FilterCondition(dataSetDef.TableViews ["ProductsOrderDetailsCust"], "[CategoryID] = 1")); e.Filter.Add(new C1.Data.FilterCondition(dataSetDef.TableViews ["CustOrdersDetails"], "[CategoryID] = 1")); } |