Data Access for Custom Collection Design

T

tecknick

OK I have a design issue with Data Access for Custom Collections and I
am hoping someone could point me in the right direction.

There are a large number (10 +) applications to be designed and all
seem to present and manipulate the same data in a huge variety of ways.
The client has indicated that there will be ongoing changes with the
data being collected so the application framework needs to allow for
easy updates of code and also needs to be extremely efficient in terms
of speed.

One chooses to go down the custom collections path and has decided to
split their INITIAL application into the following layers as follows :

- Presentation
- Business Objects (business objects and collections)
- Business Logic (business object/collection helper classes)
- Data Access (business object/collection data access)

So the BO, BL and DA layers will be reused across all applications.

Now for my DA issue!

I am going to use a very simple example to try and illustrate the
issue. If one has a few BO's as follows :

- ServiceCentre (A service centre for automobiles)
- MobileMechanic (A mechanic who works on the road)
- ServiceArea (The area that a mobile mechanic services)
- Suburb (An actual suburb that can be serviced or where a
service centre exists)

Where:
- One ServiceCentre has many MobileMechanic's
- A MobileMechanic belongs to one ServiceCentre
- A MobileMechanic has one Service Area
- A Service Area has one Mobile Mechanic
- A Service Area has many Suburb's
- A Suburb has only one ServiceArea
- A ServiceCentre is in one Suburb

Now from the above we create our 4 BO's and 2 collection (one for
MobileMechanic and the other for Suburb).

Keeping in mind that this is a very small example and the real world
example will be a few hundred BO's.
From this, a ServiceCentre uses the Suburb object and the ServiceArea
uses a collection of Suburb object. In designing the DA Layer we are
leaning toward a design where each BO has its own DA class/object (eg
the Suburb object in BO has a SuburbData object in DA). The reason for
having a DA object for each BO object is to centralise the access of
the data for the BO objects. In doing this, if the client indicates
that they would like to add a special telephone number for each suburb,
then we can easily add this attribute to the Suburb BO object and only
need to extend any code in the SuburbData DA object to ensure that the
data comes through into the BO object from the DB.

By doing this when a ServiceCentre object is filled with data, there
will be the use of ServiceCentreData object and also the SuburbData
object. When a ServiceArea is filled with data, there will be the use
of the ServiceAreaData object and the SuburbData object(mulitple
times).

But this presents a problem as it starts to bite into the performance.
In doing the data access in this way instead of using only one DB
connection/access to get ServiceArea object data (a data reader with
multiple select statements loading up the ServiceArea object and
collection of Suburb object) we are now
using 2+ DB connection/access to get the data.

Again, keep in mind that this is a very small example and that in the
real world example there are some objects which contain approximately
20 to 30 other object to hold the data that they require.

Any suggestions on how to overcome my dilemma or is the additional DB
connection/access overhead introduced by the clean design negligible?

Thanks in advance.

Tecknick
 
A

AMDRIT

This is a question that I myself have been working to resolve. I have beed
thinking along the lines of this

tell me what you think (Still its a lot of typing)

use a strongly typed dataset ("MyDataset"), complete with relationships.
Create a wrapper class as thus:

'mobile mechanic
public class mechanics
inherits collectionbase 'Needed so we can have for each
'We will continue to rely on the
datatable for enumeration

'Pointer to the datatable
private m_mechanics as MyDataset.Mechanics

'Reference to the DAL
private m_Orchistrator as myOrchistrator

'Pass in the dataset, we will get the datatable
public sub new (thedataset as Mydataset)
m_mechanics = thedataset.mechanics
end sub

'Pass in the datatable, assumes it is part of a dataset
public sub new(themechanics as mydataset.mechanics)
m_mechanics = themechanics
end sub

'Returns BO {Mechanic} based on the datarow requested
public readonly property Item(index as integer) as Mechanic
get
return castmeasmechanic(m_mechanics.rows(index))
end get
end property

'More collectionbase methods and properties
'Add
'Remove
'Remove At
'Insert
'AddRange

'Specialize Get Enumerator, since we are not using the list
' The iEnumerable will movenext over the datatable, not the list
' casting out to the BO{Mechanic}

'CRUD Operations
'Select One
'Select Many
'Select All
'Update (Inserted, Modified, Deleted)

end class

public class Mechanic
private m_datarow as Mydataset.MechanicsDataRow

public event BeforeRowChanged(sender as object, e as
MechanicChangeEventArgs)
public event AfterRowChanged(sender as object, e as
MechanicChangeEventArgs)
public event CanceledRowChanged(sender as object, e as
MechanicChangeEventArgs)

public sub new(datarow as Mydataset.MechanicsDataRow)
m_datarow = datarow
end sub

public readonly property MechanicID as integer
get
return m_datarow.MechanicID
end get
end property

public property [Name] as string
get
if m_datarow.isnamenull then
return string.empty
else
return ctype(m_datarow.name,string)
end if
end get
set(value as string)
if value is string.empty then
m_datarow.setnamenull
else
dim objEventArgs as MechanicChangeEventArgs
objEventArgs = new MechanicChangeEventArgs

