Where do you think I should do this?

Posts   
 
    
MarcoP avatar
MarcoP
User
Posts: 270
Joined: 29-Sep-2004
# Posted on: 21-Feb-2005 13:03:29   

We have a business requirement where we need to replace some certain characters in a field whenever a user saves a customer. This list of replaceable values is stored in the database. Where do you guys think I should do this? I could either do it in the controller class when a save takes place or do it in the validator class. So, when the validator's constructor fires, I could go fetch the list and cache it in a private variable for the life of the validator and then do the replace's when the properties are set.

Any suggestions?

Thanks!

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 21-Feb-2005 13:27:38   

In an IEntityValidator implementation? That's the first call in the save routine, so nothing has been checked then, so you can alter the entity's fields if you want to, in there.

Frans Bouma | Lead developer LLBLGen Pro
MarcoP avatar
MarcoP
User
Posts: 270
Joined: 29-Sep-2004
# Posted on: 21-Feb-2005 13:59:04   

Otis wrote:

In an IEntityValidator implementation? That's the first call in the save routine, so nothing has been checked then, so you can alter the entity's fields if you want to, in there.

That sounds good. When the validation is called within the IEntityValidator, is there a way to get a reference to the current data access adapter so I can make a fetch?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 21-Feb-2005 15:19:05   

MarcoP wrote:

Otis wrote:

In an IEntityValidator implementation? That's the first call in the save routine, so nothing has been checked then, so you can alter the entity's fields if you want to, in there.

That sounds good. When the validation is called within the IEntityValidator, is there a way to get a reference to the current data access adapter so I can make a fetch?

No you won't get a reference to an adapter. However it seems to me this list is a one-time-changed list, so you can easily cache it somewhere statically and refer to it (as you only read it) in your entityvalidator class.

Frans Bouma | Lead developer LLBLGen Pro
jeffreygg
User
Posts: 805
Joined: 26-Oct-2003
# Posted on: 21-Feb-2005 19:04:17   

MarcoP wrote:

We have a business requirement where we need to replace some certain characters in a field whenever a user saves a customer. This list of replaceable values is stored in the database. Where do you guys think I should do this? I could either do it in the controller class when a save takes place or do it in the validator class. So, when the validator's constructor fires, I could go fetch the list and cache it in a private variable for the life of the validator and then do the replace's when the properties are set.

Any suggestions?

Thanks!

If I may say so, I think placing this kind of logic in the validators would violate the single-purpose-ness of the validator classes. When the validator runs either when instantiated or when validating the fields, you won't be able to depend on them just doing validation. There may be a time when you want to validate the entity or fields without performing the substitution; then what do you do?

If you're using controller classes, a possible solution might be that you trap the entity's <PropertyName>Changed Event for the property you wish to handle and perform your substitutions there. As this is a business requirement and you're using controller classes this is where I recommend you place it. This also solves your "how do I get access to the Adapter" issue, as your controller classes already have direct access to it.

My two cents.

Jeff...

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 21-Feb-2005 20:40:32   

Jeff, good points!

What of course could be done is that if you want different validators, create different IEntityValidator implementations, but that's perhaps a bit odd.

I think another way to do this (and which should have occured to me earlier) is in a derived class of teh DataAccessAdapter and in a derived method of SaveEntity(). In there, you first convert the fields, then call base.SaveEntity(..).

Frans Bouma | Lead developer LLBLGen Pro
jeffreygg
User
Posts: 805
Joined: 26-Oct-2003
# Posted on: 21-Feb-2005 21:30:03   

Otis wrote:

I think another way to do this (and which should have occured to me earlier) is in a derived class of teh DataAccessAdapter and in a derived method of SaveEntity(). In there, you first convert the fields, then call base.SaveEntity(..).

Yea, I had actually written that in my previous post, but realized it was redundant if he was using controller classes, and so removed it. As long as he can control access to the data access tier properly, all work should be funnelled through those controller classes.

The other problem with using the Adapter itself to do this is when saving the entity as part of a UnitOfWork, or as part of a collection. Maybe you can correct me, Frans, but I don't think you get to intercept the persistence logic of an entity if it's done outside the context of a single entity save. I've considered doing some similar things myself in terms of interception, but wasn't confident that I could inject my logic every time the entity was saved, no matter the context.

Jeff...

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 21-Feb-2005 21:50:31   

jeffreygg wrote:

Otis wrote:

I think another way to do this (and which should have occured to me earlier) is in a derived class of teh DataAccessAdapter and in a derived method of SaveEntity(). In there, you first convert the fields, then call base.SaveEntity(..).

The other problem with using the Adapter itself to do this is when saving the entity as part of a UnitOfWork, or as part of a collection. Maybe you can correct me, Frans, but I don't think you get to intercept the persistence logic of an entity if it's done outside the context of a single entity save. I've considered doing some similar things myself in terms of interception, but wasn't confident that I could inject my logic every time the entity was saved, no matter the context.

All saves of an entity call SaveEntity(), if you call SaveEntityCollection() or commit a UoW, it doesn't matter.

