views:

160

answers:

3

I'm having difficulty wrapping my head around business objects or more specifically, business object collections.

Here's a quick example of what i'm trying to do.

If i have an Incident Object, this object can have a number of people involved and each of those Person objects can have multiple notes. Notes can't exist without a Person object and Person objects can't exist without an Incident Object.

If i have Public List notes = new List() then methods such as ADD, REMOVE become available to Person within Incident. I assume that if i was to call those methods on the Notes collection it will simply remove it from the List but not execute any code to actually add/update/delete the employee from the data source. This leads me to believe that i shouldn't use List but something else?

This also leads me to another question. Where should the actual Database CRUD operations reside. Should a Note object have it's own CRUD or should the Person object be responsible for it since it can't exist without it?

I'm a little lost about which way to go and i'd like to get this part right because it will be the template for the rest of the program.

Thanks.

+1  A: 

You have several different questions in one here, I will try to answer most.

In regards to problems using List<T> - the framework has a ReadOnlyCollection<T> that is useful in exactly your situation. This is a collection that does not allow adding or removing once created.

In regards to CRUD operation responsibility - that should belong to your data layer, not any of your objects (see SRP - Single Responsibility Principle).

Oded
You can also convert a List<T> to readonly using it's AsReadOnly method() - http://msdn.microsoft.com/en-us/library/e78dcd75.aspx
Dan Diplo
So that would require a seperate data access object for each business object? There would still be something like AddNote, RemoveNote in the Person object and when called, they would instruct the DAL to do the work?
Mathew
Person class by itself shouldnt have any knowledge about a note. It only knows about a person, and that is all it should care about. See my post for further information.
JonH
A: 

The way I do it is: each object that has children objects contains a list of them, and each object with a parent contains a property with its type. Adding is done by populating an object (or an hierarchy of objects) and sending to the DAL for persistence if desired. The CRUD operations are all in the DAL, which is agnostic of the object types but uses such types to determine which tables, columns, etc to access. Deleting is the only thing dealt with differently by setting an object's Deleted property which triggers the DAL to remove it.

Now regarding business logic - it does not reside with the objects themselves (the DAOs) but rather it is done by classes that receive or gather such DAOs when necessary, perform their work and send the DAOs back to the DAL for updates.

Otávio Décio
+1  A: 

Some great information has been given but one thing that you mentioned that may be confusing you is this:

"If i have Public List notes = new List() then methods such as ADD, REMOVE become available to Person within Incident."

That all depends on how you design your classes. One thing that you should think about is the way this data relates to one another. That will help you picture your class design.

It sounds like the following:

  • One incident can involve many people
  • One person can create many notes
  • A note is the lowest level and exists due to an incident being created and a responsible person(s) working on that incident.

Incident 1 - many Persons

Person 1 - many notes

You can do this type of relationship in a number of ways. One way may be to actually seperate the objects involved, and then create joined objects.

For instance

public class Incident {
//insert incident fields here
//do not add person logic / notes logic
//probably contains only properties
}

public class Person {
//insert person fields
//private members with public properties
//do not embed any other logic
}

public class Comment {
 //insert comment private fields
 //add public properties
 //follow the law of demeter
}

These classes do not give details to one another, they are just repositories to store this information. You then relate these classes to one another for instance

public class IncidentPersonnel {
List<Person> p;
//add methods to add a person to an incident
//add methods to remove a person from an incident
....
}

Then you may have another class handling the commenting by personnel

public class PersonnelNotes {
List<Note> n;
//other methods...
}

You can go further with this but it may complicate things but I am just giving you another idea of how to handle this.

Try to follow the law of demeter for functions

Encapsulate all of your objects, in addition, your neighbor can talk to you but not much else... This will help keep your classes loosely coupled and makes the thought process a bit simpler for you.

Finally, you mentiond how the CRUD operations should work. This all goes back to your DAL (Data Access Layer). Rather then return rows of data from a table you could then return a referenced object with all of its attributes. Add's and remove's work the same way (passing in or out an object). You can use an ORM or write up your own DAL. It all depends on how involved you want to involve yourself :).

