Enhancement to code generated files

12 posts, 0 answers
  1. Alfred Ortega
    Alfred Ortega avatar
    193 posts
    Member since:
    May 2005

    Posted 08 Nov 2008 Link to this post

    This is a feature request to add a new file (for each class) generated for business rules that will not get over written.  Basically just an empty partial class.  When the generator runs, if the file exists - leave it alone! :-) This allows for the code needed by the generator to be over written as needed but leaves the business logic we write safely alone.  Two products I have used in the past did this and I've found it to be invaluable.

    Keep up the great work,
    Al

  2. damoncarr
    damoncarr avatar
    4 posts
    Member since:
    May 2007

    Posted 08 Nov 2008 Link to this post

    Alfred,

    You should consider not doing that. You would be throwing out OO design.

    Instead treat those classes as 'providing you a service' like a third part component and no source code is available.

    I know the partial class aproach and others and it breaks design. 'Single Responsibility' as well as 'Service Layer' concepts are destroyed, and these are perhaps the two most important aspects of software in OO frameworks like .NET.

    If coding for the long term (where you must maintain this code over time), Domain Classes should never have an additional persistence responsibility, even for the smallest projects.

    If you would like elaboration I could offer a lot more but not sure your interested....

    Kind Regards,
    Damon
  3. DevCraft banner
  4. Alfred Ortega
    Alfred Ortega avatar
    193 posts
    Member since:
    May 2005

    Posted 08 Nov 2008 Link to this post

    Damon,
    Thanks for the response and if you'd like to elaborate then feel free.  I think the functionality doesn't really change the responsibility of the persistent.  For example Oracle has no boolean data type so I (and others) create a char(1) with an constraint of 0,1 as a pseudo boolean.  Then in the orm I mark the string property as private and create a boolean that is dispayable so the developer using my library deals with the boolean as they would expect. To me this makes sense and is primarily why I want it. 

    Another example is changing certain values like a status.  I don't neccessarily want the developers setting values like Widget.Status = StatusType.Final but rather calling methods like Widget.SetStatus(StatusType statusType) or Widget.FinalizeStatus() where logic can be written in.  

    In both of these examples it is simply providing a better way to change values of the object properties.  

    Al
  5. damoncarr
    damoncarr avatar
    4 posts
    Member since:
    May 2007

    Posted 08 Nov 2008 Link to this post

    OK you asked for it (grin).. No I do mean this post in the utmost respec

    This is perhaps the largest issue I encounter as a lead on teams for the last decade.

    This topic defines the difference between a super talented developer like you and an architeect (but I code just as much as any developer) who is expected to make software investments provide a return.
     
    Frankly the developers are just as valuable and have other areas to worry about that people like me can be taught a thing about.

    WITH THAT SAID:

    What I meant can be illustrated by this simple example (this is not about any technology implementation, but domain driven requirements):

    You have a use case to register a person for a conference. You have individual and corporate flows which are slightly different.

    1. In terms of ‘persistence’ as one of many examples, the goal is to elmininate/minimize its influence on the code you write (i.e. why we have ORM in the first place).
    2. From above, your ‘Registrant’ class is for your system what a String is to .NET :: A native type of the platform. Only your app is the platform. It's easier to think of it as nothing more complex then that.
      1. Mandates: A class does one and only one thing
      2. If a registrant is being a registrant, it shouldn’t ALSO be responsible for other stuff like persistence
        1. The reasons everyone knows I must assume. This is the most introductory idea of software design in OO
      3. Example: Should a .NET String know how to save itself in Oracle?! Your example is the same for reasons I will describe below.

    Here is this said another way:

    1. Each .NET class has a ‘service performed’ (note: one and only one per class)
    2. We can think of two broad categories of classes
      1. Non-Technology/Domain Services like a 'Customer' (akin to again a simple intrinsic native type) or more complex such as say 'RegistrationCoordinator' where it is passing work to others
      2. Technology Service: A class that must care/know/perform to some service that adds no business value other then implementing the need to do things like 'store data' or 'view a video on a screen'

     

    1. Our explicit goal is always that a service provided by a class from category 1 not be influenced by any aspect of  the service provided by any  class in category 2
      1. Of course you never achieve zero, but ORM is amazingly good because it shifted our ability to do this better
      2. In spite of that most ignore this and mix the layers by not differentiating
      3. It would not be an exageration or even my idea to say this is probably the largest problem in software as it elminates or makes very very hard possibilities for reuse, extensibility, etc. You write legacy code from day one instead.

    You said in the first post:


    This is a feature request to add a new file (for each class) generated for business rules that will not get over written.

    A business rule is a category 1 item.

    The generated class is by definition and creation a category 2 item.


    Make sense?

    Thanks,
    Damon



  6. Alfred Ortega
    Alfred Ortega avatar
    193 posts
    Member since:
    May 2005

    Posted 08 Nov 2008 Link to this post

    Damon,
    Thanks for the response and I love learning and coding so the worst that will happen is I learn something...  I keep an open mind (or at least try!).   I do see many examples of what you are talking about and against and I think I agree for the most part that that is a bad coding practice.  That's why I never use MyGeneration templates because they do that all over the place.  I think we are in agreement no object should have ever have "Save" or "Load" type methods.  That is the job the DAL not the object itself. 

    Using your analogy if a registrant is a class with a status - I don't like that being set via the property.  I prefer to write an enum (I can live with the ORM link in some cases) and a method (or methods) to set the status.  This does a number of things but the main thing is that ensures developers using my library are less likely to screw up and just set the status.  They have to call the appropriate method. This has nothing to do with persistence and is in keeping with the functionality of the object and it's single responsibility.  This is also the recommended way of dealing with statuses from Code Complete - even if the status is a boolean.  That might be a little much for some situations but the general rule I think is really good. 

    The second example I gave is like it - the developers don't know (or shouldn't care if they do) that the backend db is Oracle (MySql, XML or a webservice) they know that some property is a boolean.  That's all they know and all they should know.  How the persistence is handled is irrelevant to them.  They use and boolean, internally (black-box) the boolean is converted to a string and life is good. 

    So with all persistence hidden away (courtesy of the DAL via the ORM) the developers deal only with the business objects... Isn't that what DDD is designed to get us to do?

    Al
  7. damoncarr
    damoncarr avatar
    4 posts
    Member since:
    May 2007

    Posted 08 Nov 2008 Link to this post

    Al,

    Cool. However I'm not sure you fully got just how strong my statements were as your examples are 2% out of 100% of the concepts.

    1) Are you assuming that the 'status' field is NOT on a domain class but a DAL class? In other words it is already a class 100% dedicated to say CRUD behaviors?

    If so I misunderstood you because you asked Telerik for Business Logic in there but perhaps you misspoke. If you just wanted a change to some binary property on a DAL class then no problems with anything..

    For others benefit here is what I would of said if indeed you were not saying that.

    ---------------------------

    If the domain class 'template' or code in any way comes from tables then your dead. It's even worse then that...

    Assume the 'status property'. It's perfect. That should almost certainly be a class reference and not as you describe to use this example. Why?

    The colmn in some table is driving a decision to create a property.

    It is far more likely there would be a reference to a 'RegistrationContext' instance that has 'status' and much more, including more behaviors (unregister, etc.). Properties in .NET in how people use them are so so so broken.

    Get;Set; is not object oriented it is data driven. They are absolutely required but not even most of the time if your REALLY doing object oriented work. OO is about exposing operations that may change an objects internal state and almost never directly exposing a value liek people do 99% of the time (and nobody says anything... It's so weird to me as this is so basic but I know why.. Data Driven is how most systems are built).

    In the end, perhaps there WILL be a simple status property ot indeed an enumeration, whatever.

    The point is it should not be a simple thing but a rich thing more then not.

    You said:

    This does a number of things but the main thing is that ensures developers using my library are less likely to screw up and just set the status.  They have to call the appropriate method. This has nothing to do with persistence
     
    Like I say above, you see where your making decisions from the column in a table, not the domain.

    --------

    Anyway, hope others find this informative and again this has nothing to do with me. I was taught and embody this by people far smarter then I could ever be..


    Damon
  8. Alfred Ortega
    Alfred Ortega avatar
    193 posts
    Member since:
    May 2005

    Posted 08 Nov 2008 Link to this post

    Damon,
    I think you are reading to much into my request for the empty partial class file.  Although I'm glad because if you misunderstood perhaps I didn't make what I was looking for clear.  The code generated already produces a partial class so it is already doable.

    Maybe status types weren't the best example as I generally do them from enum's but some properties could from a reference table or something - they are called "relational" databases for a reason after all! :-)  In this case the Property should not be a string but a Department for an Employee.Department property.  I'm with you on that.

    The file I'm asking for is to put logic in place that to makes sense as part of the class.  Employees must be 18 to be hired.  That's has nothing to do with the database and IMHO the Employee object should check it's own DateOfBIrth value to ensure this.   It could be validating mutliple fields such as when the HireDate needs to be after the DateOfBirth is again not a database thing - it's a responsibility of the employee class itself.   I don't want it checking to see if it's ID is unique. That's not its job - that is the job of the DAL.  I'm asking that a file (user option on it's generation) be created and that this file - if it exists and the code generation tool is run again - leave my file alone.  Right now to regenerate the code, and I'm unfamiliar with tool so I could be wrong, the only way to do this is for it to overwrite all the classes it creates.  The code generated is already creating partial classes - so I can do this myself and just put my files in another folder in my assembly project.  I think it'd be nice if the ORM helped me manage this - that's it.

    Concerning your comment on gets/sets , no one has to use properties - you can do getters and setters just like in C++ or Java if you want to and .Net allows this without a complaint.  Of course anyone who does this will lose productivity time and many if not most databinding features but it can be done.  However I know of no one who even considers this as a real option because the trade off is not worth it.  Just like I know the using server side controls produces more HTML than is needed by the browser but I set EnableViewState = false and use the controls anyway because they are productive - that and I really suck at UI's and color schemes!! :-) so it's a trade off that I find acceptable.

    Al

  9. Jan Blessenohl
    Admin
    Jan Blessenohl avatar
    707 posts

    Posted 10 Nov 2008 Link to this post

    Hi Alfred Ortega,

    It is hard for me to follow your discussion ;) My feeling is that you do not talk about the same things.

    The OpenAccess Reverse Engineering wizard generates 2 files one Person.cs and on Person.OpenAccess.cs file. We will not touch the Person.cs file if it is already there, this is for you code. The Person.OpenAccess.cs file will be regenerated whenever you regenerate the source form the wizard.

    Regards,
    Jan Blessenohl
    the Telerik team

    Check out Telerik Trainer, the state of the art learning tool for Telerik products.
  10. Alfred Ortega
    Alfred Ortega avatar
    193 posts
    Member since:
    May 2005

    Posted 10 Nov 2008 Link to this post

    Jan,
    Thank you for the information and that is great news.  I saw that two files were being generated but being unfamiliar with the generator I wasn't sure if I should put anything in there or not.  I will continue to experiment with it.

    P.S.
    On the auto forward the link in the email notificition it was to admin.telerik.com vice www.telerik.com.

    Keep up the great work,
    Al
  11. Jan Blessenohl
    Admin
    Jan Blessenohl avatar
    707 posts

    Posted 12 Nov 2008 Link to this post

    Hi Alfred Ortega,

    Thanks for your hint, we will work on that.

    Sincerely yours,
    Jan Blessenohl
    the Telerik team

    Check out Telerik Trainer, the state of the art learning tool for Telerik products.
  12. Tad Carter
    Tad Carter avatar
    9 posts
    Member since:
    Feb 2008

    Posted 16 Feb 2009 Link to this post

    HI All!

    I am looking for a little guidance on how you recommend creating additional constructors, properties, public static functions for my Business Objects... I typically like to create
    1) a constructor that will initialize my business object class using its unique ID,
    2) create additional private variables with associated public properites, and
    3) also would like to create some reusable business logic common functions for each business object.  

    For example, let's say I have a data table called DATPage and use OA Reverse Mapping to generate the DATPage.cs file.  I have included code sample below (stripped down version) to use as a reference. 

    DATPage.csOpen Access Reverse Mapping (automatic generated file)

    1 namespace DataMine.DataAccess   
    2 {  
    3     public partial class DATPage   
    4     {  
    5  
    6     // Private local variables.  
    7     private _IsTopPage bool;  
    8  
    9     public DATPage() { } 
    10
    11 #region "Additional Constructors"  
    12     public DATPage(int page_id)    
    13     {   
    14         this.LoadByPrimaryKey(page_id);  
    15         SetDefaults(page_id);  
    16     }  
    17  
    18     private void SetDefaults(int page_id)  
    19     {  
    20         this.NavigateUrl= this.NavigateUrl + "?pg=" + page_id;  
    21         _IsTopPage = IsTopPage();  
    22     }
    23
    24 #endregion  
    25  
    26 #region "Additional Public Properties" 
    27     public int IsTopPage     
    28     {  
    29     get { return _IsTopPage; }   
    30     set { _IsTopPage= value; }   
    31     }
    32 #endregion  
    33  
    34 #region "Public Static Functions" 
    35  
    36     public static DATPage GetPageByID(int page_id)   
    37     {  
    38         string query = "SELECT * FROM DATPageExtent as c WHERE page_id=" + page_id;   
    39  
    40         IQueryResult result = ObjectScopeProvider1.GetNewObjectScope().GetOqlQuery(query).Execute();   
    41         return (DATPage)result;   
    42     }  
    43  
    44     public static List<DATPage> GetChildPageObjects(int parent_id)   
    45     {   
    46         string query = "SELECT * FROM DATPageExtent as c WHERE parent_id=" + parent_id;  
    47         IQueryResult result = ObjectScopeProvider1.GetNewObjectScope().GetOqlQuery(query).Execute();   
    48         return (List<DATPage>)result;     
    49     }  
    50 }
    51
    52 #endregion  
    53  

    Questions (best practice):

    1. Is it recommended that I put the additional constructors in the DATPage.cs file that was generated by OA Reverse Engineering (as I have done here in my sample)?  
    2. Can you provide sample code that would demonstrate how I might implement the "LoadByPrimaryKey()" method (line 14).  I am looking for a method that would populate my business object, (like a Fill() method on a datatable adapter).   Does a method exist in OpenAccess for this or is there alternate method you would recommend?
    3. I would like to store reusable "public static functions" that will return business objects, Generic Lists, DataTables etc... to keep my code clean (line 36, 44).   Would you recommend that I store these functions in the generated DATPage.cs file as well or create a separate class for these, DATPageCollection.cs?

    Any help and advice would be greatly appreciated.  

    Thanks,
    Tad

  13. Robert
    Robert avatar
    40 posts
    Member since:
    Jul 2008

    Posted 17 Feb 2009 Link to this post

    Hi Tad,

    Have a look at the following thread:

    In there I show how I've created a bunch of generic functions such that I'm passing the generated entities into the persistence manager to do some work.  It' still rough but gives you and idea of what can be done in terms of helper methods.  In your case, you may want to have the methods as part of a base class that is inherited by all of the generated entities.

    Hope this helps,
    Robert
Back to Top
DevCraft banner