Also, if you pass in a derived class of the DataAccessAdapter to the commit method, your derived SaveEntity() method will be called whenever a save is done, as you override a virtual method simple_smile That's polymorphism simple_smile . To break a recursive save in the SaveEntity() for example, override it, do tests and if tests fail throw an exception for example simple_smile

Frans Bouma | Lead developer LLBLGen Pro
MarcoP avatar
MarcoP
User
Posts: 270
Joined: 29-Sep-2004
# Posted on: 21-Feb-2005 23:00:34   

[quotenick="jeffreygg"]

Otis wrote:

The other problem with using the Adapter itself to do this is when saving the entity as part of a UnitOfWork, or as part of a collection. Maybe you can correct me, Frans, but I don't think you get to intercept the persistence logic of an entity if it's done outside the context of a single entity save. I've considered doing some similar things myself in terms of interception, but wasn't confident that I could inject my logic every time the entity was saved, no matter the context.

I would love to have all the business validation flow through the controller classes, but say for example, the user creates a Customer Object, adds 3 addresses to the customer and saves the customer object recursive. How do I guard against any business rules, such as what is required or not? Should I set the EntityValidatorToUse property in the constructor of the sub-classes?

The biggest problem i see if with the users creating objects and saving them recursively.

What are your thoughts on having a BusinessEntityFactory() class that is responsible for creating the appropriate derived entities and setting their individual EntityValidator objects? This way, when the objects make it down to the BL, the validators are already present.

Any help?

jeffreygg
User
Posts: 805
Joined: 26-Oct-2003
# Posted on: 22-Feb-2005 02:06:22   

Otis wrote:

All saves of an entity call SaveEntity(), if you call SaveEntityCollection() or commit a UoW, it doesn't matter.

Ooooo. Very nice. I didn't think it did that. Of course, I didn't take the time to figure out it did that, but you know what they say about programmers. simple_smile

Also, if you pass in a derived class of the DataAccessAdapter to the commit method, your derived SaveEntity() method will be called whenever a save is done, as you override a virtual method simple_smile That's polymorphism simple_smile . To break a recursive save in the SaveEntity() for example, override it, do tests and if tests fail throw an exception for example simple_smile

Yes, OO is a wonderful thing. simple_smile

MarcoP wrote:

I would love to have all the business validation flow through the controller classes, but say for example, the user creates a Customer Object, adds 3 addresses to the customer and saves the customer object recursive. How do I guard against any business rules, such as what is required or not? Should I set the EntityValidatorToUse property in the constructor of the sub-classes?

Aye, that's a problem. simple_smile I still don't think you should put the logic in the Entity Validators, but that may just be a purist/nitpicky thing. I recognize that the mechanism they provide is perfect for what you need, but they're for validating, not changing data.

Another idea would be, as IDataAccessAdapter raises an event on Entity Save, perhaps you could hook that event, test for entity type, and inject your logic there. Not very nice, really, but workable.

Another possibility would be to use the Derived Adapter Entity Templates available in the third party section and modify them to include your logic in the .Save() method call.

Taking it a bit further, if you wanted to be consistent with the architecture of LLBLGen, and if you wanted to make that kind of functionality reusable across entities you could use a little Strategy action, and:

  1. Create an interface called ISaveLogic
  2. Create classes that contain the logic you want performed when your entities are saved, that implement ISaveLogic.
  3. Create a property on the derived templates called SaveLogic
  4. Delegate the logic you want performed on Save to that object
  5. Bingo, you've got a nice reusable interface to program against when you want things done on save.

Of course, this is exactly what IEntityValidator does, but now your logic can be implemented apart from validation. simple_smile

What are your thoughts on having a BusinessEntityFactory() class that is responsible for creating the appropriate derived entities and setting their individual EntityValidator objects? This way, when the objects make it down to the BL, the validators are already present.

I use an Entity Factory myself and it works very well. In fact all of my core object instantiation happens through it. Anyway, I think you should do what you're saying, but implement it as I described above, with your own ISaveLogic interface. simple_smile

Jeff...

MarcoP avatar
MarcoP
User
Posts: 270
Joined: 29-Sep-2004
# Posted on: 22-Feb-2005 02:36:21   

jeffreygg wrote:

I use an Entity Factory myself and it works very well. In fact all of my core object instantiation happens through it.

Could you give me a code snippet of what your factory looks like? I am thinking of using the simple factory pattern.

Also, are requirements changed, so the initial replacement of valus are not longer needed. simple_smile

However, I am still trying to figure out the most effecient way to ensure the EntityValidator gets inserted properly. I kind of like the idea of hooking the event that happens before the entity save. Is this bad practice?

Also, I have three scenarios I can think of to help solve this:

  1. Create an entity factory class that is responsible for creating the derived classes and setting the entity validators. all clients will be required to go through the factory when needing concrete objects.

  2. In the SaveEntity() method in the controllers, check if the save is recursive. if so, we are assuming which entities up front need to be saved recursively, so we will set the entity validators on the objects determined ahead of time. otherwise, we will have to figure out the entire object graph and set all the objects (yikes!)

  3. Override OnSaveEntity() in the DataAccessAdapter and raise a custom event, which the controller can listen for. When the controller receives this, it can figure out which entity validator it needs to set and call the Validate() method.