views:

554

answers:

2

I'm part of a team tasked to revamping our old VB6 UI/COBOL database application to modern times. Before I was hired, the decision was made (largely on sales, I'm sure) to redo the UI before the database. So, now we're using WPF and MVVM to great effect, it's been amazing so far, especially using CSLA as our Model layer.

However, because our development is side-by-side with the next version of the old product, we're constrained a bit. We can't make any changes (or minimal changes) to the calls made to the COBOL database. This has been fine so far, albeit pining back to the glory days of SQL Server if you can believe it.

Where I've hit a particularly nasty roadblock regarding our BO design is in dealing with "light" business objects returned in lists and their "full" counterparts. Let me try and construct an example:

Let's say we have a person object in the DB with a bunch of fields. When we do a search on that table, we don't return all the fields, so we populate our lite object with these. These fields may or may not be a subset of the full person. We may have done a join or two to retrieve some other information specific to the search. But, if we want to edit our person object, we have to make another call to get the full version to populate the UI. This leaves us with two objects and attempting to juggle their state in 1 VM, all the while trying to keep the person list in sync on whatever parent object it sits after delete, edit, and add. Originally, I made our lite person object derive from ReadOnlyBase<>. But now that I'm dealing with the same list behavior you'd have with a list of full BOs except with half full, half lite, I'm thinking I should've just made both the lite and full versions derive from BusinessBase<> and simply made the lite version setter properties private.

Has anyone else out there come across and found a solution for this? After sleeping on it, I've come up with this potential solution. What if we wrap the full and lite version of our BO in another BO, like this:

public class PersonFull : BusinessBase<PersonFull>
{
  ...
}
public class PersonLite : BusinessBase<PersonLite>
{
  ...
}

public class Person : BusinessBase<Person>
{
  public PersonFull PersonFull;
  public PersonLite PersonLite;
}
public class PersonList : BusinessListBase<PersonList, Person>
{
}

Obviously everything would be CSLA registered properties and such, but for the sake of brevity they're fields there. In this case Person and PersonList would hold all the factory methods. After a search operation PersonList would be populated by Person objects whose PersonLite members were all populated and the PersonFull objects were all null. If we needed to get the full version, we simply tell the Person object to do so, and now we have our PersonFull object so we can populate the edit UI. If the Person object is to be deleted, we can easily do this with the CSLA delete procedures in place, while still maintaining the integrity of our lists across all the VMs that are listening to it.

So, I hope this made sense to everyone, and if anyone has a different solution they've successfully employed or criticism of this one, by all means!

Thanks!

(Reposted from: http://forums.lhotka.net/forums/thread/35576.aspx)

A: 

If you look over Rocky's examples that come with the CSLA framework, you'll notice that he always separates the read only objects from the read/write objects. I think this is done for good reason, because the behaviors are going to be drastically different. Read only objects will be more performance based, their validation will be very different, and usually have less information altogether. The read/write objects will not be as perfomance based and rely heavily on validation, authorization, etc.

However, that leaves you with the dilemma you currently find yourself in. What I would do is overload the constructor of each class so you can pass them between each other and "copy" what you need out of each other.

Something like this:

public class PersonLite : BusinessBase<PersonLite>
{
    public PersonLite(PersonFull fullPerson)
    {
        //copy from fullPerson's properties or whatever
    }
}

public class PersonFull : BusinessBase<PersonFull>
{
    public PersonFull(PersonLite litePerson)
    {
        //copy from litePerson's properties or whatever
    }
}

You could do this with a factory pattern as well, which is Rocky's preference I believe.

Joseph
I'm not entirely sure what you're proposing is supposed to solve. A lite BO would never populate a full BO, since we can just go to the DB to hydrate a full BO with the unique data from the lite version. I guess I could see a full version updating the lite version after an edit, but this isn't really the way to go about it.
opedog
The lite version wouldn't be able to fully populate the full version, but it might be enough so that it could be displayed to the user, and the remaining data can be lazy loaded. I've done that in the past with some success. If you're trying to modify in memory objects then your approach to this is going to be entirely different.
Joseph
If the intention is to rebuild the backend BO's now that the front-end is done, why not use this as a test case to start with new CSLA objects that contain all the properties?
Mark Redman
Good question. Currently our BOs are mapped directly to the file record layouts in the COBOL and I place attributes on the properties so a helper class knows how to parse the input from COBOL and populate the properties. This is why we have 2 different objects, because we have 2 different record layouts that come from the DB. And this development is happening concurrent with the front-end development.
opedog
A: 
public class PersonLite : ReadOnlyBase<PersonLite>
{
    public void Update(PersonFull person) { }
}

public class PersonFull : BusinessBase<PersonFull>
{
    // blah blah
}

I would update the "lite" object with the changes made to the "full" object, and leave it as ReadOnlyBase. It's important to remember that the "ReadOnly" in ReadOnlyBase means an object that is only read from the database, and never saved to it. A less elegant, but more accurate name would be NotSavableBase, because such objects lack the DataPortal_XYZ machinery for anything but fetches. For obvious reasons, such objects usually have immutable properties, but they don't have to. ReadOnlyBase derives from Core.BindableBase and implements INotifyPropertyChanged, so changing the values of its properties will work just fine with binding.

When you save your "full" object, you pass the newly saved instance to the Update(PersonFull) method of the instance that sits in your list, and update the properties of the "lite" object from the "full" object.

I've used this technique many times and it works just fine.

Gregory Higley