JonH
Just a note that this is just one way to handle this. However, it may make the most sense to other developers.
JonH
Great info, thankyou. I do have one last question.List<Note> in PersonnelNotes is currently private. If i wanted a list of the notes for that person i would return some king of readonly list and if i called DeleteNote on PersonnelNotes, it would call the DAL to delete it and then rebuild the readonly list?
Mathew
Definately should be private, if you want to remove a note from the list you can do n.Remove(YourIDRepresentationOfANote). Even if the list is not explicitly readonly you can always make it readonly at anytime. Once you remove it from the list, you can then call your DAL passing in the IDentifier of the note to remove it from the database.
JonH
Ok, i think i get it. Note would therefor need to have a private ID field which can be read through a property get (but no set for protection). That would require Reflection in order to set the ID field when it is being loaded by the DAL?
Mathew
That would be one way to do it. If you want another option you can do the following. Rather then n.remove(yourID) (removing the item from the list), you could simply call the DAL to "re-create" the list of notes. Meaning the DAL would handle the delete, create a temporary list and reload it with all the notes for just that person and then return it back to your application as a newly created list. So many options, ultimately it is up to your comfort level.
JonH
Sorry, i meant that as notes are retrieved from the datasource, the unique ID property would need to be set. Since i don't want the ID to be editable, it would need a prop get and no set but i would still need the ability to set it initially. Then i'd simply pass the whole note to be deleted/updated to the DAL which would read the ID and know exactly which record needed deleting/updating. EDIT : Ok, i don't need ID to be visible at all, just internal to the object but it still needs to be set somehow.
Mathew
Mathew, do not confuse yourself that the list object only holds say one column. Remember the list can hold any object, its simply a pointer to an object. So if notes had thirty properties inside of the Notes class, you could still add the one note object inside of the list and then pass it to your DAL which can then say Object.Property. For instance, Mathew m = new Mathew(); List<Mathew> l = new List<Mathew>; l.Add(m);. Then when you want to remove say a list item Mathew m = l.Find(m => m.ID = IDtoFind);Do not be too concerned about the ID value, just make its set private and get public
JonH
Sorry I had to create another comment (went over the limit) so once you have the object in this case: *Mathew m = l.Find(m => m.ID = IDtoFind);* then you can say l.Remove(m); that is remove the object m from the list of mathew objects. Your ID property can have a public get and a private set, you set the ID initially in the constructor of the notes object. Does that help ?
JonH
JonH, i maybe confusing the issue here. The ID part comes from the underlying database.The ID column is a unique id for that note in the notes table. If in my data access layer i wanted to remove a specific note, i would need to know the ID of it. So i assume that if my DAL was going to retrieve a list of notes for a person with SELECT * FROM Notes WHERE PersonID = 43, That might retrieve note record 4,5 and 8. This would give 3 Note objects returned as a list of notes. When i delete note 3 (ID 8), the DAL would execute DELETE FROM Notes WHERE NoteID = 8. Its the setting and protection of ID
Mathew
You set the NoteID in the constructor of the note, the set property is private (read only).
JonH
Thanks JonH. I think i was still thinking along the lines of Note having the ability to store itself in the database. I was concerned that by having ID in the constructor, it would be possible to create a new instance of note with any old ID number and then being able to save it but since control of adding or removing a note would lie within the Personnel Notes (if i understand correctly) and ultimately It's DAL, there shouldn't be the possibility of a stray Note being added without a parent. Is this correct?
Mathew
When you instantiate a note inside of the constructor you set its ID (maybe its a return value from your DAL). So you set it there. Your Note object can still exist, once you've created the note object you associate it to a person (to do this you simply add the note object to the notes list inside of the personnotes class). Remember you do not have to copy this implementation, I am just giving you ideas.Dont be too concerned about setting IDs, you are not open sourcing this to the stackoverflow community, I assume this is an internal project.If it is you don't have to design it in strictly.
JonH