3-Tier Development - A little confused..

I

iTISTIC

Developing a new app and am trying to make this my first truly
OOP/3-Tier app. I understand the principles of the presentation,
business, and data layers. I do, however, have some questions on where
certain functionality should be placed and how some things should be
implemented.

Let's use a simple example such as an application to manage customer
records (customer_id, first_name, last_name). I'd have a Customer
business object with ID, FirstName, and LastName properties. What I
don't understand is the following:

1. How do I obtain a Customer business object pre-loaded with data from
the database (customer_id 3 for example). I could create a constructor
in the Customer business class with the customer_id as a parameter and
have the constructor call the data layer to return a dataset containing
the customer's data and then set the properties accordingly. Is this
how this is normally accomplished? I could also have a method in the
Customer data layer that accepts a customer_id as a parameter and
returns a Customer business object. My only problem with this is that I
have it in my head that the presentation layer should never communicate
with the data layer. Some help here would be great.

2. How do I obtain a collection or ArrayList of Customer objects in the
case where I need to return more than one customer object? Such an
example would be a form that listed all customers. Calling a method in
the Customer data layer to return an ArrayList of Customer business
objects would work here as well, but this is breaking the same rule of
having the presentation layer working with the data layer. The other
option is to put a method in the Customer business object to return an
ArrayList of Customer business objects, but then my code has to
instantiate the Customer object simply to return more Customer objects.
This is unless I make the method in the Customer business object
shared.

3. Data layer methods to insert, update, delete database records -
Should the insert and update methods accept business objects as
parameters or should they accept a long parameter list that contains a
seperate parameter for each of the business object's properties?

Any help anyone can provide here is more than welcome. These issues
have been keeping me from developing an OOP/3-tier solution for quite
some time as I just can't seem to force myself to develop a system
without truly knowing how these issues should be tackled the proper
way.

Thanks in advance for your help!

Shawn Berg
 
K

Kevin Spencer

Hi Shawn,

Good questions! OOP is all about thinking first, and coding later.

I see a couple of holes in your understanding of these principles. The first
is understanding which layer knows what about which other layer. Whether
you're talking about a 3-tier or more-tier app, the principle is failry
simple: Each tier is a client of the tier below it. That means that no tier
knows anthing about the tier above it, and only knows about the tier
immediately below it. Example:

Presentation Tier
Knows about Business Tier (1 below)
Does NOT know about Data Tier (2 below)
Business Tier
Knows about Data Tier (1 below)
Does NOT know about Presentation Tier (above)
Data Tier
Does NOT know about Presentation Tier (above)
Does NOT know about Business Tier (above)

How does the Business tier communicate with the Presentation tier? It used
Properties which the Presentation Tier can read, and Events which the
Presentation Tier can subscribe to.