objEventArgs.Column = "Name"
objEventArgs.CurrentValue = name
objEventArgs.ProposedValue = value

raiseevent beforerowchanged(me,objEventArgs )

if not MechanicChangeEventArgs.Cancel then
m_datarow.beginedit
m_datarow.name = value
m_datarow.endedit

raiseevent afterrowchanged(me,objEventArgs )
else
raiseevent canceledrowchange(me,objEventArgs )
end if

end if
end set
end property

Public Readonly Property Tools as Tools
get
return castmeastools(m_datarow.childrows("MechanicsTools"))
end get
end Property

public sub RejectChanges
end sub

public sub AcceptChanges
end sub

end class
 
T

tecknick

Amdrit

Thank you for your reply. I am still not at the coding stage of the
design. I am just trying to think about the practically of different
solutions first before we implement it.

We will be using custom collections (no datasets, datatable etc) as we
find them easier to use/create/maintain in a large scale application
like this (some people may disagree).

I cannot follow through your example very well. We are trying to
reduce the reads from the database in a situation where a single object
consists of numerous collections of other objects. I think the
solution that you are proposing is one which I was thinking of in that
the Helper class (you call wrapper) holds a collection of the object so
that only one read is required to load the collection with its objects.
Then you can simple loop through the collection a retrieve the objects
required. This can be used mulitple times but once the instance of the
Helper class is closed and disposed one would have to reinstantiate it
and perform another read from the DB. Hence we can reduce the DB reads
while the Helper class is in use.

Am I inderstanding your proposed solution?
 
A

AMDRIT

Let me try to say it this way.


When using ADO.Net, I only know of three ways to interact with the database
(Push, Fetch, or Poll).



That said you have a choice to make when using custom business objects and
how they are to be created. One school of thought is to create a Reader
(Poll), loop over the data and then create objects accordingly (Fetch). My
school, and I could be alone on this, is to allow a dataset to do all the
dirty work (Fetch), instead will would rather wrap my business objects
around the dataset, now then I don't have to reinvent that wheel.



Essentially you would have two sets of objects for each data table:



1. Datatable and datarow will manage the concurrency of the
underlying data and hold accountability for managing state. (Strongly typed
datasets will generate these for you)



2. Custom Collection and Custom Class will wrap around the
data table and feed the consuming UI's, enforce business logic, and
facilitate the seamless coupling with the "data pair". Upon instantiating
the Custom Collection, you would pass in the datatable object that you wish
to work with. The custom class will be instantiated with a pointer to the
datarow in the datatable. The custom class will expose the data columns in
the form of properties. Each property really just acts as a proxy to the
datarow's fields, only you get the opportunity to apply additional logic to
before completing call.



By taking advantage of a strongly typed dataset, or a couple of strongly
typed datasets, you will not have to worry (too much) about data relations
and constraints.



By using a Custom Collection, and overriding the GetEnumerater, you can now
manually (For Each..) against the collection or assign the datasource
property of some repeater object (Datagrid) , the custom collection.



When updating the database with your changes, you can still rely on the
dataset to have all the answers.



Back to the model



A data table has the columns

Col1 à PKey, AutoIncrement

Col2

Col3

Col4

Col5



Your data table will have these some or all of these columns

Col1 àReadonly

Col2

Col3

Col4



Your Custom Class with have some or all of these columns

Col1 à Readonly

Col2

Col3



Now lets say that we have a business rule that says, if Col2 = "Salary" then
Col3 must be .10 through .25 other wise Col3 = 0



Public Class CustomClass



Private m_Datarow as datarow



Public Property Col2() as string

Set(Value as string)



If Value = "Salary"

If col3 >= .10d andalso col3 <= .25 then

Col3 = .10d

End end

End If



m_Datarow.BeginEdit

m_Datarow("Col2") = Value

m_Datarow.EndEdit



End Set

End Property



End Class



On the SET procedure for the property COL2 we may choose to default the
value of COL3 to .10 when it is outside the allowed range and the pending
value for COL2 is "Salary".



If you plan on implementing something like a Model-View-Controller pattern,
then you are well on your way.



Class Controller



Public Function ListGarages() as GarageCollection



Dim objGarages as GaragesCollection



'if using web services, pass this request on to your web proxy

objGarages = WebserviceProxy.ListGarages



'Otherwise create connection, dataadapter, and fill an instance
of Garages datatable

iRet = MyAdapter.Fill(MyDataset.Garages)



'if iRet > 0 Init our Garages

objGarages = new GaragesCollection(Mydataset.Garages)



Return objGarages



End Function



Public Function UpdateGarages(objGarages as Garages) as integer



'Again, if webservices, blah vlah blah



'Otherwise, update with the dataadapter

Return Da.update (objGarages.GetChanges(inserted or updated or
deleted) )



End Function



End Class
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Top