What this means with regards to Question number 1 ("How do I obtain a
Customer business object pre-loaded with data from the database"), the
answer should be obvious after understanding this principle. The Customer
class is a business class. It may know about the Data Layer. So, yes, you
could create a Contructor that takes a Customer ID and populates the
instance from the Data Tier. Or you could create a Customer ID property in
the Customer class that, in the setter method, fetches the data from the
Data Tier, and populates the instance with it. The second is the better
method. The first method can also be used, by overloading the Constructor,
and in the variation that takes a Customer ID, use the setter method of the
Customer ID property to populate the class. Very Simple Example:

public class Customer
{
private string _ConnectionString = "Your Default Connection String";
public string ConnectionString
{
get { return _ConnectionString; }
set { _ConnectionString = value; }
}

private string _FirstName;
public string FirstName
{
get { return _FirstName; }
set { _FirstName = value; }
}

private int _CustomerID;
public int CustomerID
{
get { return _CustomerID; }
set
{
// Set the private member
_CustomerID = value;

// (Imaginary Data Tier method)
// Note that the Data Tier only knows what it is told
// about the data it's working with.
// The business tier knows how to use the Data Tier.
DataTable dt = DataTier.GetDataTable(
"TblCustomer", "CustomerID = " + _CustomerID,
_ConnectionString);

// Populate the instance
_FirstName = (int)dt.Rows[0]["FirstName"];
}
}

public Customer() {}

public Customer(string connectionString)
{
_ConnectionString = value;
}

public Customer(string connectionString, int customerID)
{
// Changes the Default Connection String
_ConnectionString = connectionString;

// Note the use of the setter method here
CustomerID = customerID;
}

public Customer(int customerID)
{
// Note the use of the setter method here.
// This overload uses the Default Connection String
CustomerID = customerID;
}

}

The second thing you seem to be missing is that a class and an object are
not the same thing. There is only one copy of a class. There can be many
copies of objects. An object is an instance (or copy) of a class.

So, when you ask

"How do I obtain a collection or ArrayList of Customer objects in the case
where I need to return more than one customer object?"

and you say

"The other option is to put a method in the Customer business object to
return an ArrayList of Customer business objects"

it seems that you're mixing up classes and objects. The solution is to
create a Collection class that can be used to store multiple Customer
instances. The best thing, rather than using an ArrayList, is to create a
strongly-typed Collection that always works with Customer instances.

Question 3 is answered in my opening remarks about what layer knows what
about the other layers.

--
HTH,

Kevin Spencer
Microsoft MVP
..Net Developer

Presuming that God is "only an idea" -
Ideas exist.
Therefore, God exists.
 
I

iTISTIC

Kevin,

You've been very helpful thus far. I appreciate your time. Would you be
able to show me an example of a brief CustomerCollection class and how
it is implemented? This makes sense, but I'm not sure how to implement
it.

Another question that has been raised now is why the Business tier
deals with connection strings which are specific to the data tier?
Would it be OK to move this functionality to the data tier and have the
data tier set the connection string by default from a web.config
<connectionStrings> variable? I'm thinking I could have a base class
that all the other data classes inherit from entitled "DataBase" which
has this functionality built in, as well as the properties for changing
the connection string if necessary. Is this ok?

Thanks again,

Shawn
 
I

iTISTIC

Think I've found a good article on this. Is this what you were
referring to for the CustomerCollection class?

And then in this class I could have methods such as "LoadByCity(ByVal
city As String)", etc. which would populate the collection?

Shawn
 
K

Kevin Spencer

I don't know if it is or not, Shawn. It would seem that you omitted a link
to the article!

But from the sound of it, it seems right.

A lot depends on which version of the .Net Framework you're using. .Net 2.0
includes some nice support for Generics, which are classes that are
strongly-typed at compile-time. An example of this would be:

using System.Collections.ObjectModel;

// Collection<T> is a generic class which can be a Collection of any type.
// You pass the type to it to create a strongly-typed Collection.
// By inheriting the Strongly-typed Collection, you now have your own
// strongly-typed and extensible Collection.
// It already includes all the properties of the inherited Collection
// (which is what inheritance does), and you can add any other
// properties, methods, etc., that you want to it.
public class CustomerCollection : Collection<Customer>
{

public void LoadByCity(string cityName)
{
DataTable dt = DataTier.GetDataTable("Customer",
"City = '" + cityName + "'");
foreach (DataRow row in dt.Rows)
{
Customer c = new Customer();
c.CityName = (string)(row[CityName]);
c.FirstName = (string) (row[FirstName]);
// etc.
Items.Add(c);
}
}

public CustomerCollection() : base()
{
}

public CustomerCollection(string cityName) : base()
{
LoadByCity(cityName);
}
}

--
HTH,

Kevin Spencer
Microsoft MVP
..Net Developer

Presuming that God is "only an idea" -
Ideas exist.
Therefore, God exists.
 
K

Kevin Spencer

Yes, that's about right, if you're using .Net 1.1.

--
HTH,

Kevin Spencer
Microsoft MVP
..Net Developer

Presuming that God is "only an idea" -
Ideas exist.
Therefore, God exists.
 
K

Kevin Spencer

See my example in a previous reply. I showed you how to create a Collection
derived from the Generic Collection class. In case you have trouble finding
it, here it is again:

using System.Collections.ObjectModel;

// Collection<T> is a generic class which can be a Collection of any type.
// You pass the type to it to create a strongly-typed Collection.
// By inheriting the Strongly-typed Collection, you now have your own
// strongly-typed and extensible Collection.
// It already includes all the properties of the inherited Collection
// (which is what inheritance does), and you can add any other
// properties, methods, etc., that you want to it.
public class CustomerCollection : Collection<Customer>
{

public void LoadByCity(string cityName)
{
DataTable dt = DataTier.GetDataTable("Customer",
"City = '" + cityName + "'");
foreach (DataRow row in dt.Rows)
{
Customer c = new Customer();
c.CityName = (string)(row[CityName]);
c.FirstName = (string) (row[FirstName]);
// etc.
Items.Add(c);
}
}

public CustomerCollection() : base()
{
}

public CustomerCollection(string cityName) : base()
{
LoadByCity(cityName);
}
}

The differences are numerous:

1. Inheriting CollectionBase, which works with the base Object data type,
requires casting, which is somewhat costly in terms of performance. Not a
lot, but a little.

2. When you inherit from a Generic Collection that has been assigned a type,
there's usually no need to override anything, as everything is already
strongly-typed to the type you have set the Generic Collection to. All you
need to do is add any implementation-specific functionality you want to add
and you're done. This means that you have less code to write.

3. In some cases, you don't even need to inherit
System.Collections.ObjectModel.Collection<T>. When you add one to your code
and assign a type to it, it is compiled as that type. You only need to
inherit it if you want to add any implementation-specific functionality
and/or properties. For example, if you didn't need anything but standard
Collection behavior for your Customer Collection, you could simply use:

private Collection<Customer> Customers = new Collection<Customer>();

Generics arae very powerful tools, perhaps the single most useful addition
to the .Net 2.0 platform.
 
I

iTISTIC

Makes a lot of sense, and definitely less code. Going to have to see
how I can implement that in VB.NET versus C#. Shouldn't be too
different I wouldn't assume.

Thanks for all your help Kevin.

Shawn
 
I

iTISTIC

Ok I've got the generics figured out. I've created CutomerCollection
class that inherits from Collection(Of Customer) .. Using VB.NET so the
syntax is a little different than yours, but should be the same. Thanks
so much for your help with all of this.

My next question is how you typically segment the Data Layer of your
application. I was going to create a seperate Data Layer class for
each Business Layer object that requires DB access. This includes the
Cusomer business layer class, CustomerCollection business layer class,
etc. Inside the Customer DAL I'd have methods to select, update, and
delete single customers. In the CustomerCollection DAL class I'd have
methods to select subsets of customers (by city, customers with open
projects, etc). Do you typically make your DAL methods Shared so you
don't have to instantiate an object in code to use them? It seems from
your examples that you do. Also, does your DAL typically just return
ADO.NET objects (such as DataSets, DataTables, DataReaders, etc.) or do
you return business objects (such as Customer, CustomerCollection) from
the DAL? I'd assume based on your example you return ADO.NET objects,
but I'm not 100% certain and want to be clear.

Thanks again!
 
K

Kevin Spencer

Well, how you put your Data Tier together is a matter of what you anticipate
needing, and how extensible it needs to be.

I do have a set of Data classes that perform basic database functions, and
yes, all of the methods are static. It does always return ADO.Net objects,
as you've guessed, or .Net primitives. But I also use the Microsoft
components as well when appropriate. For example, I'm currently working on a
database interface app, and using the built-in Visual Studio capabilities of
generating typed DataSets and other Data objects, which is in this case,
appropriate (and easy). I am also using my DAL for executing more specific
types of operations. For example, I've been deriving a custom DataGridView
class that can perform custom queries built by a Wizard I also developed.
When it runs the queries, rather than fetching a large DataSet or DataTable,
it fetches only the column values needed to filter the BindingSource I'm
employing in the Grid. So, I run a custom SQL Query to fetch a DataTable,
and then throw away the DataTable after I create the filter.

In fact, you might want to download the Microsoft .Net Data Application
block:

http://msdn.microsoft.com/practices/guidetype/AppBlocks/default.aspx

I would recommend studying it at any rate. Whether you choose to use it,
modify it, or learn from it and build your own is up to you. I'm sure you'll
do well in any case. You seem to have an excellent head on your shoulders!

--
HTH,

Kevin Spencer
Microsoft MVP
..Net Developer

Presuming that God is "only an idea" -
Ideas exist.
Therefore, God exists.
 

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

Top